diff --git a/.gitignore b/.gitignore
index 2582cc534fa5fb6be2667c879fd6a0342168432d..2b1de550ab5145e593767ab3d571585c14690f83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,3 +91,4 @@ data/
 po/*.po
 docs/swagger
 _build
+front/src/translations.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3165213e2701f3796ad0299be49e8c5e801a7818..fa41fbd25f321a989710c2a3d6be97bd5d2f7d3a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,9 +19,12 @@ review_front:
   when: manual
   allow_failure: true
   before_script:
+    - apt-get update
+    - apt-get install jq -y
     - cd front
   script:
     - yarn install
+    - yarn run i18n-compile
     # this is to ensure we don't have any errors in the output,
     # cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
     - INSTANCE_URL=$REVIEW_INSTANCE_URL yarn run build | tee /dev/stderr | (! grep -i 'ERROR in')
@@ -175,11 +178,11 @@ build_front:
   stage: build
   image: node:9
   before_script:
+    - apt-get update
+    - apt-get install jq -y
     - cd front
-
   script:
     - yarn install
-    - yarn run i18n-extract
     - yarn run i18n-compile
     # this is to ensure we don't have any errors in the output,
     # cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
diff --git a/CONTRIBUTING b/CONTRIBUTING
index 24a3a78ff7358050858e9ccdda0066b7a7b1c9a4..dffe99d7189ea8bbfd020ca7b5ef9eaff50f4f2b 100644
--- a/CONTRIBUTING
+++ b/CONTRIBUTING
@@ -289,8 +289,9 @@ Typical workflow for a contribution
 Internationalization
 --------------------
 
+We're using https://github.com/Polyconseil/vue-gettext to manage i18n in the project.
 When working on the front-end, any end-user string should be translated
-using either ``<i18next path="yourstring">`` or the ``$t('yourstring')``
+using either ``<translate>yourstring</translate>`` or ``$gettext('yourstring')``
 function.
 
 Extraction is done by calling ``yarn run i18n-extract``, which
diff --git a/README.rst b/README.rst
index ef3998d117949f6fb1109393a864f0c73a3ffd9c..6f9772ea65acc9f381277d1d91463a395362a538 100644
--- a/README.rst
+++ b/README.rst
@@ -26,4 +26,9 @@ Contribute
 ----------
 
 Contribution guidelines as well as development installation instructions
-are outlined in `CONTRIBUTING <CONTRIBUTING>`_
+are outlined in `CONTRIBUTING <CONTRIBUTING>`_.
+
+Translate
+^^^^^^^^^
+
+Translators willing to help can refer to `TRANSLATORS <TRANSLATORS>`_ for instructions.
diff --git a/TRANSLATORS.rst b/TRANSLATORS.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f2015099590b54f0405408972356eebc8c187d75
--- /dev/null
+++ b/TRANSLATORS.rst
@@ -0,0 +1,28 @@
+Translating Funkwhale
+=====================
+
+Thank you for reading this! If you want to help translate Funkwhale,
+you found the proper place :)
+
+Translation is done via our own Weblate instance at https://translate.funkwhale.audio/projects/funkwhale/front/.
+
+You can signup/login using your Gitlab account (from https://code.eliotberriot.com).
+
+Translation workflow
+--------------------
+
+Once you're logged-in on the Weblate instance, you can suggest translations. Your suggestions will then be reviewer
+by the project maintainer or other translators to ensure consistency.
+
+Guidelines
+----------
+
+Respecting those guidelines is mandatory if you want your translation to be included:
+
+- Use gender-neutral language and wording
+
+Requesting a new language
+-------------------------
+
+If you'd like to see a new language in Funkwhale, please open an issue here:
+https://code.eliotberriot.com/funkwhale/funkwhale/issues
diff --git a/changes/changelog.d/161.feature b/changes/changelog.d/161.feature
new file mode 100644
index 0000000000000000000000000000000000000000..4c48387cb236c9f65e91231798e939b49a9fc61f
--- /dev/null
+++ b/changes/changelog.d/161.feature
@@ -0,0 +1 @@
+New translation workflow (#161, #167)
diff --git a/docs/index.rst b/docs/index.rst
index 7d200da54643c3b04124669691f6c3e3cf30eecd..b107739aeee0e51ef2a90df4de6d1d7b92be3b23 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -23,6 +23,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in
    api
    third-party
    contributing
+   translators
    changelog
 
 Indices and tables
diff --git a/docs/translators.rst b/docs/translators.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6728e9ad449d4a7e0fc239ab02c92193173d1b9d
--- /dev/null
+++ b/docs/translators.rst
@@ -0,0 +1 @@
+.. include:: ../TRANSLATORS.rst
diff --git a/front/build/dev-server.js b/front/build/dev-server.js
index f9c389e729867b266904026454b5c676ea0df1da..634a6d41e76af052331174198af1fd5e0669fd06 100644
--- a/front/build/dev-server.js
+++ b/front/build/dev-server.js
@@ -14,8 +14,6 @@ var webpackConfig = process.env.NODE_ENV === 'testing'
   ? require('./webpack.prod.conf')
   : require('./webpack.dev.conf')
 
-require('./i18n')
-
 // default port where dev server listens for incoming traffic
 var port = process.env.PORT || config.dev.port
 var host = process.env.HOST || config.dev.host
diff --git a/front/build/i18n.js b/front/build/i18n.js
deleted file mode 100644
index ef31070c7d913acd8ebe769ac1a10a6faf558288..0000000000000000000000000000000000000000
--- a/front/build/i18n.js
+++ /dev/null
@@ -1,49 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const { gettextToI18next } = require('i18next-conv');
-
-const poDir = path.join(__dirname, '..', '..', 'po')
-const outDir = path.join(__dirname, '..', 'static', 'translations')
-if (!fs.existsSync(outDir) || !fs.statSync(outDir).isDirectory()) {
-    fs.mkdirSync(outDir)
-}
-
-// Convert .po files to i18next files
-fs.readdir(poDir, (err, files) => {
-    if (err) {
-        return console.log(err)
-    }
-
-    for (const file of files) {
-        if (file.endsWith('.po')) {
-            const lang = file.replace(/\.po$/, '')
-            const output = path.join(outDir, `${lang}.json`)
-            fs.readFile(path.join(poDir, file), (err, content) => {
-                if (err) {
-                    return console.log(err)
-                }
-
-                gettextToI18next(lang, content).then(res => {
-                    fs.writeFile(output, res, err => {
-                        if (err) {
-                            console.log(err)
-                        } else {
-                            console.log(`Wrote translation file: ${output}`)
-                            if (lang === 'en') {
-                                // for english, we need to specify that json values are equal to the keys.
-                                // otherwise we end up with empty strings on the front end for english
-                                var contents = fs.readFileSync(output)
-                                var jsonContent = JSON.parse(contents)
-                                var finalContent = {}
-                                Object.keys(jsonContent).forEach(function(key) {
-                                    finalContent[key] = key
-                                })
-                                fs.writeFile(output, JSON.stringify(finalContent))
-                            }
-                        }
-                    })
-                })
-            })
-        }
-    }
-})
diff --git a/front/locales/app.pot b/front/locales/app.pot
new file mode 100644
index 0000000000000000000000000000000000000000..8adc51986936bd7b65e0fdefa10335f0ad477dee
--- /dev/null
+++ b/front/locales/app.pot
@@ -0,0 +1,1315 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the front package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: front 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-30 16:25+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: front/src/components/playlists/PlaylistModal.vue:9
+msgid "\"%{ title }\", by %{ artist }"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:24
+msgid "(%{ index } of %{ length })"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:43
+#: front/src/components/common/ActionTable.vue:51
+msgid "%{ count } on %{ total } selected"
+msgid_plural "%{ count } on %{ total } selected"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/Sidebar.vue:115 src/views/federation/LibraryDetail.vue:87
+msgid "%{ count } track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/Artist.vue:13
+msgid "%{ count } track in %{ albumsCount } albums"
+msgid_plural "%{ count } tracks in %{ albumsCount } albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/radios/Builder.vue:66
+msgid "%{ count } track matching combined filters"
+msgid_plural "%{ count } tracks matching combined filters"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/playlists/Card.vue:19
+msgid "%{ count} track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/activity/Like.vue:7
+msgid "%{ user } favorited a track"
+msgstr ""
+
+#: front/src/components/activity/Listen.vue:7
+msgid "%{ user } listened to a track"
+msgstr ""
+
+#: front/src/components/audio/artist/Card.vue:41
+msgid "1 album"
+msgid_plural "%{ count } albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/favorites/List.vue:10
+msgid "1 favorite"
+msgid_plural "%{ count } favorites"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/album/Card.vue:54
+#: front/src/components/federation/LibraryCard.vue:25
+msgid "1 track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/App.vue:51
+msgid "About Funkwhale"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:111
+msgid "Access disabled"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:82
+msgid "Action %{ action } was launched successfully on %{ count } element"
+msgid_plural "Action %{ action } was launched successfully on %{ count } elements"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:4
+#: front/src/components/favorites/TrackFavoriteIcon.vue:21
+msgid "Add to favorites"
+msgstr ""
+
+#: front/src/components/metadata/Search.vue:129
+#: front/src/components/audio/track/Table.vue:9
+#: front/src/components/manage/library/FilesTable.vue:40
+msgid "Album"
+msgstr ""
+
+#: front/src/components/library/import/ReleaseImport.vue:3
+msgid "Album %{ title } (%{ count } track) by %{ artist }"
+msgid_plural "Album %{ title } (%{ count } tracks) by %{ artist }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/Album.vue:12
+msgid "Album containing %{ count } track, by %{ artist }"
+msgid_plural "Album containing %{ count } tracks, by %{ artist }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/metadata/Search.vue:125
+#: front/src/components/federation/LibraryTrackTable.vue:34
+#: front/src/components/manage/library/RequestsTable.vue:50
+msgid "Artist"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:13
+msgid "By %{ artist }"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:57
+msgid "By confirming, %{ username } will be denied access to your library."
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:73
+msgid "By confirming, %{ username } will be granted access to your library."
+msgstr ""
+
+#: front/src/components/manage/library/FilesTable.vue:184
+#: front/src/components/manage/library/RequestsTable.vue:190
+#: front/src/components/manage/users/InvitationsTable.vue:161
+msgid "Delete"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:42
+msgid "Do you want to clear the playlist \"%{ playlist }\"?"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:34
+msgid "Do you want to delete the playlist \"%{ playlist }\"?"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:29
+msgid "Do you want to launch %{ action } on %{ count } element?"
+msgid_plural "Do you want to launch %{ action } on %{ count } elements?"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:82
+msgid "Error reporting"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:84
+msgid "Everyone"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:80
+msgid "Everyone on this instance"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:175
+#: front/src/views/admin/Settings.vue:79 src/views/admin/users/UsersDetail.vue:156
+msgid "Federation"
+msgstr ""
+
+#: front/src/components/federation/LibraryCard.vue:44
+msgid "Follow"
+msgstr ""
+
+#: front/src/components/activity/Like.vue:12 src/components/activity/Listen.vue:12
+msgid "from %{ album } by %{ artist }"
+msgstr ""
+
+#: front/src/components/library/Track.vue:13
+msgid "From album %{ album } by %{ artist }"
+msgstr ""
+
+#: front/src/App.vue:53
+msgid "Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"
+msgstr ""
+
+#: front/src/components/Home.vue:77
+msgid ""
+"Get quality metadata about your music thanks to\n"
+"              <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"                MusicBrainz\n"
+"              </a>"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:159
+#: front/src/components/library/Library.vue:18
+msgid "Import"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:42
+msgid "Import #%{ id } launched"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:38
+msgid "Import %{ count } track"
+msgid_plural "Import %{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:77
+msgid "Imports"
+msgstr ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:3
+msgid "In favorites"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:31
+msgid "Insert from queue (%{ count } track)"
+msgid_plural "Insert from queue (%{ count } tracks)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:75
+msgid "Instance information"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:171
+#: front/src/views/admin/users/UsersDetail.vue:152 src/components/Sidebar.vue:70
+msgid "Library"
+msgstr ""
+
+#: front/src/App.vue:29
+msgid "Links"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:38
+msgid "Logged in as %{ username }"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:192
+msgid "Mark as closed"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:191
+msgid "Mark as imported"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:115
+msgid ""
+"Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the\n"
+"            <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"              MusicBrainz\n"
+"            </a>\n"
+"            project, which you can think about as the Wikipedia of music."
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:76
+msgid "Nobody except me"
+msgstr ""
+
+#: front/src/App.vue:60
+msgid "Options"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:95
+msgid "Password updated"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:47
+msgid "Play immediatly"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:11
+msgid "Playlist containing %{ count } track, by %{ username }"
+msgid_plural "Playlist containing %{ count } tracks, by %{ username }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:78 src/components/library/Library.vue:14
+msgid "Playlists"
+msgstr ""
+
+#: front/src/components/audio/Player.vue:183
+msgid "Queue shuffled!"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:11
+msgid "Registered since %{ date }"
+msgstr ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:19
+msgid "Remove from favorites"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:31
+msgid "Result %{ current }/%{ total }"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:61
+msgid "Select all %{ total } elements"
+msgid_plural "Select all %{ total } elements"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/federation/LibraryCard.vue:43
+msgid "Send a follow request"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:179
+#: front/src/views/admin/users/UsersDetail.vue:160
+msgid "Settings"
+msgstr ""
+
+#: front/src/components/audio/artist/Card.vue:30
+msgid "Show 1 more album"
+msgid_plural "Show %{ count } more albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/album/Card.vue:40
+msgid "Show 1 more track"
+msgid_plural "Show %{ count } more tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:97
+#: front/src/components/federation/LibraryTrackTable.vue:83
+#: front/src/components/library/import/BatchDetail.vue:128
+#: front/src/components/library/import/BatchList.vue:73
+#: front/src/components/manage/library/FilesTable.vue:97
+#: front/src/components/manage/library/RequestsTable.vue:104
+#: front/src/components/manage/users/InvitationsTable.vue:76
+#: front/src/components/manage/users/UsersTable.vue:87
+msgid "Showing results %{ start }-%{ end } on %{ total }"
+msgstr ""
+
+#: front/src/App.vue:38
+msgid "Source code"
+msgstr ""
+
+#: front/src/App.vue:37
+msgid "Source code (%{version})"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:81
+msgid "Statistics"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:80
+msgid "Subsonic"
+msgstr ""
+
+#: front/src/App.vue:56
+msgid "The funkwhale logo was kindly designed and provided by Francis Gading."
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:50
+msgid "This track is not imported and cannot be played"
+msgstr ""
+
+#: front/src/App.vue:130
+msgid "This will erase your local data and disconnect you, do you want to continue?"
+msgstr ""
+
+#: front/src/components/metadata/Search.vue:133
+#: front/src/components/library/import/BatchDetail.vue:85
+msgid "Track"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:167
+#: front/src/views/admin/users/UsersDetail.vue:148
+msgid "Upload"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:76 src/views/admin/users/UsersList.vue:4
+#: front/src/views/admin/users/Base.vue:6
+msgid "Users"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:7
+msgid "You are currently logged in as %{ username }"
+msgstr ""
+
+#: front/src/App.vue:5
+msgid "Choose your instance"
+msgstr ""
+
+#: front/src/App.vue:7
+msgid "You need to select an instance in order to continue"
+msgstr ""
+
+#: front/src/App.vue:33
+msgid "About this instance"
+msgstr ""
+
+#: front/src/App.vue:36
+msgid "Documentation"
+msgstr ""
+
+#: front/src/App.vue:43
+msgid "Use another instance"
+msgstr ""
+
+#: front/src/components/radios/Button.vue:5
+msgid "Start"
+msgstr ""
+
+#: front/src/components/discussion/Comment.vue:15
+msgid "Expand"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:6
+msgid "User activity"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:43 src/components/library/Library.vue:8
+msgid "Artists"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:55
+msgid "tracks"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:22
+msgid "Changes synced with server"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:41
+msgid "Clear playlist"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:45
+msgid "This will remove all tracks from this playlist and cannot be undone."
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:4
+msgid "Manage playlists"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:27
+msgid "Available playlists"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:32
+msgid "Name"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:34
+#: front/src/components/library/Album.vue:44
+msgid "Tracks"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:64
+#: front/src/components/common/DangerousButton.vue:18
+msgid "Cancel"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:3
+msgid "Create a new playlist"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:10
+msgid "Playlist created"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:26
+msgid "Playlist visibility"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:35
+msgid "Create playlist"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:5
+msgid "Create a funkwhale account"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:14
+msgid "We cannot create your account"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:20
+#: front/src/components/manage/users/UsersTable.vue:38
+msgid "Username"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:30
+msgid "Email"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:39
+msgid "Password"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:44
+msgid "Invitation code (optional)"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:52
+msgid "Create my account"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:3
+msgid "Subsonic API password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:8
+msgid "Funkwhale is compatible with other music players that support the Subsonic API."
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:9
+msgid "You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance."
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:15
+msgid "Discover how to use Funkwhale from other apps"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:21
+msgid "Error"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:35
+#: front/src/components/auth/SubsonicTokenForm.vue:38
+msgid "Request a new password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:36
+msgid "Request a new Subsonic API password?"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:44
+msgid "Request a password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:52
+msgid "This will completely disable access to the Subsonic API using from account."
+msgstr ""
+
+#: front/src/components/auth/Login.vue:5
+msgid "Log in to your Funkwhale account"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:10
+msgid "Please double-check your username/password couple is correct"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:16
+msgid "Username or email"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:18
+msgid "Create an account"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:16
+msgid "This is you!"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:20
+#: front/src/components/manage/users/UsersTable.vue:71
+msgid "Staff member"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:6
+msgid "Are you sure you want to log out?"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:9
+msgid "Yes, log me out!"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:6
+msgid "Account settings"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:28
+msgid "Update settings"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:35
+msgid "Change my password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:44
+msgid "Cannot change your password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:51
+msgid "Old password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:63
+msgid "Change password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:64
+msgid "Change your password?"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:68
+msgid "You will be logged out from this session and have to log out with the new one"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:72
+msgid "Disable access"
+msgstr ""
+
+#: front/src/components/favorites/List.vue:6
+msgid "Loading your favorites..."
+msgstr ""
+
+#: front/src/components/favorites/List.vue:34 src/components/library/Radios.vue:29
+#: front/src/components/manage/users/UsersTable.vue:20
+#: front/src/views/federation/LibraryList.vue:29
+msgid "Ascending"
+msgstr ""
+
+#: front/src/components/favorites/List.vue:39 src/components/library/Radios.vue:37
+#: front/src/views/federation/LibraryList.vue:34
+msgid "Results per page"
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:6
+msgid "Error while saving settings"
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:12
+msgid "Settings updated successfully."
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:64
+#: front/src/components/library/radios/Builder.vue:20
+msgid "Save"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:5
+msgid "Browsing radios"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:8
+msgid "Create your own radio"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:6
+#: front/src/components/manage/library/FilesTable.vue:6
+#: front/src/components/manage/library/RequestsTable.vue:6
+#: front/src/components/manage/users/InvitationsTable.vue:6
+#: front/src/components/manage/users/UsersTable.vue:6
+#: front/src/views/federation/LibraryList.vue:15
+msgid "Search"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:6
+msgid "Builder"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:12
+msgid "Radio name"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:17
+msgid "Display publicly"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:27
+msgid "Add filters to customize your radio"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:37
+msgid "Add filter"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:47
+msgid "Filter name"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:49
+msgid "Config"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:51
+#: front/src/components/common/ActionTable.vue:9
+msgid "Actions"
+msgstr ""
+
+#: front/src/components/library/Library.vue:5
+msgid "Browse"
+msgstr ""
+
+#: front/src/components/library/Library.vue:11
+msgid "Radios"
+msgstr ""
+
+#: front/src/components/library/Library.vue:21
+msgid "Import batches"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:5
+msgid "Browsing artists"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:16 src/views/playlists/List.vue:18
+msgid "Ordering"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:24 src/views/playlists/List.vue:26
+msgid "Ordering direction"
+msgstr ""
+
+#: front/src/components/library/Artist.vue:45
+msgid "Albums by this artist"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:5
+msgid "Ensure your music files are properly tagged before uploading them."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:6
+msgid "We recommend using Picard for that purpose."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:37
+msgid "Once all your files are uploaded, simply click the following button to check the import status."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:44
+msgid "File name"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:46
+#: front/src/components/library/import/BatchDetail.vue:67
+#: front/src/components/library/import/BatchList.vue:12
+#: front/src/components/library/import/BatchList.vue:38
+#: front/src/components/federation/LibraryFollowTable.vue:24
+#: front/src/components/federation/LibraryTrackTable.vue:32
+#: front/src/components/manage/library/RequestsTable.vue:25
+#: front/src/components/manage/users/UsersTable.vue:44
+msgid "Status"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:57
+#: front/src/components/library/import/BatchDetail.vue:72
+#: front/src/components/library/import/BatchList.vue:17
+msgid "Success"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:60
+#: front/src/components/library/import/BatchDetail.vue:34
+#: front/src/components/library/import/BatchDetail.vue:70
+#: front/src/components/library/import/BatchList.vue:15
+#: front/src/components/federation/LibraryFollowTable.vue:47
+#: front/src/components/manage/library/RequestsTable.vue:28
+#: front/src/components/manage/library/RequestsTable.vue:64
+msgid "Pending"
+msgstr ""
+
+#: front/src/components/library/import/ArtistImport.vue:8
+msgid "Filter album types"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:18
+#: front/src/components/library/import/BatchDetail.vue:83
+msgid "Source"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:43
+msgid "Search query"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:45
+msgid "Imported URL"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:11
+msgid "Import batch"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:19
+#: front/src/components/library/import/BatchList.vue:36
+msgid "Launch date"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:38
+msgid "Skipped"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:42
+msgid "Errored"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:55
+msgid "Finished"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:81
+msgid "Job ID"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:7
+msgid "Import source"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:13
+msgid "Metadata"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:19
+msgid "Music"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:30
+msgid "Next step"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:66
+msgid "External source. Supported backends"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:86
+msgid "Search an entity you want to import:"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:94
+msgid "Or"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:104
+msgid "You will import:"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:150
+msgid "This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:23
+#: front/src/components/federation/LibraryTrackTable.vue:12
+msgid "Any"
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:25
+msgid "API"
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:40
+msgid "Submitted by"
+msgstr ""
+
+#: front/src/components/library/Track.vue:24
+msgid "Artist page"
+msgstr ""
+
+#: front/src/components/library/Track.vue:31
+msgid "Play"
+msgstr ""
+
+#: front/src/components/library/Track.vue:41
+msgid "Search on Wikipedia"
+msgstr ""
+
+#: front/src/components/library/Track.vue:45
+msgid "View on MusicBrainz"
+msgstr ""
+
+#: front/src/components/library/Track.vue:49 src/components/audio/track/Table.vue:24
+msgid "Download"
+msgstr ""
+
+#: front/src/components/library/Track.vue:59
+#: front/src/components/manage/library/FilesTable.vue:44
+msgid "Duration"
+msgstr ""
+
+#: front/src/components/library/Track.vue:65 src/components/library/Track.vue:76
+#: front/src/components/manage/library/FilesTable.vue:64
+#: front/src/components/manage/library/FilesTable.vue:76
+#: front/src/components/manage/library/FilesTable.vue:82
+#: front/src/components/manage/library/RequestsTable.vue:72
+#: front/src/components/manage/library/RequestsTable.vue:83
+#: front/src/components/manage/users/UsersTable.vue:62
+msgid "N/A"
+msgstr ""
+
+#: front/src/components/library/Track.vue:70
+msgid "Size"
+msgstr ""
+
+#: front/src/components/library/Track.vue:81
+msgid "Bitrate"
+msgstr ""
+
+#: front/src/components/library/Home.vue:10
+msgid "Latest artists"
+msgstr ""
+
+#: front/src/components/library/Home.vue:27
+msgid "Music requests"
+msgstr ""
+
+#: front/src/components/requests/Card.vue:26
+#: front/src/components/manage/library/RequestsTable.vue:89
+msgid "Create import"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:4
+msgid "Something's missing in the library? Let us know what you would like to listen!"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:11
+msgid "Leave this field empty if you're requesting the whole discography."
+msgstr ""
+
+#: front/src/components/requests/Form.vue:15
+#: front/src/components/manage/library/RequestsTable.vue:52
+msgid "Comment"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:22
+msgid "We've received your request, you'll get some groove soon ;)"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:27
+msgid "Pending requests"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:12
+msgid "Pending approval"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:22
+msgid "Actor"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:39
+msgid "Approved"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:43
+msgid "Refused"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:64
+msgid "Deny"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:53
+msgid "Not imported"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:36
+msgid "Published date"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:51
+msgid "In library"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:4
+msgid "Federate with a new instance"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:11
+msgid "Error while scanning library"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:32
+msgid "Launch scan"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:44
+#: front/src/components/audio/artist/Card.vue:34
+msgid "Collapse"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:51
+msgid "Play all"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:14
+msgid "Add to queue"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:15
+msgid "Play next"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:16
+msgid "Play now"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:145
+msgid "%{ count } track was added to your queue"
+msgid_plural "%{ count } tracks were added to your queue"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/track/Table.vue:7
+#: front/src/components/manage/library/FilesTable.vue:38
+msgid "Title"
+msgstr ""
+
+#: front/src/components/audio/track/Table.vue:30
+msgid "There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as cURL to easily download a list of tracks."
+msgstr ""
+
+#: front/src/components/audio/Search.vue:3
+msgid "Search for some music"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:8
+msgid "Page not found!"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:11
+msgid "We're sorry, the page you asked for does not exist:"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:15
+msgid "Go to home page"
+msgstr ""
+
+#: front/src/components/About.vue:16
+msgid "Unfortunately, owners of this instance did not yet take the time to complete this page."
+msgstr ""
+
+#: front/src/components/Sidebar.vue:21
+msgid "Queue"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:23
+msgid "(empty)"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:43
+msgid "Logout"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:44
+msgid "Login"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:64
+msgid "Administration"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:124
+msgid "Yes"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:158
+msgid "New tracks will be appended here automatically."
+msgstr ""
+
+#: front/src/components/manage/library/FilesTable.vue:42
+msgid "Type"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:30
+#: front/src/components/manage/library/RequestsTable.vue:62
+msgid "Imported"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:48
+msgid "User"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:54
+msgid "Import date"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:5
+msgid "Error while creating invitation"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:12
+msgid "Invitation code"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:27
+#: front/src/components/manage/users/InvitationsTable.vue:43
+msgid "Code"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:38
+msgid "Clear"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:20
+msgid "All"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:22
+msgid "Expired/used"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:39
+msgid "Owner"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:41
+msgid "Creation date"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:50
+msgid "Used"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:52
+msgid "Not used"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:40
+msgid "Account status"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:42
+msgid "Last activity"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:54
+msgid "Active"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:22
+msgid "Go"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:40
+msgid "Launch"
+msgstr ""
+
+#: front/src/components/Home.vue:6
+msgid "Welcome on Funkwhale"
+msgstr ""
+
+#: front/src/components/Home.vue:14
+msgid "Get me to the library"
+msgstr ""
+
+#: front/src/components/Home.vue:25
+msgid "Why funkwhale?"
+msgstr ""
+
+#: front/src/components/Home.vue:40
+msgid "Funkwhale is designed to make it easy to listen to music you like, or to discover new artists."
+msgstr ""
+
+#: front/src/components/Home.vue:45
+msgid "Click once, listen for hours using built-in radios"
+msgstr ""
+
+#: front/src/components/Home.vue:57
+msgid "Playlists? We got them"
+msgstr ""
+
+#: front/src/components/Home.vue:67
+msgid "Funkwhale takes care of handling your music"
+msgstr ""
+
+#: front/src/components/Home.vue:72
+msgid "Import music from various platforms, such as YouTube or SoundCloud"
+msgstr ""
+
+#: front/src/components/Home.vue:89
+msgid "Covers, lyrics, our goal is to have them all ;)"
+msgstr ""
+
+#: front/src/components/Home.vue:99
+msgid "Funkwhale is dead simple to use."
+msgstr ""
+
+#: front/src/components/Home.vue:104
+msgid "No add-ons, no plugins : you only need a web library"
+msgstr ""
+
+#: front/src/components/Home.vue:118
+msgid "Your music, your way"
+msgstr ""
+
+#: front/src/components/Home.vue:131
+msgid "We do not track you or bother you with ads"
+msgstr ""
+
+#: front/src/views/instance/Timeline.vue:5
+msgid "Loading timeline..."
+msgstr ""
+
+#: front/src/views/instance/Timeline.vue:8
+msgid "Recent activity on this instance"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:30
+msgid "Edit..."
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:38
+msgid "Delete playlist"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:4
+msgid "Browsing playlists"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:9
+msgid "Manage your playlists"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:29
+msgid "Descending"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:5
+msgid "Reset your password"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:15
+msgid "Account's email"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:25
+#: front/src/views/auth/PasswordResetConfirm.vue:19
+#: front/src/views/auth/EmailConfirm.vue:18
+msgid "Back to login"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:5
+msgid "Change your password"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:29
+msgid "Password updated successfully"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:32
+msgid "Proceed to login"
+msgstr ""
+
+#: front/src/views/auth/EmailConfirm.vue:5
+msgid "Confirm your email"
+msgstr ""
+
+#: front/src/views/auth/EmailConfirm.vue:25
+msgid "Your email address was confirmed, you can now use the service without limitations."
+msgstr ""
+
+#: front/src/views/admin/library/RequestsList.vue:4
+msgid "Import requests"
+msgstr ""
+
+#: front/src/views/admin/library/FilesList.vue:4
+msgid "Library files"
+msgstr ""
+
+#: front/src/views/admin/library/Base.vue:6
+msgid "Files"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:30
+msgid "Email address"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:55
+msgid "Account active"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:85
+msgid "Refresh"
+msgstr ""
+
+#: front/src/views/admin/users/InvitationsList.vue:4
+msgid "Invitations"
+msgstr ""
+
+#: front/src/views/federation/LibraryFollowersList.vue:4
+msgid "Browsing followers"
+msgstr ""
+
+#: front/src/views/federation/LibraryTrackList.vue:4
+msgid "Browsing federated tracks"
+msgstr ""
+
+#: front/src/views/federation/LibraryList.vue:4
+msgid "Browsing libraries"
+msgstr ""
+
+#: front/src/views/federation/LibraryList.vue:9
+msgid "Add a new library"
+msgstr ""
+
+#: front/src/views/federation/LibraryDetail.vue:57
+msgid "Auto importing"
+msgstr ""
+
+#: front/src/views/federation/LibraryDetail.vue:85
+msgid "Library size"
+msgstr ""
+
+#: front/src/views/federation/Base.vue:6
+msgid "Libraries"
+msgstr ""
+
+#: front/src/views/federation/Base.vue:14
+msgid "Followers"
+msgstr ""
diff --git a/front/locales/en_US/LC_MESSAGES/app.po b/front/locales/en_US/LC_MESSAGES/app.po
new file mode 100644
index 0000000000000000000000000000000000000000..b5abf8088fe65c4f17203dc634974e5508b367a7
--- /dev/null
+++ b/front/locales/en_US/LC_MESSAGES/app.po
@@ -0,0 +1,1323 @@
+# English translations for front package.
+# Copyright (C) 2018 THE front'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the front package.
+# Automatically generated, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: front 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-30 16:25+0200\n"
+"PO-Revision-Date: 2018-06-30 11:34+0200\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: en_US\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: front/src/components/playlists/PlaylistModal.vue:9
+msgid "\"%{ title }\", by %{ artist }"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:24
+msgid "(%{ index } of %{ length })"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:43
+#: front/src/components/common/ActionTable.vue:51
+msgid "%{ count } on %{ total } selected"
+msgid_plural "%{ count } on %{ total } selected"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/Sidebar.vue:115
+#: src/views/federation/LibraryDetail.vue:87
+msgid "%{ count } track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/Artist.vue:13
+msgid "%{ count } track in %{ albumsCount } albums"
+msgid_plural "%{ count } tracks in %{ albumsCount } albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/radios/Builder.vue:66
+msgid "%{ count } track matching combined filters"
+msgid_plural "%{ count } tracks matching combined filters"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/playlists/Card.vue:19
+msgid "%{ count} track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/activity/Like.vue:7
+msgid "%{ user } favorited a track"
+msgstr ""
+
+#: front/src/components/activity/Listen.vue:7
+msgid "%{ user } listened to a track"
+msgstr ""
+
+#: front/src/components/audio/artist/Card.vue:41
+msgid "1 album"
+msgid_plural "%{ count } albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/favorites/List.vue:10
+msgid "1 favorite"
+msgid_plural "%{ count } favorites"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/album/Card.vue:54
+#: front/src/components/federation/LibraryCard.vue:25
+msgid "1 track"
+msgid_plural "%{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/App.vue:51
+msgid "About Funkwhale"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:111
+msgid "Access disabled"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:82
+msgid "Action %{ action } was launched successfully on %{ count } element"
+msgid_plural "Action %{ action } was launched successfully on %{ count } elements"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:4
+#: front/src/components/favorites/TrackFavoriteIcon.vue:21
+msgid "Add to favorites"
+msgstr ""
+
+#: front/src/components/metadata/Search.vue:129
+#: front/src/components/audio/track/Table.vue:9
+#: front/src/components/manage/library/FilesTable.vue:40
+msgid "Album"
+msgstr ""
+
+#: front/src/components/library/import/ReleaseImport.vue:3
+msgid "Album %{ title } (%{ count } track) by %{ artist }"
+msgid_plural "Album %{ title } (%{ count } tracks) by %{ artist }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/library/Album.vue:12
+msgid "Album containing %{ count } track, by %{ artist }"
+msgid_plural "Album containing %{ count } tracks, by %{ artist }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/metadata/Search.vue:125
+#: front/src/components/federation/LibraryTrackTable.vue:34
+#: front/src/components/manage/library/RequestsTable.vue:50
+msgid "Artist"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:13
+msgid "By %{ artist }"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:57
+msgid "By confirming, %{ username } will be denied access to your library."
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:73
+msgid "By confirming, %{ username } will be granted access to your library."
+msgstr ""
+
+#: front/src/components/manage/library/FilesTable.vue:184
+#: front/src/components/manage/library/RequestsTable.vue:190
+#: front/src/components/manage/users/InvitationsTable.vue:161
+msgid "Delete"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:42
+msgid "Do you want to clear the playlist \"%{ playlist }\"?"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:34
+msgid "Do you want to delete the playlist \"%{ playlist }\"?"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:29
+msgid "Do you want to launch %{ action } on %{ count } element?"
+msgid_plural "Do you want to launch %{ action } on %{ count } elements?"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:82
+msgid "Error reporting"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:84
+msgid "Everyone"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:80
+msgid "Everyone on this instance"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:175
+#: front/src/views/admin/Settings.vue:79
+#: src/views/admin/users/UsersDetail.vue:156
+msgid "Federation"
+msgstr ""
+
+#: front/src/components/federation/LibraryCard.vue:44
+msgid "Follow"
+msgstr ""
+
+#: front/src/components/activity/Like.vue:12
+#: src/components/activity/Listen.vue:12
+msgid "from %{ album } by %{ artist }"
+msgstr ""
+
+#: front/src/components/library/Track.vue:13
+msgid "From album %{ album } by %{ artist }"
+msgstr ""
+
+#: front/src/App.vue:53
+msgid "Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"
+msgstr ""
+
+#: front/src/components/Home.vue:77
+msgid ""
+"Get quality metadata about your music thanks to\n"
+"              <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"                MusicBrainz\n"
+"              </a>"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:159
+#: front/src/components/library/Library.vue:18
+msgid "Import"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:42
+msgid "Import #%{ id } launched"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:38
+msgid "Import %{ count } track"
+msgid_plural "Import %{ count } tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:77
+msgid "Imports"
+msgstr ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:3
+msgid "In favorites"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:31
+msgid "Insert from queue (%{ count } track)"
+msgid_plural "Insert from queue (%{ count } tracks)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:75
+msgid "Instance information"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:171
+#: front/src/views/admin/users/UsersDetail.vue:152
+#: src/components/Sidebar.vue:70
+msgid "Library"
+msgstr ""
+
+#: front/src/App.vue:29
+msgid "Links"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:38
+msgid "Logged in as %{ username }"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:192
+msgid "Mark as closed"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:191
+msgid "Mark as imported"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:115
+msgid ""
+"Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the\n"
+"            <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"              MusicBrainz\n"
+"            </a>\n"
+"            project, which you can think about as the Wikipedia of music."
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:76
+msgid "Nobody except me"
+msgstr ""
+
+#: front/src/App.vue:60
+msgid "Options"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:95
+msgid "Password updated"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:47
+msgid "Play immediatly"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:11
+msgid "Playlist containing %{ count } track, by %{ username }"
+msgid_plural "Playlist containing %{ count } tracks, by %{ username }"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/views/admin/Settings.vue:78 src/components/library/Library.vue:14
+msgid "Playlists"
+msgstr ""
+
+#: front/src/components/audio/Player.vue:183
+msgid "Queue shuffled!"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:11
+msgid "Registered since %{ date }"
+msgstr ""
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:19
+msgid "Remove from favorites"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:31
+msgid "Result %{ current }/%{ total }"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:61
+msgid "Select all %{ total } elements"
+msgid_plural "Select all %{ total } elements"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/federation/LibraryCard.vue:43
+msgid "Send a follow request"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:179
+#: front/src/views/admin/users/UsersDetail.vue:160
+msgid "Settings"
+msgstr ""
+
+#: front/src/components/audio/artist/Card.vue:30
+msgid "Show 1 more album"
+msgid_plural "Show %{ count } more albums"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/album/Card.vue:40
+msgid "Show 1 more track"
+msgid_plural "Show %{ count } more tracks"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:97
+#: front/src/components/federation/LibraryTrackTable.vue:83
+#: front/src/components/library/import/BatchDetail.vue:128
+#: front/src/components/library/import/BatchList.vue:73
+#: front/src/components/manage/library/FilesTable.vue:97
+#: front/src/components/manage/library/RequestsTable.vue:104
+#: front/src/components/manage/users/InvitationsTable.vue:76
+#: front/src/components/manage/users/UsersTable.vue:87
+msgid "Showing results %{ start }-%{ end } on %{ total }"
+msgstr ""
+
+#: front/src/App.vue:38
+msgid "Source code"
+msgstr ""
+
+#: front/src/App.vue:37
+msgid "Source code (%{version})"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:81
+msgid "Statistics"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:80
+msgid "Subsonic"
+msgstr ""
+
+#: front/src/App.vue:56
+msgid "The funkwhale logo was kindly designed and provided by Francis Gading."
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:50
+msgid "This track is not imported and cannot be played"
+msgstr ""
+
+#: front/src/App.vue:130
+msgid "This will erase your local data and disconnect you, do you want to continue?"
+msgstr ""
+
+#: front/src/components/metadata/Search.vue:133
+#: front/src/components/library/import/BatchDetail.vue:85
+msgid "Track"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:167
+#: front/src/views/admin/users/UsersDetail.vue:148
+msgid "Upload"
+msgstr ""
+
+#: front/src/views/admin/Settings.vue:76 src/views/admin/users/UsersList.vue:4
+#: front/src/views/admin/users/Base.vue:6
+msgid "Users"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:7
+msgid "You are currently logged in as %{ username }"
+msgstr ""
+
+#: front/src/App.vue:5
+msgid "Choose your instance"
+msgstr ""
+
+#: front/src/App.vue:7
+msgid "You need to select an instance in order to continue"
+msgstr ""
+
+#: front/src/App.vue:33
+msgid "About this instance"
+msgstr ""
+
+#: front/src/App.vue:36
+msgid "Documentation"
+msgstr ""
+
+#: front/src/App.vue:43
+msgid "Use another instance"
+msgstr ""
+
+#: front/src/components/radios/Button.vue:5
+msgid "Start"
+msgstr ""
+
+#: front/src/components/discussion/Comment.vue:15
+msgid "Expand"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:6
+msgid "User activity"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:43
+#: src/components/library/Library.vue:8
+msgid "Artists"
+msgstr ""
+
+#: front/src/components/instance/Stats.vue:55
+msgid "tracks"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:22
+msgid "Changes synced with server"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:41
+msgid "Clear playlist"
+msgstr ""
+
+#: front/src/components/playlists/Editor.vue:45
+msgid "This will remove all tracks from this playlist and cannot be undone."
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:4
+msgid "Manage playlists"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:27
+msgid "Available playlists"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:32
+msgid "Name"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:34
+#: front/src/components/library/Album.vue:44
+msgid "Tracks"
+msgstr ""
+
+#: front/src/components/playlists/PlaylistModal.vue:64
+#: front/src/components/common/DangerousButton.vue:18
+msgid "Cancel"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:3
+msgid "Create a new playlist"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:10
+msgid "Playlist created"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:26
+msgid "Playlist visibility"
+msgstr ""
+
+#: front/src/components/playlists/Form.vue:35
+msgid "Create playlist"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:5
+msgid "Create a funkwhale account"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:14
+msgid "We cannot create your account"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:20
+#: front/src/components/manage/users/UsersTable.vue:38
+msgid "Username"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:30
+msgid "Email"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:39
+msgid "Password"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:44
+msgid "Invitation code (optional)"
+msgstr ""
+
+#: front/src/components/auth/Signup.vue:52
+msgid "Create my account"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:3
+msgid "Subsonic API password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:8
+msgid "Funkwhale is compatible with other music players that support the Subsonic API."
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:9
+msgid "You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance."
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:15
+msgid "Discover how to use Funkwhale from other apps"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:21
+msgid "Error"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:35
+#: front/src/components/auth/SubsonicTokenForm.vue:38
+msgid "Request a new password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:36
+msgid "Request a new Subsonic API password?"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:44
+msgid "Request a password"
+msgstr ""
+
+#: front/src/components/auth/SubsonicTokenForm.vue:52
+msgid "This will completely disable access to the Subsonic API using from account."
+msgstr ""
+
+#: front/src/components/auth/Login.vue:5
+msgid "Log in to your Funkwhale account"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:10
+msgid "Please double-check your username/password couple is correct"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:16
+msgid "Username or email"
+msgstr ""
+
+#: front/src/components/auth/Login.vue:18
+msgid "Create an account"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:16
+msgid "This is you!"
+msgstr ""
+
+#: front/src/components/auth/Profile.vue:20
+#: front/src/components/manage/users/UsersTable.vue:71
+msgid "Staff member"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:6
+msgid "Are you sure you want to log out?"
+msgstr ""
+
+#: front/src/components/auth/Logout.vue:9
+msgid "Yes, log me out!"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:6
+msgid "Account settings"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:28
+msgid "Update settings"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:35
+msgid "Change my password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:44
+msgid "Cannot change your password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:51
+msgid "Old password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:63
+msgid "Change password"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:64
+msgid "Change your password?"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:68
+msgid "You will be logged out from this session and have to log out with the new one"
+msgstr ""
+
+#: front/src/components/auth/Settings.vue:72
+msgid "Disable access"
+msgstr ""
+
+#: front/src/components/favorites/List.vue:6
+msgid "Loading your favorites..."
+msgstr ""
+
+#: front/src/components/favorites/List.vue:34
+#: src/components/library/Radios.vue:29
+#: front/src/components/manage/users/UsersTable.vue:20
+#: front/src/views/federation/LibraryList.vue:29
+msgid "Ascending"
+msgstr ""
+
+#: front/src/components/favorites/List.vue:39
+#: src/components/library/Radios.vue:37
+#: front/src/views/federation/LibraryList.vue:34
+msgid "Results per page"
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:6
+msgid "Error while saving settings"
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:12
+msgid "Settings updated successfully."
+msgstr ""
+
+#: front/src/components/admin/SettingsGroup.vue:64
+#: front/src/components/library/radios/Builder.vue:20
+msgid "Save"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:5
+msgid "Browsing radios"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:8
+msgid "Create your own radio"
+msgstr ""
+
+#: front/src/components/library/Radios.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:6
+#: front/src/components/manage/library/FilesTable.vue:6
+#: front/src/components/manage/library/RequestsTable.vue:6
+#: front/src/components/manage/users/InvitationsTable.vue:6
+#: front/src/components/manage/users/UsersTable.vue:6
+#: front/src/views/federation/LibraryList.vue:15
+msgid "Search"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:6
+msgid "Builder"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:12
+msgid "Radio name"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:17
+msgid "Display publicly"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:27
+msgid "Add filters to customize your radio"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:37
+msgid "Add filter"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:47
+msgid "Filter name"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:49
+msgid "Config"
+msgstr ""
+
+#: front/src/components/library/radios/Builder.vue:51
+#: front/src/components/common/ActionTable.vue:9
+msgid "Actions"
+msgstr ""
+
+#: front/src/components/library/Library.vue:5
+msgid "Browse"
+msgstr ""
+
+#: front/src/components/library/Library.vue:11
+msgid "Radios"
+msgstr ""
+
+#: front/src/components/library/Library.vue:21
+msgid "Import batches"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:5
+msgid "Browsing artists"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:16 src/views/playlists/List.vue:18
+msgid "Ordering"
+msgstr ""
+
+#: front/src/components/library/Artists.vue:24 src/views/playlists/List.vue:26
+msgid "Ordering direction"
+msgstr ""
+
+#: front/src/components/library/Artist.vue:45
+msgid "Albums by this artist"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:5
+msgid "Ensure your music files are properly tagged before uploading them."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:6
+msgid "We recommend using Picard for that purpose."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:37
+msgid "Once all your files are uploaded, simply click the following button to check the import status."
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:44
+msgid "File name"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:46
+#: front/src/components/library/import/BatchDetail.vue:67
+#: front/src/components/library/import/BatchList.vue:12
+#: front/src/components/library/import/BatchList.vue:38
+#: front/src/components/federation/LibraryFollowTable.vue:24
+#: front/src/components/federation/LibraryTrackTable.vue:32
+#: front/src/components/manage/library/RequestsTable.vue:25
+#: front/src/components/manage/users/UsersTable.vue:44
+msgid "Status"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:57
+#: front/src/components/library/import/BatchDetail.vue:72
+#: front/src/components/library/import/BatchList.vue:17
+msgid "Success"
+msgstr ""
+
+#: front/src/components/library/import/FileUpload.vue:60
+#: front/src/components/library/import/BatchDetail.vue:34
+#: front/src/components/library/import/BatchDetail.vue:70
+#: front/src/components/library/import/BatchList.vue:15
+#: front/src/components/federation/LibraryFollowTable.vue:47
+#: front/src/components/manage/library/RequestsTable.vue:28
+#: front/src/components/manage/library/RequestsTable.vue:64
+msgid "Pending"
+msgstr ""
+
+#: front/src/components/library/import/ArtistImport.vue:8
+msgid "Filter album types"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:18
+#: front/src/components/library/import/BatchDetail.vue:83
+msgid "Source"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:43
+msgid "Search query"
+msgstr ""
+
+#: front/src/components/library/import/TrackImport.vue:45
+msgid "Imported URL"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:11
+msgid "Import batch"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:19
+#: front/src/components/library/import/BatchList.vue:36
+msgid "Launch date"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:38
+msgid "Skipped"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:42
+msgid "Errored"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:55
+msgid "Finished"
+msgstr ""
+
+#: front/src/components/library/import/BatchDetail.vue:81
+msgid "Job ID"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:7
+msgid "Import source"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:13
+msgid "Metadata"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:19
+msgid "Music"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:30
+msgid "Next step"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:66
+msgid "External source. Supported backends"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:86
+msgid "Search an entity you want to import:"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:94
+msgid "Or"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:104
+msgid "You will import:"
+msgstr ""
+
+#: front/src/components/library/import/Main.vue:150
+msgid "This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:23
+#: front/src/components/federation/LibraryTrackTable.vue:12
+msgid "Any"
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:25
+msgid "API"
+msgstr ""
+
+#: front/src/components/library/import/BatchList.vue:40
+msgid "Submitted by"
+msgstr ""
+
+#: front/src/components/library/Track.vue:24
+msgid "Artist page"
+msgstr ""
+
+#: front/src/components/library/Track.vue:31
+msgid "Play"
+msgstr ""
+
+#: front/src/components/library/Track.vue:41
+msgid "Search on Wikipedia"
+msgstr ""
+
+#: front/src/components/library/Track.vue:45
+msgid "View on MusicBrainz"
+msgstr ""
+
+#: front/src/components/library/Track.vue:49
+#: src/components/audio/track/Table.vue:24
+msgid "Download"
+msgstr ""
+
+#: front/src/components/library/Track.vue:59
+#: front/src/components/manage/library/FilesTable.vue:44
+msgid "Duration"
+msgstr ""
+
+#: front/src/components/library/Track.vue:65
+#: src/components/library/Track.vue:76
+#: front/src/components/manage/library/FilesTable.vue:64
+#: front/src/components/manage/library/FilesTable.vue:76
+#: front/src/components/manage/library/FilesTable.vue:82
+#: front/src/components/manage/library/RequestsTable.vue:72
+#: front/src/components/manage/library/RequestsTable.vue:83
+#: front/src/components/manage/users/UsersTable.vue:62
+msgid "N/A"
+msgstr ""
+
+#: front/src/components/library/Track.vue:70
+msgid "Size"
+msgstr ""
+
+#: front/src/components/library/Track.vue:81
+msgid "Bitrate"
+msgstr ""
+
+#: front/src/components/library/Home.vue:10
+msgid "Latest artists"
+msgstr ""
+
+#: front/src/components/library/Home.vue:27
+msgid "Music requests"
+msgstr ""
+
+#: front/src/components/requests/Card.vue:26
+#: front/src/components/manage/library/RequestsTable.vue:89
+msgid "Create import"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:4
+msgid "Something's missing in the library? Let us know what you would like to listen!"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:11
+msgid "Leave this field empty if you're requesting the whole discography."
+msgstr ""
+
+#: front/src/components/requests/Form.vue:15
+#: front/src/components/manage/library/RequestsTable.vue:52
+msgid "Comment"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:22
+msgid "We've received your request, you'll get some groove soon ;)"
+msgstr ""
+
+#: front/src/components/requests/Form.vue:27
+msgid "Pending requests"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:12
+msgid "Pending approval"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:22
+msgid "Actor"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:39
+msgid "Approved"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:43
+msgid "Refused"
+msgstr ""
+
+#: front/src/components/federation/LibraryFollowTable.vue:64
+msgid "Deny"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:53
+msgid "Not imported"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:36
+msgid "Published date"
+msgstr ""
+
+#: front/src/components/federation/LibraryTrackTable.vue:51
+msgid "In library"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:4
+msgid "Federate with a new instance"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:11
+msgid "Error while scanning library"
+msgstr ""
+
+#: front/src/components/federation/LibraryForm.vue:32
+msgid "Launch scan"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:44
+#: front/src/components/audio/artist/Card.vue:34
+msgid "Collapse"
+msgstr ""
+
+#: front/src/components/audio/album/Card.vue:51
+msgid "Play all"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:14
+msgid "Add to queue"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:15
+msgid "Play next"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:16
+msgid "Play now"
+msgstr ""
+
+#: front/src/components/audio/PlayButton.vue:145
+msgid "%{ count } track was added to your queue"
+msgid_plural "%{ count } tracks were added to your queue"
+msgstr[0] ""
+msgstr[1] ""
+
+#: front/src/components/audio/track/Table.vue:7
+#: front/src/components/manage/library/FilesTable.vue:38
+msgid "Title"
+msgstr ""
+
+#: front/src/components/audio/track/Table.vue:30
+msgid "There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as cURL to easily download a list of tracks."
+msgstr ""
+
+#: front/src/components/audio/Search.vue:3
+msgid "Search for some music"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:8
+msgid "Page not found!"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:11
+msgid "We're sorry, the page you asked for does not exist:"
+msgstr ""
+
+#: front/src/components/PageNotFound.vue:15
+msgid "Go to home page"
+msgstr ""
+
+#: front/src/components/About.vue:16
+msgid "Unfortunately, owners of this instance did not yet take the time to complete this page."
+msgstr ""
+
+#: front/src/components/Sidebar.vue:21
+msgid "Queue"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:23
+msgid "(empty)"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:43
+msgid "Logout"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:44
+msgid "Login"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:64
+msgid "Administration"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:124
+msgid "Yes"
+msgstr ""
+
+#: front/src/components/Sidebar.vue:158
+msgid "New tracks will be appended here automatically."
+msgstr ""
+
+#: front/src/components/manage/library/FilesTable.vue:42
+msgid "Type"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:30
+#: front/src/components/manage/library/RequestsTable.vue:62
+msgid "Imported"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:48
+msgid "User"
+msgstr ""
+
+#: front/src/components/manage/library/RequestsTable.vue:54
+msgid "Import date"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:5
+msgid "Error while creating invitation"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:12
+msgid "Invitation code"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:27
+#: front/src/components/manage/users/InvitationsTable.vue:43
+msgid "Code"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationForm.vue:38
+msgid "Clear"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:20
+msgid "All"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:22
+msgid "Expired/used"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:39
+msgid "Owner"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:41
+msgid "Creation date"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:50
+msgid "Used"
+msgstr ""
+
+#: front/src/components/manage/users/InvitationsTable.vue:52
+msgid "Not used"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:40
+msgid "Account status"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:42
+msgid "Last activity"
+msgstr ""
+
+#: front/src/components/manage/users/UsersTable.vue:54
+msgid "Active"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:22
+msgid "Go"
+msgstr ""
+
+#: front/src/components/common/ActionTable.vue:40
+msgid "Launch"
+msgstr ""
+
+#: front/src/components/Home.vue:6
+msgid "Welcome on Funkwhale"
+msgstr ""
+
+#: front/src/components/Home.vue:14
+msgid "Get me to the library"
+msgstr ""
+
+#: front/src/components/Home.vue:25
+msgid "Why funkwhale?"
+msgstr ""
+
+#: front/src/components/Home.vue:40
+msgid "Funkwhale is designed to make it easy to listen to music you like, or to discover new artists."
+msgstr ""
+
+#: front/src/components/Home.vue:45
+msgid "Click once, listen for hours using built-in radios"
+msgstr ""
+
+#: front/src/components/Home.vue:57
+msgid "Playlists? We got them"
+msgstr ""
+
+#: front/src/components/Home.vue:67
+msgid "Funkwhale takes care of handling your music"
+msgstr ""
+
+#: front/src/components/Home.vue:72
+msgid "Import music from various platforms, such as YouTube or SoundCloud"
+msgstr ""
+
+#: front/src/components/Home.vue:89
+msgid "Covers, lyrics, our goal is to have them all ;)"
+msgstr ""
+
+#: front/src/components/Home.vue:99
+msgid "Funkwhale is dead simple to use."
+msgstr ""
+
+#: front/src/components/Home.vue:104
+msgid "No add-ons, no plugins : you only need a web library"
+msgstr ""
+
+#: front/src/components/Home.vue:118
+msgid "Your music, your way"
+msgstr ""
+
+#: front/src/components/Home.vue:131
+msgid "We do not track you or bother you with ads"
+msgstr ""
+
+#: front/src/views/instance/Timeline.vue:5
+msgid "Loading timeline..."
+msgstr ""
+
+#: front/src/views/instance/Timeline.vue:8
+msgid "Recent activity on this instance"
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:30
+msgid "Edit..."
+msgstr ""
+
+#: front/src/views/playlists/Detail.vue:38
+msgid "Delete playlist"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:4
+msgid "Browsing playlists"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:9
+msgid "Manage your playlists"
+msgstr ""
+
+#: front/src/views/playlists/List.vue:29
+msgid "Descending"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:5
+msgid "Reset your password"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:15
+msgid "Account's email"
+msgstr ""
+
+#: front/src/views/auth/PasswordReset.vue:25
+#: front/src/views/auth/PasswordResetConfirm.vue:19
+#: front/src/views/auth/EmailConfirm.vue:18
+msgid "Back to login"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:5
+msgid "Change your password"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:29
+msgid "Password updated successfully"
+msgstr ""
+
+#: front/src/views/auth/PasswordResetConfirm.vue:32
+msgid "Proceed to login"
+msgstr ""
+
+#: front/src/views/auth/EmailConfirm.vue:5
+msgid "Confirm your email"
+msgstr ""
+
+#: front/src/views/auth/EmailConfirm.vue:25
+msgid "Your email address was confirmed, you can now use the service without limitations."
+msgstr ""
+
+#: front/src/views/admin/library/RequestsList.vue:4
+msgid "Import requests"
+msgstr ""
+
+#: front/src/views/admin/library/FilesList.vue:4
+msgid "Library files"
+msgstr ""
+
+#: front/src/views/admin/library/Base.vue:6
+msgid "Files"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:30
+msgid "Email address"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:55
+msgid "Account active"
+msgstr ""
+
+#: front/src/views/admin/users/UsersDetail.vue:85
+msgid "Refresh"
+msgstr ""
+
+#: front/src/views/admin/users/InvitationsList.vue:4
+msgid "Invitations"
+msgstr ""
+
+#: front/src/views/federation/LibraryFollowersList.vue:4
+msgid "Browsing followers"
+msgstr ""
+
+#: front/src/views/federation/LibraryTrackList.vue:4
+msgid "Browsing federated tracks"
+msgstr ""
+
+#: front/src/views/federation/LibraryList.vue:4
+msgid "Browsing libraries"
+msgstr ""
+
+#: front/src/views/federation/LibraryList.vue:9
+msgid "Add a new library"
+msgstr ""
+
+#: front/src/views/federation/LibraryDetail.vue:57
+msgid "Auto importing"
+msgstr ""
+
+#: front/src/views/federation/LibraryDetail.vue:85
+msgid "Library size"
+msgstr ""
+
+#: front/src/views/federation/Base.vue:6
+msgid "Libraries"
+msgstr ""
+
+#: front/src/views/federation/Base.vue:14
+msgid "Followers"
+msgstr ""
diff --git a/front/locales/fr_FR/LC_MESSAGES/app.po b/front/locales/fr_FR/LC_MESSAGES/app.po
new file mode 100644
index 0000000000000000000000000000000000000000..217bbc30b430257a42317ff738d21ad3d2a7c431
--- /dev/null
+++ b/front/locales/fr_FR/LC_MESSAGES/app.po
@@ -0,0 +1,1382 @@
+# French translations for front package.
+# Copyright (C) 2018 THE front'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the front package.
+# Automatically generated, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: front 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-30 16:25+0200\n"
+"PO-Revision-Date: 2018-06-30 18:18+0000\n"
+"Last-Translator: Eliot Berriot <contact@eliotberriot.com>\n"
+"Language-Team: French <https://translate.funkwhale.audio/projects/funkwhale/"
+"front/fr/>\n"
+"Language: fr_FR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 2.20\n"
+
+#: front/src/components/playlists/PlaylistModal.vue:9
+msgid "\"%{ title }\", by %{ artist }"
+msgstr "\"%{ title }\", de %{ artist }"
+
+#: front/src/components/Sidebar.vue:24
+msgid "(%{ index } of %{ length })"
+msgstr "(%{ index } sur %{ length })"
+
+#: front/src/components/common/ActionTable.vue:43
+#: front/src/components/common/ActionTable.vue:51
+msgid "%{ count } on %{ total } selected"
+msgid_plural "%{ count } on %{ total } selected"
+msgstr[0] "%{ count } sur %{ total } élément sélectionné"
+msgstr[1] "%{ count } sur %{ total } éléments sélectionnés"
+
+#: front/src/components/Sidebar.vue:115
+#: src/views/federation/LibraryDetail.vue:87
+msgid "%{ count } track"
+msgid_plural "%{ count } tracks"
+msgstr[0] "%{ count } piste"
+msgstr[1] "%{ count } pistes"
+
+#: front/src/components/library/Artist.vue:13
+msgid "%{ count } track in %{ albumsCount } albums"
+msgid_plural "%{ count } tracks in %{ albumsCount } albums"
+msgstr[0] "%{ count } piste dans %{ albumsCount } albums"
+msgstr[1] "%{ count } pistes dans %{ albumsCount } albums"
+
+#: front/src/components/library/radios/Builder.vue:66
+msgid "%{ count } track matching combined filters"
+msgid_plural "%{ count } tracks matching combined filters"
+msgstr[0] "%{ count } piste correspondant aux filtres sélectionnés"
+msgstr[1] "%{ count } pistes correspondant aux filtres sélectionnés"
+
+#: front/src/components/playlists/Card.vue:19
+msgid "%{ count} track"
+msgid_plural "%{ count } tracks"
+msgstr[0] "%{ count} piste"
+msgstr[1] "%{ count } pistes"
+
+#: front/src/components/activity/Like.vue:7
+msgid "%{ user } favorited a track"
+msgstr "%{ user } a ajouté une piste a ses favoris"
+
+#: front/src/components/activity/Listen.vue:7
+msgid "%{ user } listened to a track"
+msgstr "%{ user } a écouté une piste"
+
+#: front/src/components/audio/artist/Card.vue:41
+msgid "1 album"
+msgid_plural "%{ count } albums"
+msgstr[0] "1 album"
+msgstr[1] "%{ count } albums"
+
+#: front/src/components/favorites/List.vue:10
+msgid "1 favorite"
+msgid_plural "%{ count } favorites"
+msgstr[0] "1 favoris"
+msgstr[1] "%{ count } favoris"
+
+#: front/src/components/audio/album/Card.vue:54
+#: front/src/components/federation/LibraryCard.vue:25
+msgid "1 track"
+msgid_plural "%{ count } tracks"
+msgstr[0] "1 piste"
+msgstr[1] "%{ count } pistes"
+
+#: front/src/App.vue:51
+msgid "About Funkwhale"
+msgstr "À propos de Funkwhale"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:111
+msgid "Access disabled"
+msgstr "Accès désactivé"
+
+#: front/src/components/common/ActionTable.vue:82
+msgid "Action %{ action } was launched successfully on %{ count } element"
+msgid_plural "Action %{ action } was launched successfully on %{ count } elements"
+msgstr[0] "L'action %{ action } a été lancée avec succès sur %{ count } élément"
+msgstr[1] "L'action %{ action } a été lancée avec succès sur %{ count } éléments"
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:4
+#: front/src/components/favorites/TrackFavoriteIcon.vue:21
+msgid "Add to favorites"
+msgstr "Ajouter aux favoris"
+
+#: front/src/components/metadata/Search.vue:129
+#: front/src/components/audio/track/Table.vue:9
+#: front/src/components/manage/library/FilesTable.vue:40
+msgid "Album"
+msgstr "Album"
+
+#: front/src/components/library/import/ReleaseImport.vue:3
+msgid "Album %{ title } (%{ count } track) by %{ artist }"
+msgid_plural "Album %{ title } (%{ count } tracks) by %{ artist }"
+msgstr[0] "Album %{ title } (%{ count } piste) par %{ artist }"
+msgstr[1] "Album %{ title } (%{ count } pistes) by %{ artist }"
+
+#: front/src/components/library/Album.vue:12
+msgid "Album containing %{ count } track, by %{ artist }"
+msgid_plural "Album containing %{ count } tracks, by %{ artist }"
+msgstr[0] "Album incluant %{ count } piste, de %{ artist }"
+msgstr[1] "Album incluant %{ count } pistes, de %{ artist }"
+
+#: front/src/components/metadata/Search.vue:125
+#: front/src/components/federation/LibraryTrackTable.vue:34
+#: front/src/components/manage/library/RequestsTable.vue:50
+msgid "Artist"
+msgstr "Artiste"
+
+#: front/src/components/audio/album/Card.vue:13
+msgid "By %{ artist }"
+msgstr "De %{ artist }"
+
+#: front/src/components/federation/LibraryFollowTable.vue:57
+msgid "By confirming, %{ username } will be denied access to your library."
+msgstr ""
+"Si vous confirmez, %{ username } se verra refuser l'accès à votre "
+"bibliothèque."
+
+#: front/src/components/federation/LibraryFollowTable.vue:73
+msgid "By confirming, %{ username } will be granted access to your library."
+msgstr ""
+"Si vous confirmez, %{ username } se verra accorder l'accès à votre "
+"bitliothèque."
+
+#: front/src/components/manage/library/FilesTable.vue:184
+#: front/src/components/manage/library/RequestsTable.vue:190
+#: front/src/components/manage/users/InvitationsTable.vue:161
+msgid "Delete"
+msgstr "Supprimer"
+
+#: front/src/components/playlists/Editor.vue:42
+msgid "Do you want to clear the playlist \"%{ playlist }\"?"
+msgstr "Voulez-vous vider la playlist \"%{ playlist }\" ?"
+
+#: front/src/views/playlists/Detail.vue:34
+msgid "Do you want to delete the playlist \"%{ playlist }\"?"
+msgstr "Voulez-vous supprimer la playlist \"%{ playlist }\" ?"
+
+#: front/src/components/common/ActionTable.vue:29
+msgid "Do you want to launch %{ action } on %{ count } element?"
+msgid_plural "Do you want to launch %{ action } on %{ count } elements?"
+msgstr[0] "Voulez-vous effectuer l'action \"%{ action } sur %{ count } élément ?"
+msgstr[1] "Voulez-vous effectuer l'action \"%{ action } sur %{ count } éléments ?"
+
+#: front/src/views/admin/Settings.vue:82
+msgid "Error reporting"
+msgstr "Rapports d'erreur"
+
+#: front/src/components/playlists/Form.vue:84
+msgid "Everyone"
+msgstr "Tout le monde"
+
+#: front/src/components/playlists/Form.vue:80
+msgid "Everyone on this instance"
+msgstr "Tout le monde sur cette instance"
+
+#: front/src/components/manage/users/UsersTable.vue:175
+#: front/src/views/admin/Settings.vue:79
+#: src/views/admin/users/UsersDetail.vue:156
+msgid "Federation"
+msgstr "Fédération"
+
+#: front/src/components/federation/LibraryCard.vue:44
+msgid "Follow"
+msgstr "Suivre"
+
+#: front/src/components/activity/Like.vue:12
+#: src/components/activity/Listen.vue:12
+msgid "from %{ album } by %{ artist }"
+msgstr "de l'album %{ album } par %{ artist }"
+
+#: front/src/components/library/Track.vue:13
+msgid "From album %{ album } by %{ artist }"
+msgstr "De l'album %{ album } par %{ artist }"
+
+#: front/src/App.vue:53
+msgid "Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"
+msgstr ""
+"Funkwale est un projet open-source et gratuit, animé par des bénévoles. Vous "
+"pouvez nous aider à améliorer le service en remontant les problèmes que vous "
+"rencontrez, en suggérant des améliorations et en parlant du projet autour de "
+"vous !"
+
+#: front/src/components/Home.vue:77
+msgid ""
+"Get quality metadata about your music thanks to\n"
+"              <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"                MusicBrainz\n"
+"              </a>"
+msgstr ""
+"Obtenez des métadonnées de qualité grâce à              <a href=\""
+"https://musicbrainz.org\" target=\"_blank\">\n"
+"                MusicBrainz\n"
+"              </a>"
+
+#: front/src/components/federation/LibraryTrackTable.vue:159
+#: front/src/components/library/Library.vue:18
+msgid "Import"
+msgstr "Import"
+
+#: front/src/components/federation/LibraryTrackTable.vue:42
+msgid "Import #%{ id } launched"
+msgstr "Import #%{ id } démarré"
+
+#: front/src/components/library/import/Main.vue:38
+msgid "Import %{ count } track"
+msgid_plural "Import %{ count } tracks"
+msgstr[0] "Import %{ count } track"
+msgstr[1] "Importer %{ count } pistes"
+
+#: front/src/views/admin/Settings.vue:77
+msgid "Imports"
+msgstr "Imports"
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:3
+msgid "In favorites"
+msgstr "Dans les favoris"
+
+#: front/src/components/playlists/Editor.vue:31
+msgid "Insert from queue (%{ count } track)"
+msgid_plural "Insert from queue (%{ count } tracks)"
+msgstr[0] "Insérer depuis la queue (%{ count } piste)"
+msgstr[1] "Insérer depuis la queue (%{ count } pistes)"
+
+#: front/src/views/admin/Settings.vue:75
+msgid "Instance information"
+msgstr "Informations relatives à cette instance"
+
+#: front/src/components/manage/users/UsersTable.vue:171
+#: front/src/views/admin/users/UsersDetail.vue:152
+#: src/components/Sidebar.vue:70
+msgid "Library"
+msgstr "Bibliothèque"
+
+#: front/src/App.vue:29
+msgid "Links"
+msgstr "Liens"
+
+#: front/src/components/Sidebar.vue:38
+msgid "Logged in as %{ username }"
+msgstr "Connecté·e en tant que %{ username }"
+
+#: front/src/components/manage/library/RequestsTable.vue:192
+msgid "Mark as closed"
+msgstr "Marquer comme fermé"
+
+#: front/src/components/manage/library/RequestsTable.vue:191
+msgid "Mark as imported"
+msgstr "Marquer comme importé"
+
+#: front/src/components/library/import/Main.vue:115
+msgid ""
+"Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the\n"
+"            <a href=\"https://musicbrainz.org\" target=\"_blank\">\n"
+"              MusicBrainz\n"
+"            </a>\n"
+"            project, which you can think about as the Wikipedia of music."
+msgstr ""
+"Les métadonnées sont les informations relatives à la musique que vous "
+"souhaitez importer. Cela inclut notamment toutes les informations concernant "
+"les artistes, albums et pistes. Afin d'avoir des métadonnées de qualité, il "
+"est recommandé d'utiliser les métadonnées du projet            <a href=\""
+"https://musicbrainz.org\" target=\"_blank\">\n"
+"              MusicBrainz\n"
+"            </a>\n"
+"            qui est une base de données musicale fonctionnant sur le même "
+"modèle que Wikipédia."
+
+#: front/src/components/playlists/Form.vue:76
+msgid "Nobody except me"
+msgstr "Personne à part moi"
+
+#: front/src/App.vue:60
+msgid "Options"
+msgstr "Options"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:95
+msgid "Password updated"
+msgstr "Mot de passe mis à jour"
+
+#: front/src/components/audio/PlayButton.vue:47
+msgid "Play immediatly"
+msgstr "Jouer immédiatement"
+
+#: front/src/views/playlists/Detail.vue:11
+msgid "Playlist containing %{ count } track, by %{ username }"
+msgid_plural "Playlist containing %{ count } tracks, by %{ username }"
+msgstr[0] "Playlist contenant %{ count } piste, par %{ username }"
+msgstr[1] "Playlist contenant %{ count } pistes, par %{ username }"
+
+#: front/src/views/admin/Settings.vue:78 src/components/library/Library.vue:14
+msgid "Playlists"
+msgstr "Playlists"
+
+#: front/src/components/audio/Player.vue:183
+msgid "Queue shuffled!"
+msgstr "La queue a été mélangée !"
+
+#: front/src/components/auth/Profile.vue:11
+msgid "Registered since %{ date }"
+msgstr "Inscrit·e depuis le %{ date }"
+
+#: front/src/components/favorites/TrackFavoriteIcon.vue:19
+msgid "Remove from favorites"
+msgstr "Retirer des favoris"
+
+#: front/src/components/library/import/TrackImport.vue:31
+msgid "Result %{ current }/%{ total }"
+msgstr "Résultat %{ current }/%{ total }"
+
+#: front/src/components/common/ActionTable.vue:61
+msgid "Select all %{ total } elements"
+msgid_plural "Select all %{ total } elements"
+msgstr[0] "Selectionner l'ensemble des %{ total } élément"
+msgstr[1] "Selectionner l'ensemble des %{ total } éléments"
+
+#: front/src/components/federation/LibraryCard.vue:43
+msgid "Send a follow request"
+msgstr "Envoyer une demande d'abonnement"
+
+#: front/src/components/manage/users/UsersTable.vue:179
+#: front/src/views/admin/users/UsersDetail.vue:160
+msgid "Settings"
+msgstr "Paramètres"
+
+#: front/src/components/audio/artist/Card.vue:30
+msgid "Show 1 more album"
+msgid_plural "Show %{ count } more albums"
+msgstr[0] "Montrer 1 album supplémentaire"
+msgstr[1] "Montrer %{ count } albums supplémentaires"
+
+#: front/src/components/audio/album/Card.vue:40
+msgid "Show 1 more track"
+msgid_plural "Show %{ count } more tracks"
+msgstr[0] "Afficher 1 piste de plus"
+msgstr[1] "Afficher %{ count } pistes de plus"
+
+#: front/src/components/federation/LibraryFollowTable.vue:97
+#: front/src/components/federation/LibraryTrackTable.vue:83
+#: front/src/components/library/import/BatchDetail.vue:128
+#: front/src/components/library/import/BatchList.vue:73
+#: front/src/components/manage/library/FilesTable.vue:97
+#: front/src/components/manage/library/RequestsTable.vue:104
+#: front/src/components/manage/users/InvitationsTable.vue:76
+#: front/src/components/manage/users/UsersTable.vue:87
+msgid "Showing results %{ start }-%{ end } on %{ total }"
+msgstr "Affichage des résultats %{ start }-%{ end } sur %{ total }"
+
+#: front/src/App.vue:38
+msgid "Source code"
+msgstr "Code source"
+
+#: front/src/App.vue:37
+msgid "Source code (%{version})"
+msgstr "Code source (%{version})"
+
+#: front/src/views/admin/Settings.vue:81
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: front/src/views/admin/Settings.vue:80
+msgid "Subsonic"
+msgstr "Subsonic"
+
+#: front/src/App.vue:56
+msgid "The funkwhale logo was kindly designed and provided by Francis Gading."
+msgstr ""
+"Le logo de Funkwhale a été généreusement dessiné et fourni par Francis "
+"Gading."
+
+#: front/src/components/audio/PlayButton.vue:50
+msgid "This track is not imported and cannot be played"
+msgstr "Cette piste n'est pas importée et ne peut pas être jouée"
+
+#: front/src/App.vue:130
+msgid "This will erase your local data and disconnect you, do you want to continue?"
+msgstr ""
+"Cela va effacer vos données locales et vous déconnecter, voulez-vous "
+"continuer ?"
+
+#: front/src/components/metadata/Search.vue:133
+#: front/src/components/library/import/BatchDetail.vue:85
+msgid "Track"
+msgstr "Piste"
+
+#: front/src/components/manage/users/UsersTable.vue:167
+#: front/src/views/admin/users/UsersDetail.vue:148
+msgid "Upload"
+msgstr "Envoi"
+
+#: front/src/views/admin/Settings.vue:76 src/views/admin/users/UsersList.vue:4
+#: front/src/views/admin/users/Base.vue:6
+msgid "Users"
+msgstr "Utilisateur·ice·s"
+
+#: front/src/components/auth/Logout.vue:7
+msgid "You are currently logged in as %{ username }"
+msgstr "Vous êtes actuellement connecté·e en tant que %{ username }"
+
+#: front/src/App.vue:5
+msgid "Choose your instance"
+msgstr "Choisissez votre instance"
+
+#: front/src/App.vue:7
+msgid "You need to select an instance in order to continue"
+msgstr "Vous devez choisir une instance pour continuer"
+
+#: front/src/App.vue:33
+msgid "About this instance"
+msgstr "A propos de cette instance"
+
+#: front/src/App.vue:36
+msgid "Documentation"
+msgstr "Documentation"
+
+#: front/src/App.vue:43
+msgid "Use another instance"
+msgstr "Utiliser une autre instance"
+
+#: front/src/components/radios/Button.vue:5
+msgid "Start"
+msgstr "Démarrer"
+
+#: front/src/components/discussion/Comment.vue:15
+msgid "Expand"
+msgstr "Ouvrir"
+
+#: front/src/components/instance/Stats.vue:6
+msgid "User activity"
+msgstr "Activité des utilisateur·ice·s"
+
+#: front/src/components/instance/Stats.vue:43
+#: src/components/library/Library.vue:8
+msgid "Artists"
+msgstr "Artistes"
+
+#: front/src/components/instance/Stats.vue:55
+msgid "tracks"
+msgstr "pistes"
+
+#: front/src/components/playlists/Editor.vue:22
+msgid "Changes synced with server"
+msgstr "Changements synchronisés avec le serveur"
+
+#: front/src/components/playlists/Editor.vue:41
+msgid "Clear playlist"
+msgstr "Vider la playlist"
+
+#: front/src/components/playlists/Editor.vue:45
+msgid "This will remove all tracks from this playlist and cannot be undone."
+msgstr ""
+"Cela supprimera toutes les pistes de la playlist et ne pourra pas être "
+"annulé."
+
+#: front/src/components/playlists/PlaylistModal.vue:4
+msgid "Manage playlists"
+msgstr "Gérer les playlists"
+
+#: front/src/components/playlists/PlaylistModal.vue:27
+msgid "Available playlists"
+msgstr "Playlists disponibles"
+
+#: front/src/components/playlists/PlaylistModal.vue:32
+msgid "Name"
+msgstr "Nom"
+
+#: front/src/components/playlists/PlaylistModal.vue:34
+#: front/src/components/library/Album.vue:44
+msgid "Tracks"
+msgstr "Pistes"
+
+#: front/src/components/playlists/PlaylistModal.vue:64
+#: front/src/components/common/DangerousButton.vue:18
+msgid "Cancel"
+msgstr "Annuler"
+
+#: front/src/components/playlists/Form.vue:3
+msgid "Create a new playlist"
+msgstr "Créer une nouvelle playlist"
+
+#: front/src/components/playlists/Form.vue:10
+msgid "Playlist created"
+msgstr "Playlist créée"
+
+#: front/src/components/playlists/Form.vue:26
+msgid "Playlist visibility"
+msgstr "Visibilité de la playlist"
+
+#: front/src/components/playlists/Form.vue:35
+msgid "Create playlist"
+msgstr "Créer une playlist"
+
+#: front/src/components/auth/Signup.vue:5
+msgid "Create a funkwhale account"
+msgstr "Créer un compte funkwhale"
+
+#: front/src/components/auth/Signup.vue:14
+msgid "We cannot create your account"
+msgstr "Nous ne pouvons pas créer votre compte"
+
+#: front/src/components/auth/Signup.vue:20
+#: front/src/components/manage/users/UsersTable.vue:38
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: front/src/components/auth/Signup.vue:30
+msgid "Email"
+msgstr "Email"
+
+#: front/src/components/auth/Signup.vue:39
+msgid "Password"
+msgstr "Mot de passe"
+
+#: front/src/components/auth/Signup.vue:44
+msgid "Invitation code (optional)"
+msgstr "Code d'invitation (optionnel)"
+
+#: front/src/components/auth/Signup.vue:52
+msgid "Create my account"
+msgstr "Créer votre compte"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:3
+msgid "Subsonic API password"
+msgstr "Mot de passe de l'API Subsonic"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:8
+msgid "Funkwhale is compatible with other music players that support the Subsonic API."
+msgstr ""
+"Funkwhale est compatible avec d'autres lecteurs de musique qui supportent "
+"l'API Subsonic."
+
+#: front/src/components/auth/SubsonicTokenForm.vue:9
+msgid "You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance."
+msgstr ""
+"Vous pouvez les utiliser pour profiter de vos playlists et de votre musique "
+"en mode hors-ligne sur votre smatphone ou tablette, par exemple."
+
+#: front/src/components/auth/SubsonicTokenForm.vue:15
+msgid "Discover how to use Funkwhale from other apps"
+msgstr "Découvrez comment utiliser Funkwhale sur d'autres applications"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:21
+msgid "Error"
+msgstr "Erreur"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:35
+#: front/src/components/auth/SubsonicTokenForm.vue:38
+msgid "Request a new password"
+msgstr "Demander un nouveau mot de passe"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:36
+msgid "Request a new Subsonic API password?"
+msgstr "Demander un nouveau mot de passe pour l'API Subsonic ?"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:44
+msgid "Request a password"
+msgstr "Demander un mot de passe"
+
+#: front/src/components/auth/SubsonicTokenForm.vue:52
+msgid "This will completely disable access to the Subsonic API using from account."
+msgstr ""
+"Cela désactivera complétement l'accès à l'API Subsonic depuis votre compte."
+
+#: front/src/components/auth/Login.vue:5
+msgid "Log in to your Funkwhale account"
+msgstr "Connectez vous à votre compte Funkwhale"
+
+#: front/src/components/auth/Login.vue:10
+msgid "Please double-check your username/password couple is correct"
+msgstr ""
+"Merci de vérifier que votre nom d'utilisateur et mot de passe sont corrects"
+
+#: front/src/components/auth/Login.vue:16
+msgid "Username or email"
+msgstr "Nom d'utilisateur ou email"
+
+#: front/src/components/auth/Login.vue:18
+msgid "Create an account"
+msgstr "Créer un compte"
+
+#: front/src/components/auth/Profile.vue:16
+msgid "This is you!"
+msgstr "C'est vous !"
+
+#: front/src/components/auth/Profile.vue:20
+#: front/src/components/manage/users/UsersTable.vue:71
+msgid "Staff member"
+msgstr "Membre de l'équipe"
+
+#: front/src/components/auth/Logout.vue:6
+msgid "Are you sure you want to log out?"
+msgstr "Êtes-vous sur de vouloir vous déconnecter ?"
+
+#: front/src/components/auth/Logout.vue:9
+msgid "Yes, log me out!"
+msgstr "Oui, déconnectez-moi !"
+
+#: front/src/components/auth/Settings.vue:6
+msgid "Account settings"
+msgstr "Paramètres du compte"
+
+#: front/src/components/auth/Settings.vue:28
+msgid "Update settings"
+msgstr "Mettre à jour les paramètres"
+
+#: front/src/components/auth/Settings.vue:35
+msgid "Change my password"
+msgstr "Changer mon mot de passe"
+
+#: front/src/components/auth/Settings.vue:44
+msgid "Cannot change your password"
+msgstr "Mot de passe ne peut pas être changé"
+
+#: front/src/components/auth/Settings.vue:51
+msgid "Old password"
+msgstr "Ancien mot de passe"
+
+#: front/src/components/auth/Settings.vue:63
+msgid "Change password"
+msgstr "Changer le mot de passe"
+
+#: front/src/components/auth/Settings.vue:64
+msgid "Change your password?"
+msgstr "Changer le mot de passe ?"
+
+#: front/src/components/auth/Settings.vue:68
+msgid "You will be logged out from this session and have to log out with the new one"
+msgstr ""
+"Vous allez être déconnecté de cette session et vous allez devoir vous "
+"connecter avec votre nouveau mot de passe"
+
+#: front/src/components/auth/Settings.vue:72
+msgid "Disable access"
+msgstr "Désactiver l'accès"
+
+#: front/src/components/favorites/List.vue:6
+msgid "Loading your favorites..."
+msgstr "Chargement de vos favoris ..."
+
+#: front/src/components/favorites/List.vue:34
+#: src/components/library/Radios.vue:29
+#: front/src/components/manage/users/UsersTable.vue:20
+#: front/src/views/federation/LibraryList.vue:29
+msgid "Ascending"
+msgstr "Ascendant"
+
+#: front/src/components/favorites/List.vue:39
+#: src/components/library/Radios.vue:37
+#: front/src/views/federation/LibraryList.vue:34
+msgid "Results per page"
+msgstr "Résultats par page"
+
+#: front/src/components/admin/SettingsGroup.vue:6
+msgid "Error while saving settings"
+msgstr "Erreur pendant l'enregistrement des paramètres"
+
+#: front/src/components/admin/SettingsGroup.vue:12
+msgid "Settings updated successfully."
+msgstr "Paramètres modifiés avec succès."
+
+#: front/src/components/admin/SettingsGroup.vue:64
+#: front/src/components/library/radios/Builder.vue:20
+msgid "Save"
+msgstr "Enregistrer"
+
+#: front/src/components/library/Radios.vue:5
+msgid "Browsing radios"
+msgstr "Parcourir les radios"
+
+#: front/src/components/library/Radios.vue:8
+msgid "Create your own radio"
+msgstr "Créer votre propre radio"
+
+#: front/src/components/library/Radios.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:6
+#: front/src/components/manage/library/FilesTable.vue:6
+#: front/src/components/manage/library/RequestsTable.vue:6
+#: front/src/components/manage/users/InvitationsTable.vue:6
+#: front/src/components/manage/users/UsersTable.vue:6
+#: front/src/views/federation/LibraryList.vue:15
+msgid "Search"
+msgstr "Rechercher"
+
+#: front/src/components/library/radios/Builder.vue:6
+msgid "Builder"
+msgstr "Éditeur"
+
+#: front/src/components/library/radios/Builder.vue:12
+msgid "Radio name"
+msgstr "Nom de la radio"
+
+#: front/src/components/library/radios/Builder.vue:17
+msgid "Display publicly"
+msgstr "Afficher publiquement"
+
+#: front/src/components/library/radios/Builder.vue:27
+msgid "Add filters to customize your radio"
+msgstr "Ajouter des filtres pour personnaliser votre radio"
+
+#: front/src/components/library/radios/Builder.vue:37
+msgid "Add filter"
+msgstr "Ajouter des filtres"
+
+#: front/src/components/library/radios/Builder.vue:47
+msgid "Filter name"
+msgstr "Nom du filtre"
+
+#: front/src/components/library/radios/Builder.vue:49
+msgid "Config"
+msgstr "Configuration"
+
+#: front/src/components/library/radios/Builder.vue:51
+#: front/src/components/common/ActionTable.vue:9
+msgid "Actions"
+msgstr "Actions"
+
+#: front/src/components/library/Library.vue:5
+msgid "Browse"
+msgstr "Parcourir"
+
+#: front/src/components/library/Library.vue:11
+msgid "Radios"
+msgstr "Radios"
+
+#: front/src/components/library/Library.vue:21
+msgid "Import batches"
+msgstr "Lots d'imports"
+
+#: front/src/components/library/Artists.vue:5
+msgid "Browsing artists"
+msgstr "Parcourir les artistes"
+
+#: front/src/components/library/Artists.vue:16 src/views/playlists/List.vue:18
+msgid "Ordering"
+msgstr "Ordre"
+
+#: front/src/components/library/Artists.vue:24 src/views/playlists/List.vue:26
+msgid "Ordering direction"
+msgstr "Direction"
+
+#: front/src/components/library/Artist.vue:45
+msgid "Albums by this artist"
+msgstr "Albums de cet·te artiste"
+
+#: front/src/components/library/import/FileUpload.vue:5
+msgid "Ensure your music files are properly tagged before uploading them."
+msgstr ""
+"Vérifier que vos fichiers musicaux sont correctement taggués avant de les "
+"envoyer."
+
+#: front/src/components/library/import/FileUpload.vue:6
+msgid "We recommend using Picard for that purpose."
+msgstr "Nous recommandons d'utiliser le logiciel Picard pour cela."
+
+#: front/src/components/library/import/FileUpload.vue:37
+msgid "Once all your files are uploaded, simply click the following button to check the import status."
+msgstr ""
+"Une fois que tous vos fichiers sont chargés, cliquez sur le bouton suivant "
+"pour vérifier le statut d'import."
+
+#: front/src/components/library/import/FileUpload.vue:44
+msgid "File name"
+msgstr "Nom du fichier"
+
+#: front/src/components/library/import/FileUpload.vue:46
+#: front/src/components/library/import/BatchDetail.vue:67
+#: front/src/components/library/import/BatchList.vue:12
+#: front/src/components/library/import/BatchList.vue:38
+#: front/src/components/federation/LibraryFollowTable.vue:24
+#: front/src/components/federation/LibraryTrackTable.vue:32
+#: front/src/components/manage/library/RequestsTable.vue:25
+#: front/src/components/manage/users/UsersTable.vue:44
+msgid "Status"
+msgstr "Statut"
+
+#: front/src/components/library/import/FileUpload.vue:57
+#: front/src/components/library/import/BatchDetail.vue:72
+#: front/src/components/library/import/BatchList.vue:17
+msgid "Success"
+msgstr "Succès"
+
+#: front/src/components/library/import/FileUpload.vue:60
+#: front/src/components/library/import/BatchDetail.vue:34
+#: front/src/components/library/import/BatchDetail.vue:70
+#: front/src/components/library/import/BatchList.vue:15
+#: front/src/components/federation/LibraryFollowTable.vue:47
+#: front/src/components/manage/library/RequestsTable.vue:28
+#: front/src/components/manage/library/RequestsTable.vue:64
+msgid "Pending"
+msgstr "En attente"
+
+#: front/src/components/library/import/ArtistImport.vue:8
+msgid "Filter album types"
+msgstr "Filtrer le type d'album"
+
+#: front/src/components/library/import/TrackImport.vue:18
+#: front/src/components/library/import/BatchDetail.vue:83
+msgid "Source"
+msgstr "Source"
+
+#: front/src/components/library/import/TrackImport.vue:43
+msgid "Search query"
+msgstr "Recherche"
+
+#: front/src/components/library/import/TrackImport.vue:45
+msgid "Imported URL"
+msgstr "URL importée"
+
+#: front/src/components/library/import/BatchDetail.vue:11
+msgid "Import batch"
+msgstr "Lot d'import"
+
+#: front/src/components/library/import/BatchDetail.vue:19
+#: front/src/components/library/import/BatchList.vue:36
+msgid "Launch date"
+msgstr "Date de lancement"
+
+#: front/src/components/library/import/BatchDetail.vue:38
+msgid "Skipped"
+msgstr "Ignoré"
+
+#: front/src/components/library/import/BatchDetail.vue:42
+msgid "Errored"
+msgstr "En erreur"
+
+#: front/src/components/library/import/BatchDetail.vue:55
+msgid "Finished"
+msgstr "Terminé"
+
+#: front/src/components/library/import/BatchDetail.vue:81
+msgid "Job ID"
+msgstr "ID de la tâche"
+
+#: front/src/components/library/import/Main.vue:7
+msgid "Import source"
+msgstr "Source de l'import"
+
+#: front/src/components/library/import/Main.vue:13
+msgid "Metadata"
+msgstr "Metadonnées"
+
+#: front/src/components/library/import/Main.vue:19
+msgid "Music"
+msgstr "Musique"
+
+#: front/src/components/library/import/Main.vue:30
+msgid "Next step"
+msgstr "Prochaine étape"
+
+#: front/src/components/library/import/Main.vue:66
+msgid "External source. Supported backends"
+msgstr "Source externe. Services supportés"
+
+#: front/src/components/library/import/Main.vue:86
+msgid "Search an entity you want to import:"
+msgstr "Rechercher une ressource que vous voulez importer :"
+
+#: front/src/components/library/import/Main.vue:94
+msgid "Or"
+msgstr "Ou"
+
+#: front/src/components/library/import/Main.vue:104
+msgid "You will import:"
+msgstr "Vous allez importer :"
+
+#: front/src/components/library/import/Main.vue:150
+msgid "This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."
+msgstr ""
+"Cet import sera associé à la requête ci dessous. Une fois l'import terminé, "
+"la requête sera marquée comme complétée."
+
+#: front/src/components/library/import/BatchList.vue:23
+#: front/src/components/federation/LibraryTrackTable.vue:12
+msgid "Any"
+msgstr "Tous"
+
+#: front/src/components/library/import/BatchList.vue:25
+msgid "API"
+msgstr "API"
+
+#: front/src/components/library/import/BatchList.vue:40
+msgid "Submitted by"
+msgstr "Proposé par"
+
+#: front/src/components/library/Track.vue:24
+msgid "Artist page"
+msgstr "Page de l'artiste"
+
+#: front/src/components/library/Track.vue:31
+msgid "Play"
+msgstr "Jouer"
+
+#: front/src/components/library/Track.vue:41
+msgid "Search on Wikipedia"
+msgstr "Rechercher sur Wikipédia"
+
+#: front/src/components/library/Track.vue:45
+msgid "View on MusicBrainz"
+msgstr "Voir sur MusicBrainz"
+
+#: front/src/components/library/Track.vue:49
+#: src/components/audio/track/Table.vue:24
+msgid "Download"
+msgstr "Télécharger"
+
+#: front/src/components/library/Track.vue:59
+#: front/src/components/manage/library/FilesTable.vue:44
+msgid "Duration"
+msgstr "Durée"
+
+#: front/src/components/library/Track.vue:65
+#: src/components/library/Track.vue:76
+#: front/src/components/manage/library/FilesTable.vue:64
+#: front/src/components/manage/library/FilesTable.vue:76
+#: front/src/components/manage/library/FilesTable.vue:82
+#: front/src/components/manage/library/RequestsTable.vue:72
+#: front/src/components/manage/library/RequestsTable.vue:83
+#: front/src/components/manage/users/UsersTable.vue:62
+msgid "N/A"
+msgstr "ND"
+
+#: front/src/components/library/Track.vue:70
+msgid "Size"
+msgstr "Taille"
+
+#: front/src/components/library/Track.vue:81
+msgid "Bitrate"
+msgstr "Bitrate"
+
+#: front/src/components/library/Home.vue:10
+msgid "Latest artists"
+msgstr "Derniers artistes"
+
+#: front/src/components/library/Home.vue:27
+msgid "Music requests"
+msgstr "Requête musicale"
+
+#: front/src/components/requests/Card.vue:26
+#: front/src/components/manage/library/RequestsTable.vue:89
+msgid "Create import"
+msgstr "Créer un import"
+
+#: front/src/components/requests/Form.vue:4
+msgid "Something's missing in the library? Let us know what you would like to listen!"
+msgstr ""
+"Quelque chose manque dans la bibliothèque ? Dîtes nous ce que vous voulez "
+"écouter !"
+
+#: front/src/components/requests/Form.vue:11
+msgid "Leave this field empty if you're requesting the whole discography."
+msgstr "Laisser ce champ vide si vous voulez suggérer toute la discographie."
+
+#: front/src/components/requests/Form.vue:15
+#: front/src/components/manage/library/RequestsTable.vue:52
+msgid "Comment"
+msgstr "Commentaire"
+
+#: front/src/components/requests/Form.vue:22
+msgid "We've received your request, you'll get some groove soon ;)"
+msgstr ""
+"Nous avons bien reçu votre requête, vous aurez bientôt de nos nouvelles ;)"
+
+#: front/src/components/requests/Form.vue:27
+msgid "Pending requests"
+msgstr "Requêtes en attente"
+
+#: front/src/components/federation/LibraryFollowTable.vue:12
+msgid "Pending approval"
+msgstr "En attente de validation"
+
+#: front/src/components/federation/LibraryFollowTable.vue:22
+msgid "Actor"
+msgstr "Acteur·rice"
+
+#: front/src/components/federation/LibraryFollowTable.vue:39
+msgid "Approved"
+msgstr "Approuvé"
+
+#: front/src/components/federation/LibraryFollowTable.vue:43
+msgid "Refused"
+msgstr "Refusé"
+
+#: front/src/components/federation/LibraryFollowTable.vue:64
+msgid "Deny"
+msgstr "Refuser"
+
+#: front/src/components/federation/LibraryTrackTable.vue:14
+#: front/src/components/federation/LibraryTrackTable.vue:53
+msgid "Not imported"
+msgstr "Non importé"
+
+#: front/src/components/federation/LibraryTrackTable.vue:36
+msgid "Published date"
+msgstr "Date de sortie"
+
+#: front/src/components/federation/LibraryTrackTable.vue:51
+msgid "In library"
+msgstr "Dans la bibliothèque"
+
+#: front/src/components/federation/LibraryForm.vue:4
+msgid "Federate with a new instance"
+msgstr "Fédérer avec une nouvelle instance"
+
+#: front/src/components/federation/LibraryForm.vue:11
+msgid "Error while scanning library"
+msgstr "Erreur lors du scan de la bibliothèque"
+
+#: front/src/components/federation/LibraryForm.vue:32
+msgid "Launch scan"
+msgstr "Démarrer le scan"
+
+#: front/src/components/audio/album/Card.vue:44
+#: front/src/components/audio/artist/Card.vue:34
+msgid "Collapse"
+msgstr "Réduire"
+
+#: front/src/components/audio/album/Card.vue:51
+msgid "Play all"
+msgstr "Tout lire"
+
+#: front/src/components/audio/PlayButton.vue:14
+msgid "Add to queue"
+msgstr "Ajouter à la queue"
+
+#: front/src/components/audio/PlayButton.vue:15
+msgid "Play next"
+msgstr "Lire ensuite"
+
+#: front/src/components/audio/PlayButton.vue:16
+msgid "Play now"
+msgstr "Lire maintenant"
+
+#: front/src/components/audio/PlayButton.vue:145
+msgid "%{ count } track was added to your queue"
+msgid_plural "%{ count } tracks were added to your queue"
+msgstr[0] "%{ count } piste a été ajouté à votre queue"
+msgstr[1] "%{ count } pistes ont été ajoutées à votre queue"
+
+#: front/src/components/audio/track/Table.vue:7
+#: front/src/components/manage/library/FilesTable.vue:38
+msgid "Title"
+msgstr "Titre"
+
+#: front/src/components/audio/track/Table.vue:30
+msgid "There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as cURL to easily download a list of tracks."
+msgstr ""
+"Il n'est pas possible pour le moment de télécharger un ensemble de pistes "
+"sous forme d'archive. Cependant, vous pouvez utiliser un outil en ligne de "
+"commande tel que cURL pour télécharger facilement une liste de pistes."
+
+#: front/src/components/audio/Search.vue:3
+msgid "Search for some music"
+msgstr "Rechercher de la musique"
+
+#: front/src/components/PageNotFound.vue:8
+msgid "Page not found!"
+msgstr "Page non trouvée !"
+
+#: front/src/components/PageNotFound.vue:11
+msgid "We're sorry, the page you asked for does not exist:"
+msgstr "Désolés, la page demandée n’existe pas :"
+
+#: front/src/components/PageNotFound.vue:15
+msgid "Go to home page"
+msgstr "Retourner à la page d'accueil"
+
+#: front/src/components/About.vue:16
+msgid "Unfortunately, owners of this instance did not yet take the time to complete this page."
+msgstr ""
+"Malheureusement, les gestionnaires de cette instance n'ont pas encore pris "
+"le temps de compléter cette page."
+
+#: front/src/components/Sidebar.vue:21
+msgid "Queue"
+msgstr "Queue"
+
+#: front/src/components/Sidebar.vue:23
+msgid "(empty)"
+msgstr "(vide)"
+
+#: front/src/components/Sidebar.vue:43
+msgid "Logout"
+msgstr "Déconnexion"
+
+#: front/src/components/Sidebar.vue:44
+msgid "Login"
+msgstr "Connexion"
+
+#: front/src/components/Sidebar.vue:64
+msgid "Administration"
+msgstr "Administration"
+
+#: front/src/components/Sidebar.vue:124
+msgid "Yes"
+msgstr "Oui"
+
+#: front/src/components/Sidebar.vue:158
+msgid "New tracks will be appended here automatically."
+msgstr "Les nouvelles pistes seront ajoutées ici automatiquement."
+
+#: front/src/components/manage/library/FilesTable.vue:42
+msgid "Type"
+msgstr "Type"
+
+#: front/src/components/manage/library/RequestsTable.vue:30
+#: front/src/components/manage/library/RequestsTable.vue:62
+msgid "Imported"
+msgstr "Importé"
+
+#: front/src/components/manage/library/RequestsTable.vue:48
+msgid "User"
+msgstr "Utilisateur·rice"
+
+#: front/src/components/manage/library/RequestsTable.vue:54
+msgid "Import date"
+msgstr "Date d'import"
+
+#: front/src/components/manage/users/InvitationForm.vue:5
+msgid "Error while creating invitation"
+msgstr "Erreur lors de la création de l'invitation"
+
+#: front/src/components/manage/users/InvitationForm.vue:12
+msgid "Invitation code"
+msgstr "Code d'invitation"
+
+#: front/src/components/manage/users/InvitationForm.vue:27
+#: front/src/components/manage/users/InvitationsTable.vue:43
+msgid "Code"
+msgstr "Code"
+
+#: front/src/components/manage/users/InvitationForm.vue:38
+msgid "Clear"
+msgstr "Effacer"
+
+#: front/src/components/manage/users/InvitationsTable.vue:20
+msgid "All"
+msgstr "Tout"
+
+#: front/src/components/manage/users/InvitationsTable.vue:22
+msgid "Expired/used"
+msgstr "Expirée/utilisée"
+
+#: front/src/components/manage/users/InvitationsTable.vue:39
+msgid "Owner"
+msgstr "Propriétaire"
+
+#: front/src/components/manage/users/InvitationsTable.vue:41
+msgid "Creation date"
+msgstr "Date de création"
+
+#: front/src/components/manage/users/InvitationsTable.vue:50
+msgid "Used"
+msgstr "Utilisé"
+
+#: front/src/components/manage/users/InvitationsTable.vue:52
+msgid "Not used"
+msgstr "Pas utilisé"
+
+#: front/src/components/manage/users/UsersTable.vue:40
+msgid "Account status"
+msgstr "Statut du compte"
+
+#: front/src/components/manage/users/UsersTable.vue:42
+msgid "Last activity"
+msgstr "Dernière activité"
+
+#: front/src/components/manage/users/UsersTable.vue:54
+msgid "Active"
+msgstr "Actif"
+
+#: front/src/components/common/ActionTable.vue:22
+msgid "Go"
+msgstr "Aller"
+
+#: front/src/components/common/ActionTable.vue:40
+msgid "Launch"
+msgstr "Démarrer"
+
+#: front/src/components/Home.vue:6
+msgid "Welcome on Funkwhale"
+msgstr "Bienvenue sur Funkwhale"
+
+#: front/src/components/Home.vue:14
+msgid "Get me to the library"
+msgstr "Amenez moi à la bibliothèque"
+
+#: front/src/components/Home.vue:25
+msgid "Why funkwhale?"
+msgstr "Pourquoi Funkwhale ?"
+
+#: front/src/components/Home.vue:40
+msgid "Funkwhale is designed to make it easy to listen to music you like, or to discover new artists."
+msgstr ""
+"Funkwhale est conçu pour faciliter l'écoute des musiques que vous aimez et "
+"découvrir de nouveaux artistes."
+
+#: front/src/components/Home.vue:45
+msgid "Click once, listen for hours using built-in radios"
+msgstr ""
+"Écoutez de la musique pendant des heures, en un clic, grâce aux radios "
+"intégrées."
+
+#: front/src/components/Home.vue:57
+msgid "Playlists? We got them"
+msgstr "Les playlists ? Elles sont là !"
+
+#: front/src/components/Home.vue:67
+msgid "Funkwhale takes care of handling your music"
+msgstr "Funkwhale prend soin de votre musique"
+
+#: front/src/components/Home.vue:72
+msgid "Import music from various platforms, such as YouTube or SoundCloud"
+msgstr ""
+"Importez de la musique de différentes plate-formes, comme YouTube ou "
+"Soundcloud"
+
+#: front/src/components/Home.vue:89
+msgid "Covers, lyrics, our goal is to have them all ;)"
+msgstr "Pochettes d'albums, paroles, notre but est de tout implémenter ;)"
+
+#: front/src/components/Home.vue:99
+msgid "Funkwhale is dead simple to use."
+msgstr "Funkwhale est très simple à utiliser."
+
+#: front/src/components/Home.vue:104
+msgid "No add-ons, no plugins : you only need a web library"
+msgstr ""
+
+#: front/src/components/Home.vue:118
+msgid "Your music, your way"
+msgstr "Votre musique, à votre façon"
+
+#: front/src/components/Home.vue:131
+msgid "We do not track you or bother you with ads"
+msgstr "Nous ne vous pistons pas et ne vous exposons pas à des publicités"
+
+#: front/src/views/instance/Timeline.vue:5
+msgid "Loading timeline..."
+msgstr "Chargement de la timeline ..."
+
+#: front/src/views/instance/Timeline.vue:8
+msgid "Recent activity on this instance"
+msgstr "Activité récente sur cette instance"
+
+#: front/src/views/playlists/Detail.vue:30
+msgid "Edit..."
+msgstr "Éditer..."
+
+#: front/src/views/playlists/Detail.vue:38
+msgid "Delete playlist"
+msgstr "Supprimer la playlist"
+
+#: front/src/views/playlists/List.vue:4
+msgid "Browsing playlists"
+msgstr "Parcourir les playlists"
+
+#: front/src/views/playlists/List.vue:9
+msgid "Manage your playlists"
+msgstr "Gérer vos playlists"
+
+#: front/src/views/playlists/List.vue:29
+msgid "Descending"
+msgstr "Descendant"
+
+#: front/src/views/auth/PasswordReset.vue:5
+msgid "Reset your password"
+msgstr "Réinitialiser votre mot de passe"
+
+#: front/src/views/auth/PasswordReset.vue:15
+msgid "Account's email"
+msgstr "Email du compte"
+
+#: front/src/views/auth/PasswordReset.vue:25
+#: front/src/views/auth/PasswordResetConfirm.vue:19
+#: front/src/views/auth/EmailConfirm.vue:18
+msgid "Back to login"
+msgstr "Retour à la page de connexion"
+
+#: front/src/views/auth/PasswordResetConfirm.vue:5
+msgid "Change your password"
+msgstr "Changer votre mot de passe"
+
+#: front/src/views/auth/PasswordResetConfirm.vue:29
+msgid "Password updated successfully"
+msgstr "Mot de passe modifié avec succès"
+
+#: front/src/views/auth/PasswordResetConfirm.vue:32
+msgid "Proceed to login"
+msgstr "Poursuivre vers la page de connexion"
+
+#: front/src/views/auth/EmailConfirm.vue:5
+msgid "Confirm your email"
+msgstr "Confirmer votre email"
+
+#: front/src/views/auth/EmailConfirm.vue:25
+msgid "Your email address was confirmed, you can now use the service without limitations."
+msgstr ""
+"Votre adresse email a été confirmée, vous pouvez maintenant utiliser le "
+"service sans limitations."
+
+#: front/src/views/admin/library/RequestsList.vue:4
+msgid "Import requests"
+msgstr "Requêtes d'import"
+
+#: front/src/views/admin/library/FilesList.vue:4
+msgid "Library files"
+msgstr "Fichiers de la bibliothèque"
+
+#: front/src/views/admin/library/Base.vue:6
+msgid "Files"
+msgstr "Fichiers"
+
+#: front/src/views/admin/users/UsersDetail.vue:30
+msgid "Email address"
+msgstr "Adresse email"
+
+#: front/src/views/admin/users/UsersDetail.vue:55
+msgid "Account active"
+msgstr "Compte actif"
+
+#: front/src/views/admin/users/UsersDetail.vue:85
+msgid "Refresh"
+msgstr "Rafraîchir"
+
+#: front/src/views/admin/users/InvitationsList.vue:4
+msgid "Invitations"
+msgstr "Invitations"
+
+#: front/src/views/federation/LibraryFollowersList.vue:4
+msgid "Browsing followers"
+msgstr "Parcourir les abonnés"
+
+#: front/src/views/federation/LibraryTrackList.vue:4
+msgid "Browsing federated tracks"
+msgstr "Parcourir les pistes fédérées"
+
+#: front/src/views/federation/LibraryList.vue:4
+msgid "Browsing libraries"
+msgstr "Parcourir les bibliothèques"
+
+#: front/src/views/federation/LibraryList.vue:9
+msgid "Add a new library"
+msgstr "Ajouter une nouvelle bibliothèque"
+
+#: front/src/views/federation/LibraryDetail.vue:57
+msgid "Auto importing"
+msgstr "Import automatique"
+
+#: front/src/views/federation/LibraryDetail.vue:85
+msgid "Library size"
+msgstr "Taille de la bibliothèque"
+
+#: front/src/views/federation/Base.vue:6
+msgid "Libraries"
+msgstr "Bibliothèques"
+
+#: front/src/views/federation/Base.vue:14
+msgid "Followers"
+msgstr "Abonnés"
diff --git a/front/package.json b/front/package.json
index 3dec9c2571a25cf4e8592bb5da7a28e7699d636a..9837479badac5b9a1897c10ad2d375cf71d5e8a0 100644
--- a/front/package.json
+++ b/front/package.json
@@ -5,11 +5,11 @@
   "author": "Eliot Berriot <contact@eliotberriot.com>",
   "private": true,
   "scripts": {
-    "dev": "node build/dev-server.js",
-    "start": "node build/dev-server.js",
+    "dev": "scripts/i18n-compile.sh && node build/dev-server.js",
+    "start": "scripts/i18n-compile.sh && node build/dev-server.js",
     "build": "node build/build.js",
-    "i18n-extract": "find src/ -name '*.vue' | xargs vendor/vue-i18n-xgettext/index.js > ../po/en.po",
-    "i18n-compile": "node build/i18n.js",
+    "i18n-extract": "scripts/i18n-extract.sh",
+    "i18n-compile": "scripts/i18n-compile.sh",
     "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
     "unit-watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js",
     "e2e": "node test/e2e/runner.js",
@@ -21,9 +21,6 @@
     "axios": "^0.17.1",
     "dateformat": "^2.0.0",
     "django-channels": "^1.1.6",
-    "i18next": "^11.1.1",
-    "i18next-conv": "^6.0.0",
-    "i18next-fetch-backend": "^0.1.0",
     "js-logger": "^1.3.0",
     "jwt-decode": "^2.2.0",
     "lodash": "^4.17.4",
@@ -34,6 +31,7 @@
     "semantic-ui-css": "^2.2.10",
     "showdown": "^1.8.6",
     "vue": "^2.5.16",
+    "vue-gettext": "^2.0.31",
     "vue-lazyload": "^1.1.4",
     "vue-masonry": "^0.10.16",
     "vue-router": "^2.3.1",
@@ -61,6 +59,7 @@
     "cross-env": "^4.0.0",
     "cross-spawn": "^5.0.1",
     "css-loader": "^0.28.0",
+    "easygettext": "^2.5.0",
     "es6-promise": "^4.2.2",
     "eslint": "^3.19.0",
     "eslint-config-standard": "^6.2.1",
@@ -104,7 +103,6 @@
     "sinon-chai": "^2.8.0",
     "sinon-stub-promise": "^4.0.0",
     "url-loader": "^0.5.8",
-    "vue-i18n-xgettext": "^0.0.4",
     "vue-loader": "^12.1.0",
     "vue-style-loader": "^3.0.1",
     "vue-template-compiler": "^2.3.3",
diff --git a/front/scripts/i18n-compile.sh b/front/scripts/i18n-compile.sh
new file mode 100755
index 0000000000000000000000000000000000000000..211f8ee8c06264db2dbf0a935be0528175ae1e47
--- /dev/null
+++ b/front/scripts/i18n-compile.sh
@@ -0,0 +1,3 @@
+#!/bin/bash -eux
+locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
+find locales -name '*.po' | xargs $(yarn bin gettext-extract)/gettext-compile --output src/translations.json
diff --git a/front/scripts/i18n-extract.sh b/front/scripts/i18n-extract.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d4fea4b62fbc02c647d1d9cfbd0e8adcb33c4061
--- /dev/null
+++ b/front/scripts/i18n-extract.sh
@@ -0,0 +1,28 @@
+#!/bin/bash -eux
+locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
+locales_dir="locales"
+sources=$(find src -name '*.vue' -o -name '*.html' 2> /dev/null)
+js_sources=$(find src -name '*.vue' -o -name '*.js')
+touch $locales_dir/app.pot
+
+# Create a main .pot template, then generate .po files for each available language.
+# Extract gettext strings from templates files and create a POT dictionary template.
+$(yarn bin gettext-extract)/gettext-extract --attribute v-translate --quiet --output $locales_dir/app.pot $sources
+xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
+    --from-code=utf-8 --join-existing --no-wrap \
+    --package-name=$(node -e "console.log(require('./package.json').name);") \
+    --package-version=$(node -e "console.log(require('./package.json').version);") \
+    --output $locales_dir/app.pot $js_sources
+
+# Fix broken files path/lines in pot
+sed -e 's|#: src/|#: front/src/|' -i $locales_dir/app.pot
+
+# Generate .po files for each available language.
+echo $locales
+for lang in $locales; do \
+    po_file=$locales_dir/$lang/LC_MESSAGES/app.po; \
+    echo "msgmerge --update $po_file "; \
+    mkdir -p $(dirname $po_file); \
+    [ -f $po_file ] && msgmerge --lang=$lang --update $po_file $locales_dir/app.pot || msginit --no-translator --locale=$lang --input=$locales_dir/app.pot --output-file=$po_file; \
+    msgattrib --no-wrap --no-obsolete -o $po_file $po_file; \
+done;
diff --git a/front/src/App.vue b/front/src/App.vue
index 56dbe0aad41f7af3d382202832ab6828bb431837..6efd3e582184af778b4a18459cd1734862437b1d 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -2,14 +2,14 @@
   <div id="app">
     <div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
       <div class="ui padded segment">
-        <h1 class="ui header">{{ $t('Choose your instance') }}</h1>
+        <h1 class="ui header">{{ $gettext('Choose your instance') }}</h1>
         <form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)">
-          <p>{{ $t('You need to select an instance in order to continue') }}</p>
+          <p>{{ $gettext('You need to select an instance in order to continue') }}</p>
           <div class="ui action input">
             <input type="text" v-model="instanceUrl">
-            <button type="submit" class="ui button">{{ $t('Submit') }}</button>
+            <button type="submit" class="ui button">{{ $gettext('Submit') }}</button>
           </div>
-          <p>{{ $t('Suggested choices') }}</p>
+          <p>{{ $gettext('Suggested choices') }}</p>
           <div class="ui bulleted list">
             <div class="ui item" v-for="url in suggestedInstances">
               <a @click="instanceUrl = url">{{ url }}</a>
@@ -27,20 +27,20 @@
         <div class="ui container">
           <div class="ui stackable equal height stackable grid">
             <div class="three wide column">
-              <i18next tag="h4" class="ui header" path="Links"></i18next>
+              <h4 v-translate class="ui header">Links</h4>
               <div class="ui link list">
                 <router-link class="item" to="/about">
-                  <i18next path="About this instance" />
+                  {{ $gettext('About this instance') }}
                 </router-link>
-                <a href="https://funkwhale.audio" class="item" target="_blank">{{ $t('Official website') }}</a>
-                <a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $t('Documentation') }}</a>
+                <a href="https://funkwhale.audio" class="item" target="_blank">{{ $gettext('Official website') }}</a>
+                <a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $gettext('Documentation') }}</a>
                 <a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank">
-                  <template v-if="version">{{ $t('Source code ({% version %})', {version: version}) }}</template>
-                  <template v-else>{{ $t('Source code') }}</template>
+                  <translate :translate-params="{version: version}" v-if="version">Source code (%{version})</translate>
+                  <translate v-else>Source code</translate>
                 </a>
-                <a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $t('Issue tracker') }}</a>
+                <a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $gettext('Issue tracker') }}</a>
                 <a @click="switchInstance" class="item" >
-                  {{ $t('Use another instance') }}
+                  {{ $gettext('Use another instance') }}
                   <template v-if="$store.state.instance.instanceUrl !== '/'">
                     <br>
                     ({{ $store.state.instance.instanceUrl }})
@@ -49,14 +49,26 @@
               </div>
             </div>
             <div class="ten wide column">
-              <i18next tag="h4" class="ui header" path="About funkwhale" />
+              <h4 v-translate class="ui header">About Funkwhale</h4>
               <p>
-                <i18next path="Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"/>
+                <translate>Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!</translate>
               </p>
               <p>
-                <i18next path="The funkwhale logo was kindly designed and provided by Francis Gading."/>
+                <translate>The funkwhale logo was kindly designed and provided by Francis Gading.</translate>
               </p>
             </div>
+            <div class="three wide column">
+              <h4 v-translate class="ui header">Options</h4>
+              <div class="ui form">
+                <div class="ui field">
+                  <label>{{ $gettext('Change language') }}</label>
+                  <select class="ui dropdown" v-model="$language.current">
+                    <option v-for="(language, key) in $language.available" :value="key">{{ language }}</option>
+                  </select>
+                </div>
+              </div>
+            </div>
+
           </div>
         </div>
       </div>
@@ -115,7 +127,7 @@ export default {
       })
     },
     switchInstance () {
-      let confirm = window.confirm(this.$t('This will erase your local data and disconnect you, do you want to continue?'))
+      let confirm = window.confirm(this.$gettext('This will erase your local data and disconnect you, do you want to continue?'))
       if (confirm) {
         this.$store.commit('instance/instanceUrl', null)
       }
@@ -144,6 +156,9 @@ export default {
     '$store.state.instance.instanceUrl' () {
       this.$store.dispatch('instance/fetchSettings')
       this.fetchNodeInfo()
+    },
+    '$language.current' (newValue) {
+      this.$store.commit('ui/currentLanguage', newValue)
     }
   }
 }
diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index 59c8411ac131fe65ec72fa6ed1e4769eed5b361a..5ebed8102e848b3dc621b946319a9705a1f2039a 100644
--- a/front/src/components/About.vue
+++ b/front/src/components/About.vue
@@ -3,21 +3,23 @@
     <div class="ui vertical center aligned stripe segment">
       <div class="ui text container">
         <h1 class="ui huge header">
-            <template v-if="instance.name.value">{{ $t('About {%instance%}', { instance: instance.name.value }) }}</template>
-            <template v-else="instance.name.value">{{ $t('About this instance') }}</template>
+            <template v-if="instance.name.value" :template-params="{instance: instance.name}">
+             About %{ instance }
+            </template>
+            <template v-else="instance.name.value">{{ $gettext('About this instance') }}</template>
         </h1>
         <stats></stats>
       </div>
     </div>
     <div class="ui vertical stripe segment">
       <p v-if="!instance.short_description.value && !instance.long_description.value">
-        {{ $t('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
+        {{ $gettext('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
       </p>
       <router-link
         class="ui button"
         v-if="$store.state.auth.availablePermissions['settings']"
         :to="{path: '/manage/settings', hash: 'instance'}">
-        <i class="pencil icon"></i>{{ $t('Edit instance info') }}
+        <i class="pencil icon"></i>{{ $gettext('Edit instance info') }}
       </router-link>
       <div
         v-if="instance.short_description.value"
diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue
index 2af1bfc35b1b65a60a686268a31594c2604ada9a..dcea5c72bb3cd4d0e00d8f5c836533aefdab4e5c 100644
--- a/front/src/components/Home.vue
+++ b/front/src/components/Home.vue
@@ -3,15 +3,15 @@
     <div class="ui vertical center aligned stripe segment">
       <div class="ui text container">
         <h1 class="ui huge header">
-          {{ $t('Welcome on Funkwhale') }}
+          {{ $gettext('Welcome on Funkwhale') }}
         </h1>
-        <p>{{ $t('We think listening to music should be simple.') }}</p>
+        <p>{{ $gettext('We think listening to music should be simple.') }}</p>
         <router-link class="ui icon button" to="/about">
           <i class="info icon"></i>
-          {{ $t('Learn more about this instance') }}
+          {{ $gettext('Learn more about this instance') }}
         </router-link>
         <router-link class="ui icon teal button" to="/library">
-          {{ $t('Get me to the library') }}
+          {{ $gettext('Get me to the library') }}
           <i class="right arrow icon"></i>
         </router-link>
       </div>
@@ -22,9 +22,9 @@
           <div class="row">
             <div class="eight wide left floated column">
               <h2 class="ui header">
-                {{ $t('Why funkwhale?') }}
+                {{ $gettext('Why funkwhale?') }}
               </h2>
-              <p>{{ $t('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
+              <p>{{ $gettext('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
             </div>
             <div class="four wide left floated column">
               <img class="ui medium image" src="../assets/logo/logo.png" />
@@ -35,26 +35,26 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $t('Unlimited music') }}
+          {{ $gettext('Unlimited music') }}
         </h2>
-        <p>{{ $t('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
+        <p>{{ $gettext('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
         <div class="ui list">
           <div class="item">
             <i class="sound icon"></i>
             <div class="content">
-              {{ $t('Click once, listen for hours using built-in radios') }}
+              {{ $gettext('Click once, listen for hours using built-in radios') }}
             </div>
           </div>
           <div class="item">
             <i class="heart icon"></i>
             <div class="content">
-              {{ $t('Keep a track of your favorite songs') }}
+              {{ $gettext('Keep a track of your favorite songs') }}
             </div>
           </div>
           <div class="item">
             <i class="list icon"></i>
             <div class="content">
-              {{ $t('Playlists? We got them') }}
+              {{ $gettext('Playlists? We got them') }}
             </div>
           </div>
         </div>
@@ -62,28 +62,31 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $t('Clean library') }}
+          {{ $gettext('Clean library') }}
         </h2>
-        <p>{{ $t('Funkwhale takes care of handling your music') }}.</p>
+        <p>{{ $gettext('Funkwhale takes care of handling your music') }}.</p>
         <div class="ui list">
           <div class="item">
             <i class="download icon"></i>
             <div class="content">
-              {{ $t('Import music from various platforms, such as YouTube or SoundCloud') }}
+              {{ $gettext('Import music from various platforms, such as YouTube or SoundCloud') }}
             </div>
           </div>
           <div class="item">
             <i class="tag icon"></i>
             <div class="content">
-              <i18next path="Get quality metadata about your music thanks to {%0%}">
-                <a href="https://musicbrainz.org" target="_blank">{{ $t('MusicBrainz') }}</a>
-              </i18next>
+              <template v-translate>
+                Get quality metadata about your music thanks to
+                <a href="https://musicbrainz.org" target="_blank">
+                  MusicBrainz
+                </a>
+              </template>
             </div>
           </div>
           <div class="item">
             <i class="plus icon"></i>
             <div class="content">
-              {{ $t('Covers, lyrics, our goal is to have them all ;)') }}
+              {{ $gettext('Covers, lyrics, our goal is to have them all ;)') }}
             </div>
           </div>
         </div>
@@ -91,20 +94,20 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $t('Easy to use') }}
+          {{ $gettext('Easy to use') }}
         </h2>
-        <p>{{ $t('Funkwhale is dead simple to use.') }}</p>
+        <p>{{ $gettext('Funkwhale is dead simple to use.') }}</p>
         <div class="ui list">
           <div class="item">
             <i class="book icon"></i>
             <div class="content">
-              {{ $t('No add-ons, no plugins : you only need a web library') }}
+              {{ $gettext('No add-ons, no plugins : you only need a web library') }}
             </div>
           </div>
           <div class="item">
             <i class="wizard icon"></i>
             <div class="content">
-              {{ $t('Access your music from a clean interface that focus on what really matters') }}
+              {{ $gettext('Access your music from a clean interface that focus on what really matters') }}
             </div>
           </div>
         </div>
@@ -112,26 +115,26 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $t('Your music, your way') }}
+          {{ $gettext('Your music, your way') }}
         </h2>
-        <p>{{ $t('Funkwhale is free and gives you control on your music.') }}</p>
+        <p>{{ $gettext('Funkwhale is free and gives you control on your music.') }}</p>
         <div class="ui list">
           <div class="item">
             <i class="smile icon"></i>
             <div class="content">
-              {{ $t('The plaform is free and open-source, you can install it and modify it without worries') }}
+              {{ $gettext('The plaform is free and open-source, you can install it and modify it without worries') }}
             </div>
           </div>
           <div class="item">
             <i class="protect icon"></i>
             <div class="content">
-              {{ $t('We do not track you or bother you with ads') }}
+              {{ $gettext('We do not track you or bother you with ads') }}
             </div>
           </div>
           <div class="item">
             <i class="users icon"></i>
             <div class="content">
-              {{ $t('You can invite friends and family to your instance so they can enjoy your music') }}
+              {{ $gettext('You can invite friends and family to your instance so they can enjoy your music') }}
             </div>
           </div>
         </div>
diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue
index b4d2250ca061db4b36c729b42dc22901d684cd7d..150abd8c0da21e4b42809cbb05c4cf5b81f4223e 100644
--- a/front/src/components/PageNotFound.vue
+++ b/front/src/components/PageNotFound.vue
@@ -5,13 +5,14 @@
         <h1 class="ui huge header">
           <i class="warning icon"></i>
           <div class="content">
-            <strike>{{ $t('Whale') }}</strike> {{ $t('Page not found!') }}
+            <strike>{{ $gettext('Whale') }}</strike> {{ $gettext('Page not found!') }}
           </div>
         </h1>
-        <p>{{ $t('We\'re sorry, the page you asked for does not exists.') }}</p>
-        <i18next path="Requested URL: {%0%}"><a :href="path">{{ path }}</a></i18next>
+        <p>{{ $gettext('We\'re sorry, the page you asked for does not exist:') }}</p>
+        <a :href="path">{{ path }}</a>
+        <div class="ui hidden divider"></div>
         <router-link class="ui icon button" to="/">
-          {{ $t('Go to home page') }}
+          {{ $gettext('Go to home page') }}
           <i class="right arrow icon"></i>
         </router-link>
       </div>
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 9eec6c0e2721f50ba9209474cabddf028ca4c163..439a1e630b743dc308004211f2199f5b5d42f63b 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -18,13 +18,13 @@
     <div class="ui compact fluid two item inverted menu">
       <a class="active item" @click="selectedTab = 'library'" data-tab="library">Browse</a>
       <a class="item" @click="selectedTab = 'queue'" data-tab="queue">
-        {{ $t('Queue') }}
+        {{ $gettext('Queue') }}&nbsp;
          <template v-if="queue.tracks.length === 0">
-           {{ $t('(empty)') }}
-         </template>
-         <template v-else>
-           {{ $t('({%index%} of {%length%})', { index: queue.currentIndex + 1, length: queue.tracks.length }) }}
+           {{ $gettext('(empty)') }}
          </template>
+         <translate v-else :translate-params="{index: queue.currentIndex + 1, length: queue.tracks.length}">
+          (%{ index } of %{ length })
+         </translate>
       </a>
     </div>
   </div>
@@ -32,40 +32,45 @@
     <div class="ui bottom attached active tab" data-tab="library">
       <div class="ui inverted vertical large fluid menu">
         <div class="item">
-          <div class="header">{{ $t('My account') }}</div>
+          <div class="header">{{ $gettext('My account') }}</div>
           <div class="menu">
-            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}"><i class="user icon"></i>{{ $t('Logged in as {%name%}', { name: $store.state.auth.username }) }}</router-link>
-            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i>{{ $t('Logout') }}</router-link>
-            <router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i>{{ $t('Login') }}</router-link>
+            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}">
+              <i class="user icon"></i>
+              <translate :translate-params="{username: $store.state.auth.username}">
+                Logged in as %{ username }
+              </translate>
+            </router-link>
+            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i>{{ $gettext('Logout') }}</router-link>
+            <router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i>{{ $gettext('Login') }}</router-link>
           </div>
         </div>
         <div class="item">
-          <div class="header">{{ $t('Music') }}</div>
+          <div class="header">{{ $gettext('Music') }}</div>
           <div class="menu">
-            <router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i>{{ $t('Browse library') }}</router-link>
-            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i>{{ $t('Favorites') }}</router-link>
+            <router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i>{{ $gettext('Browse library') }}</router-link>
+            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i>{{ $gettext('Favorites') }}</router-link>
             <a
               @click="$store.commit('playlists/chooseTrack', null)"
               v-if="$store.state.auth.authenticated"
               class="item">
-              <i class="list icon"></i>{{ $t('Playlists') }}
+              <i class="list icon"></i>{{ $gettext('Playlists') }}
             </a>
             <router-link
               v-if="$store.state.auth.authenticated"
-              class="item" :to="{path: '/activity'}"><i class="bell icon"></i>{{ $t('Activity') }}</router-link>
+              class="item" :to="{path: '/activity'}"><i class="bell icon"></i>{{ $gettext('Activity') }}</router-link>
           </div>
         </div>
         <div class="item" v-if="showAdmin">
-          <div class="header">{{ $t('Administration') }}</div>
+          <div class="header">{{ $gettext('Administration') }}</div>
           <div class="menu">
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['library']"
               :to="{name: 'manage.library.files'}">
-              <i class="book icon"></i>{{ $t('Library') }}
+              <i class="book icon"></i>{{ $gettext('Library') }}
               <div
                 :class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
-                :title="$t('Pending import requests')">
+                :title="$gettext('Pending import requests')">
                 {{ $store.state.ui.notifications.importRequests }}</div>
 
             </router-link>
@@ -73,29 +78,29 @@
               class="item"
               v-else-if="$store.state.auth.availablePermissions['upload']"
               to="/library/import/launch">
-              <i class="download icon"></i>{{ $t('Import music') }}
+              <i class="download icon"></i>{{ $gettext('Import music') }}
             </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['federation']"
               :to="{path: '/manage/federation/libraries'}">
-              <i class="sitemap icon"></i>{{ $t('Federation') }}
+              <i class="sitemap icon"></i>{{ $gettext('Federation') }}
               <div
                 :class="['ui', {'teal': $store.state.ui.notifications.federation > 0}, 'label']"
-                :title="$t('Pending follow requests')">
+                :title="$gettext('Pending follow requests')">
                 {{ $store.state.ui.notifications.federation }}</div>
             </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['settings']"
               :to="{path: '/manage/settings'}">
-              <i class="settings icon"></i>{{ $t('Settings') }}
+              <i class="settings icon"></i>{{ $gettext('Settings') }}
             </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['settings']"
               :to="{name: 'manage.users.users.list'}">
-              <i class="users icon"></i>{{ $t('Users') }}
+              <i class="users icon"></i>{{ $gettext('Users') }}
             </router-link>
           </div>
         </div>
@@ -105,12 +110,19 @@
       <i class="history icon"></i>
       <div class="content">
         <div class="header">
-          {{ $t('Do you want to restore your previous queue?') }}
+          {{ $gettext('Do you want to restore your previous queue?') }}
         </div>
-        <p>{{ $t('{%count%} tracks', { count: queue.previousQueue.tracks.length }) }}</p>
+        <p>
+          <translate
+            translate-plural="%{ count } tracks"
+            :translate-n="queue.previousQueue.tracks.length"
+            :translate-params="{count: queue.previousQueue.tracks.length}">
+            %{ count } track
+          </translate>
+        </p>
         <div class="ui two buttons">
-          <div @click="queue.restore()" class="ui basic inverted green button">{{ $t('Yes') }}</div>
-          <div @click="queue.removePrevious()" class="ui basic inverted red button">{{ $t('No') }}</div>
+          <div @click="queue.restore()" class="ui basic inverted green button">{{ $gettext('Yes') }}</div>
+          <div @click="queue.removePrevious()" class="ui basic inverted red button">{{ $gettext('No') }}</div>
         </div>
       </div>
     </div>
@@ -141,10 +153,10 @@
       <div v-if="$store.state.radios.running" class="ui black message">
         <div class="content">
           <div class="header">
-            <i class="feed icon"></i> {{ $t('You have a radio playing') }}
+            <i class="feed icon"></i> {{ $gettext('You have a radio playing') }}
           </div>
-          <p>{{ $t('New tracks will be appended here automatically.') }}</p>
-          <div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button">{{ $t('Stop radio') }}</div>
+          <p>{{ $gettext('New tracks will be appended here automatically.') }}</p>
+          <div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button">{{ $gettext('Stop radio') }}</div>
         </div>
       </div>
     </div>
diff --git a/front/src/components/activity/Like.vue b/front/src/components/activity/Like.vue
index 5396accc233033222eca551364cc4da740f59a47..8e35598d532cb158bf1c53e8fc03970e297da869 100644
--- a/front/src/components/activity/Like.vue
+++ b/front/src/components/activity/Like.vue
@@ -5,20 +5,12 @@
    </div>
    <div class="content">
      <div class="summary">
-       <i18next path="{%0%} favorited a track">
-         <username class="user" :username="event.actor.local_id" />
-       </i18next>
+       <translate :translate-params="{user: event.actor.local_id}">%{ user } favorited a track</translate>
        <human-date class="date" :date="event.published" />
      </div>
      <div class="extra text">
        <router-link :to="{name: 'library.tracks.detail', params: {id: event.object.local_id }}">{{ event.object.name }}</router-link>
-        <i18next path="from album {%0%}, by {%1%}" v-if="event.object.album">
-          {{ event.object.album }}
-          <em>{{ event.object.artist }}</em>
-        </i18next>
-        <i18next path=", by {%0%}" v-else>
-          <em>{{ event.object.artist }}</em>
-        </i18next>
+       <translate :translate-params="{album: event.object.album, artist: event.object.artist}">from %{ album } by %{ artist }</translate>
       </div>
    </div>
  </div>
diff --git a/front/src/components/activity/Listen.vue b/front/src/components/activity/Listen.vue
index bfa3ca16450a69adfa086e1189ab0446e81e0733..2d1a3c1843d2901c728c5e7c4a14d140dbb06200 100644
--- a/front/src/components/activity/Listen.vue
+++ b/front/src/components/activity/Listen.vue
@@ -5,20 +5,12 @@
    </div>
    <div class="content">
      <div class="summary">
-       <i18next path="{%0%} listened to a track">
-         <username class="user" :username="event.actor.local_id" />
-       </i18next>
-       <human-date class="date" :date="event.published" />
-
+      <translate :translate-params="{user: event.actor.local_id}">%{ user } listened to a track</translate>
+      <human-date class="date" :date="event.published" />
      </div>
      <div class="extra text">
        <router-link :to="{name: 'library.tracks.detail', params: {id: event.object.local_id }}">{{ event.object.name }}</router-link>
-        <i18next path="from album {%0%}, by {%1%}" v-if="event.object.album">
-          {{ event.object.album }}<em>{{ event.object.artist }}</em>
-        </i18next>
-        <i18next path=", by {%0%}" v-else>
-          <em>{{ event.object.artist }}</em>
-        </i18next>
+       <translate :translate-params="{album: event.object.album, artist: event.object.artist}">from %{ album } by %{ artist }</translate>
       </div>
    </div>
  </div>
diff --git a/front/src/components/admin/SettingsGroup.vue b/front/src/components/admin/SettingsGroup.vue
index f6d57c239b0d4d6bff7c6b0af1f6e397f2da941c..8ba0fb6f1a2bd76ac6533c1239b9730c3e02b273 100644
--- a/front/src/components/admin/SettingsGroup.vue
+++ b/front/src/components/admin/SettingsGroup.vue
@@ -3,13 +3,13 @@
     <div class="ui divider" />
     <h3 class="ui header">{{ group.label }}</h3>
     <div v-if="errors.length > 0" class="ui negative message">
-      <div class="header">{{ $t('Error while saving settings') }}</div>
+      <div class="header">{{ $gettext('Error while saving settings') }}</div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
       </ul>
     </div>
     <div v-if="result" class="ui positive message">
-      {{ $t('Settings updated successfully.') }}
+      {{ $gettext('Settings updated successfully.') }}
     </div>
     <p v-if="group.help">{{ group.help }}</p>
     <div v-for="setting in settings" class="ui field">
@@ -61,7 +61,7 @@
     <button
       type="submit"
       :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']">
-        {{ $t('Save') }}
+        {{ $gettext('Save') }}
     </button>
   </form>
 </template>
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index 9777fa83ca2d52605e39d37219e25694e49b614c..5c057245ba7f58797096491f4464d8aa90c9bda3 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -1,19 +1,19 @@
 <template>
   <div :title="title" :class="['ui', {'tiny': discrete}, 'buttons']">
     <button
-      :title="$t('Add to current queue')"
+      :title="$gettext('Add to current queue')"
       @click="addNext(true)"
       :disabled="!playable"
       :class="['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}, 'button']">
       <i class="ui play icon"></i>
-      <template v-if="!discrete"><slot><i18next path="Play"/></slot></template>
+      <template v-if="!discrete"><slot>{{ $gettext('Play') }}</slot></template>
     </button>
     <div v-if="!discrete" :class="['ui', {disabled: !playable}, 'floating', 'dropdown', 'icon', 'button']">
       <i class="dropdown icon"></i>
       <div class="menu">
-        <div class="item" :disabled="!playable" @click="add"><i class="plus icon"></i><i18next path="Add to queue"/></div>
-        <div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i><i18next path="Play next"/></div>
-        <div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i><i18next path="Play now"/></div>
+        <div class="item" :disabled="!playable" @click="add"><i class="plus icon"></i>{{ $gettext('Add to queue') }}</div>
+        <div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i>{{ $gettext('Play next') }}</div>
+        <div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i>{{ $gettext('Play now') }}</div>
       </div>
     </div>
   </div>
@@ -44,10 +44,10 @@ export default {
   computed: {
     title () {
       if (this.playable) {
-        return this.$t('Play immediatly')
+        return this.$gettext('Play immediatly')
       } else {
         if (this.track) {
-          return this.$t('This track is not imported and cannot be played')
+          return this.$gettext('This track is not imported and cannot be played')
         }
       }
     },
@@ -142,8 +142,9 @@ export default {
       if (tracks.length < 1) {
         return
       }
+      let msg = this.$ngettext('%{ count } track was added to your queue', '%{ count } tracks were added to your queue', tracks.length)
       this.$store.commit('ui/addMessage', {
-        content: this.$t('{% tracks %} tracks were added to your queue.', {tracks: tracks.length}),
+        content: this.$gettextInterpolate(msg, {count: tracks.length}),
         date: new Date()
       })
     }
diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue
index 8eecb232f67b770be9f28477c4a3380f56a3607d..37457df7d7628d9a3a365a07c35b53e8eea86905 100644
--- a/front/src/components/audio/Player.vue
+++ b/front/src/components/audio/Player.vue
@@ -57,44 +57,44 @@
 
       <div class="two wide column controls ui grid">
         <div
-          :title="$t('Previous track')"
+          :title="$gettext('Previous track')"
           class="two wide column control"
           :disabled="emptyQueue">
             <i @click="previous" :class="['ui', 'backward', {'disabled': emptyQueue}, 'big', 'icon']"></i>
         </div>
         <div
           v-if="!playing"
-          :title="$t('Play track')"
+          :title="$gettext('Play track')"
           class="two wide column control">
             <i @click="togglePlay" :class="['ui', 'play', {'disabled': !currentTrack}, 'big', 'icon']"></i>
         </div>
         <div
           v-else
-          :title="$t('Pause track')"
+          :title="$gettext('Pause track')"
           class="two wide column control">
             <i @click="togglePlay" :class="['ui', 'pause', {'disabled': !currentTrack}, 'big', 'icon']"></i>
         </div>
         <div
-          :title="$t('Next track')"
+          :title="$gettext('Next track')"
           class="two wide column control"
           :disabled="!hasNext">
             <i @click="next" :class="['ui', {'disabled': !hasNext}, 'step', 'forward', 'big', 'icon']" ></i>
         </div>
         <div class="two wide column control volume-control">
-          <i :title="$t('Unmute')" @click="$store.commit('player/volume', 1)" v-if="volume === 0" class="volume off secondary icon"></i>
-          <i :title="$t('Mute')" @click="$store.commit('player/volume', 0)" v-else-if="volume < 0.5" class="volume down secondary icon"></i>
-          <i :title="$t('Mute')" @click="$store.commit('player/volume', 0)" v-else class="volume up secondary icon"></i>
+          <i :title="$gettext('Unmute')" @click="$store.commit('player/volume', 1)" v-if="volume === 0" class="volume off secondary icon"></i>
+          <i :title="$gettext('Mute')" @click="$store.commit('player/volume', 0)" v-else-if="volume < 0.5" class="volume down secondary icon"></i>
+          <i :title="$gettext('Mute')" @click="$store.commit('player/volume', 0)" v-else class="volume up secondary icon"></i>
           <input type="range" step="0.05" min="0" max="1" v-model="sliderVolume" />
         </div>
         <div class="two wide column control looping">
           <i
-            :title="$t('Looping disabled. Click to switch to single-track looping.')"
+            :title="$gettext('Looping disabled. Click to switch to single-track looping.')"
             v-if="looping === 0"
             @click="$store.commit('player/looping', 1)"
             :disabled="!currentTrack"
             :class="['ui', {'disabled': !currentTrack}, 'step', 'repeat', 'secondary', 'icon']"></i>
           <i
-            :title="$t('Looping on a single track. Click to switch to whole queue looping.')"
+            :title="$gettext('Looping on a single track. Click to switch to whole queue looping.')"
             v-if="looping === 1"
             @click="$store.commit('player/looping', 2)"
             :disabled="!currentTrack"
@@ -102,7 +102,7 @@
             <span class="ui circular tiny orange label">1</span>
           </i>
           <i
-            :title="$t('Looping on whole queue. Click to disable looping.')"
+            :title="$gettext('Looping on whole queue. Click to disable looping.')"
             v-if="looping === 2"
             @click="$store.commit('player/looping', 0)"
             :disabled="!currentTrack"
@@ -111,7 +111,7 @@
         </div>
         <div
           :disabled="queue.tracks.length === 0"
-          :title="$t('Shuffle your queue')"
+          :title="$gettext('Shuffle your queue')"
           class="two wide column control">
           <div v-if="isShuffling" class="ui inline shuffling inverted small active loader"></div>
           <i v-else @click="shuffle()" :class="['ui', 'random', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
@@ -119,7 +119,7 @@
         <div class="one wide column"></div>
         <div
           :disabled="queue.tracks.length === 0"
-          :title="$t('Clear your queue')"
+          :title="$gettext('Clear your queue')"
           class="two wide column control">
           <i @click="clean()" :class="['ui', 'trash', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
         </div>
@@ -180,12 +180,13 @@ export default {
         return
       }
       let self = this
+      let msg = this.$gettext('Queue shuffled!')
       this.isShuffling = true
       setTimeout(() => {
         self.$store.dispatch('queue/shuffle', () => {
           self.isShuffling = false
           self.$store.commit('ui/addMessage', {
-            content: self.$t('Queue shuffled!'),
+            content: msg,
             date: new Date()
           })
         })
diff --git a/front/src/components/audio/Search.vue b/front/src/components/audio/Search.vue
index 9cfea3bc0cae5da768bbf1dbefe7e12b405f508c..ad5bdbeb85a00e501aacaf01fad08f07223b04a7 100644
--- a/front/src/components/audio/Search.vue
+++ b/front/src/components/audio/Search.vue
@@ -1,6 +1,6 @@
 <template>
   <div>
-    <h2><i18next path="Search for some music"/></h2>
+    <h2>{{ $gettext('Search for some music') }}</h2>
     <div :class="['ui', {'loading': isLoading }, 'search']">
       <div class="ui icon big input">
         <i class="search icon"></i>
@@ -8,22 +8,22 @@
       </div>
     </div>
     <template v-if="query.length > 0">
-      <h3 class="ui title"><i18next path="Artists"/></h3>
+      <h3 class="ui title">{{ $gettext('Artists') }}</h3>
       <div v-if="results.artists.length > 0" class="ui stackable three column grid">
         <div class="column" :key="artist.id" v-for="artist in results.artists">
           <artist-card class="fluid" :artist="artist" ></artist-card>
         </div>
       </div>
-      <p v-else><i18next path="Sorry, we did not found any artist matching your query"/></p>
+      <p v-else>{{ $gettext('Sorry, we did not found any artist matching your query') }}</p>
     </template>
     <template v-if="query.length > 0">
-      <h3 class="ui title"><i18next path="Albums"/></h3>
+      <h3 class="ui title">{{ $gettext('Albums') }}</h3>
       <div v-if="results.albums.length > 0" class="ui stackable three column grid">
         <div class="column" :key="album.id" v-for="album in results.albums">
           <album-card class="fluid" :album="album" ></album-card>
         </div>
       </div>
-      <p v-else><i18next path="Sorry, we did not found any album matching your query"/></p>
+      <p v-else>{{ $gettext('Sorry, we did not found any album matching your query') }}</p>
     </template>
   </div>
 </template>
diff --git a/front/src/components/audio/album/Card.vue b/front/src/components/audio/album/Card.vue
index 3782771803edce6432a125957e9d10346b073fbd..a12acdccaf83f9f2a673c2c6754ec6965afbf6e7 100644
--- a/front/src/components/audio/album/Card.vue
+++ b/front/src/components/audio/album/Card.vue
@@ -10,10 +10,9 @@
         </div>
         <div class="meta">
           <span>
-            <i18next path="By {%0%}">
-              <router-link tag="span" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
-              {{ album.artist.name }}</router-link>
-            </i18next>
+            <router-link tag="span" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
+              <translate :translate-params="{artist: album.artist.name}">By %{ artist }</translate>
+            </router-link>
           </span><span class="time" v-if="album.release_date">– {{ album.release_date | year }}</span>
         </div>
         <div class="description" v-if="mode === 'rich'">
@@ -39,23 +38,21 @@
           </table>
           <div class="center aligned segment" v-if="album.tracks.length > initialTracks">
             <em v-if="!showAllTracks" @click="showAllTracks = true" class="expand">
-              <i18next path="Show {%0%} more tracks">{{ album.tracks.length - initialTracks }}</i18next>
+              <translate :translate-params="{count: album.tracks.length - initialTracks}" :translate-n="album.tracks.length - initialTracks" translate-plural="Show %{ count } more tracks">Show 1 more track</translate>
             </em>
             <em v-else @click="showAllTracks = false" class="expand">
-              <i18next path="Collapse" />
+              {{ $gettext('Collapse') }}
             </em>
           </div>
         </div>
       </div>
       <div class="extra content">
         <play-button class="mini basic orange right floated" :tracks="album.tracks">
-          <i18next path="Play all"/>
+          {{ $gettext('Play all') }}
         </play-button>
         <span>
           <i class="music icon"></i>
-          <i18next path="{%0%} tracks">
-            {{ album.tracks.length }}
-          </i18next>
+          <translate :translate-params="{count: album.tracks.length}" :translate-n="album.tracks.length" translate-plural="%{ count } tracks">1 track</translate>
         </span>
       </div>
     </div>
diff --git a/front/src/components/audio/artist/Card.vue b/front/src/components/audio/artist/Card.vue
index b19c5e12d727b6a32c6a57c04e9dab4b0b9c3134..10745f14988e186cf2982e30f5cc505df4668de1 100644
--- a/front/src/components/audio/artist/Card.vue
+++ b/front/src/components/audio/artist/Card.vue
@@ -28,12 +28,10 @@
           </table>
           <div class="center aligned segment" v-if="artist.albums.length > initialAlbums">
             <em v-if="!showAllAlbums" @click="showAllAlbums = true" class="expand">
-              <i18next path="Show {%0%} more albums">
-                {{ artist.albums.length - initialAlbums }}
-              </i18next>
+              <translate :translate-params="{count: artist.albums.length - initialAlbums}" :translate-n="artist.albums.length - initialAlbums" translate-plural="Show %{ count } more albums">Show 1 more album</translate>
             </em>
             <em v-else @click="showAllAlbums = false" class="expand">
-              <i18next path="Collapse"/>
+              {{ $gettext('Collapse') }}
             </em>
           </div>
         </div>
@@ -41,12 +39,10 @@
     <div class="extra content">
         <span>
           <i class="sound icon"></i>
-          <i18next path="{%0%} albums">
-            {{ artist.albums.length }}
-          </i18next>
+            <translate :translate-params="{count: artist.albums.length}" :translate-n="artist.albums.length" translate-plural="%{ count } albums">1 album</translate>
         </span>
         <play-button class="mini basic orange right floated" :artist="artist.id">
-          <i18next path="Play all"/>
+          {{ $gettext('Play all') }}
         </play-button>
       </div>
     </div>
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index 81869ff564af2f8b5ee5260535b7fda5f95f924e..5ef9eb494ebc07689679e0be38b855d3c7480bf4 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -4,9 +4,9 @@
       <tr>
         <th></th>
         <th></th>
-        <i18next tag="th" colspan="6" path="Title"/>
-        <i18next tag="th" colspan="6" path="Artist"/>
-        <i18next tag="th" colspan="6" path="Album"/>
+        <th colspan="6">{{ $gettext('Title') }}</th>
+        <th colspan="6">{{ $gettext('Artist') }}</th>
+        <th colspan="6">{{ $gettext('Album') }}</th>
         <th></th>
       </tr>
     </thead>
@@ -21,17 +21,17 @@
       <tr>
         <th colspan="3">
           <button @click="showDownloadModal = !showDownloadModal" class="ui basic button">
-            <i18next path="Download..."/>
+            {{ $gettext('Download') }}
           </button>
           <modal :show.sync="showDownloadModal">
-            <i18next tag="div" path="Download tracks" class="header" />
+            <div class="header">{{ $gettext('Download tracks') }}</div>
             <div class="content">
               <div class="description">
-                <i18next tag="p" path="There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as {%0%} to easily download a list of tracks.">
-                  <a href="https://curl.haxx.se/" target="_blank">cURL</a>
-                </i18next>
-                <i18next path="Simply copy paste the snippet below into a terminal to launch the download."/>
-                <i18next tag="div" class="ui warning message" path="Keep your PRIVATE_TOKEN secret as it gives access to your account."/>
+                <p>{{ $gettext('There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as cURL to easily download a list of tracks.') }}</p>
+                {{ $gettext('Simply copy paste the snippet below into a terminal to launch the download.') }}
+                <div class="ui warning message">
+                  {{ $gettext('Keep your PRIVATE_TOKEN secret as it gives access to your account.') }}
+                </div>
                 <pre>
 export PRIVATE_TOKEN="{{ $store.state.auth.token }}"
 <template v-for="track in tracks"><template v-if="track.files.length > 0">
@@ -40,7 +40,7 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="$store.state.auth.aut
               </div>
             </div>
             <div class="actions">
-              <i18next tag="div" class="ui black deny button" path="Cancel" />
+              <div class="ui black deny button">{{ $gettext('Cancel') }}</div>
             </div>
           </modal>
         </th>
diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue
index 8286860bcaa051fdf2ee844dc878805c3402dcfd..df5a07637814b4aa6fa25b40716517f073c32d57 100644
--- a/front/src/components/auth/Login.vue
+++ b/front/src/components/auth/Login.vue
@@ -2,20 +2,20 @@
   <div class="main pusher" v-title="'Log In'">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2><i18next path="Log in to your Funkwhale account"/></h2>
+        <h2>{{ $gettext('Log in to your Funkwhale account') }}</h2>
         <form class="ui form" @submit.prevent="submit()">
           <div v-if="error" class="ui negative message">
-            <div class="header"><i18next path="We cannot log you in"/></div>
+            <div class="header">{{ $gettext('We cannot log you in') }}</div>
             <ul class="list">
-              <i18next tag="li" v-if="error == 'invalid_credentials'" path="Please double-check your username/password couple is correct"/>
-              <i18next tag="li" v-else path="An unknown error happend, this can mean the server is down or cannot be reached"/>
+              <li v-if="error == 'invalid_credentials'">{{ $gettext('Please double-check your username/password couple is correct') }}</li>
+              <li v-else>{{ $gettext('An unknown error happend, this can mean the server is down or cannot be reached') }}</li>
             </ul>
           </div>
           <div class="field">
             <label>
-              {{ $t('Username or email') }} |
+              {{ $gettext('Username or email') }} |
               <router-link :to="{path: '/signup'}">
-                {{ $t('Create an account') }}
+                {{ $gettext('Create an account') }}
               </router-link>
             </label>
             <input
@@ -30,15 +30,17 @@
           </div>
           <div class="field">
             <label>
-              {{ $t('Password') }} |
+              {{ $gettext('Password') }} |
               <router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}">
-                {{ $t('Reset your password') }}
+                {{ $gettext('Reset your password') }}
               </router-link>
             </label>
             <password-input :index="2" required v-model="credentials.password" />
 
           </div>
-          <button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit"><i18next path="Login"/></button>
+          <button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
+            {{ $gettext('Login') }}
+          </button>
         </form>
       </div>
     </div>
diff --git a/front/src/components/auth/Logout.vue b/front/src/components/auth/Logout.vue
index f5a994e73ff71d96a77fb5bf2fa1b96da8ec7466..2090a65990ded910a3150d1302a627d613369313 100644
--- a/front/src/components/auth/Logout.vue
+++ b/front/src/components/auth/Logout.vue
@@ -2,10 +2,11 @@
   <div class="main pusher" v-title="'Log Out'">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2><i18next path="Are you sure you want to log out?"/></h2>
-        <i18next tag="p" path="You are currently logged in as {%0%}">{{ $store.state.auth.username }}</i18next>
-        <button class="ui button" @click="$store.dispatch('auth/logout')"><i18next path="Yes, log me out!"/></button>
-        </form>
+        <h2>
+          {{ $gettext('Are you sure you want to log out?') }}
+        </h2>
+        <p v-translate="{username: $store.state.auth.username}">You are currently logged in as %{ username }</p>
+        <button class="ui button" @click="$store.dispatch('auth/logout')">{{ $gettext('Yes, log me out!') }}</button>
       </div>
     </div>
   </div>
diff --git a/front/src/components/auth/Profile.vue b/front/src/components/auth/Profile.vue
index ab728c036beb8a29518bb1851a172ff38c1f62d0..cb6ed27444e6ec1386d7dc130e99ed086ef45fd5 100644
--- a/front/src/components/auth/Profile.vue
+++ b/front/src/components/auth/Profile.vue
@@ -9,17 +9,19 @@
           <i class="circular inverted user green icon"></i>
           <div class="content">
             {{ $store.state.auth.profile.username }}
-            <i18next class="sub header" path="Registered since {%0%}">{{ signupDate }}</i18next>
+            <div class="sub header" v-translate="{date: signupDate}">Registered since %{ date }</div>
           </div>
         </h2>
-        <div class="ui basic green label"><i18next path="This is you!"/></div>
+        <div class="ui basic green label">
+          {{ $gettext('This is you!') }}
+        </div>
         <div v-if="$store.state.auth.profile.is_staff" class="ui yellow label">
           <i class="star icon"></i>
-          <i18next path="Staff member"/>
+          {{ $gettext('Staff member') }}
         </div>
         <router-link class="ui tiny basic button" :to="{path: '/settings'}">
           <i class="setting icon"> </i>
-          <i18next path="Settings..."/>
+          {{ $gettext('Settings...') }}
         </router-link>
 
       </div>
diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue
index 5468358aeb438ff768dc9e66d1845c1ecdf5e34d..1dcc05445cb42c307471167292517b8163afaee0 100644
--- a/front/src/components/auth/Settings.vue
+++ b/front/src/components/auth/Settings.vue
@@ -2,13 +2,17 @@
   <div class="main pusher" v-title="'Account Settings'">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2 class="ui header"><i18next path="Account settings"/></h2>
+        <h2 class="ui header">
+          {{ $gettext('Account settings') }}
+        </h2>
         <form class="ui form" @submit.prevent="submitSettings()">
           <div v-if="settings.success" class="ui positive message">
-            <div class="header"><i18next path="Settings updated"/></div>
+            <div class="header">
+              {{ $gettext('Settings updated') }}
+            </div>
           </div>
           <div v-if="settings.errors.length > 0" class="ui negative message">
-            <i18next tag="div" class="header" path="We cannot save your settings"/>
+            <div class="header">{{ $gettext('We cannot save your settings') }}</div>
             <ul class="list">
               <li v-for="error in settings.errors">{{ error }}</li>
             </ul>
@@ -20,46 +24,52 @@
               <option :value="c.value" v-for="c in f.choices">{{ c.label }}</option>
             </select>
           </div>
-          <button :class="['ui', {'loading': isLoading}, 'button']" type="submit"><i18next path="Update settings"/></button>
+          <button :class="['ui', {'loading': isLoading}, 'button']" type="submit">
+            {{ $gettext('Update settings') }}
+          </button>
         </form>
       </div>
       <div class="ui hidden divider"></div>
       <div class="ui small text container">
-        <h2 class="ui header"><i18next path="Change my password"/></h2>
+        <h2 class="ui header">
+          {{ $gettext('Change my password') }}
+        </h2>
         <div class="ui message">
-          {{ $t('Changing your password will also change your Subsonic API password if you have requested one.') }}
-          {{ $t('You will have to update your password on your clients that use this password.') }}
+          {{ $gettext('Changing your password will also change your Subsonic API password if you have requested one.') }}
+          {{ $gettext('You will have to update your password on your clients that use this password.') }}
         </div>
         <form class="ui form" @submit.prevent="submitPassword()">
           <div v-if="passwordError" class="ui negative message">
-            <div class="header"><i18next path="Cannot change your password"/></div>
+            <div class="header">
+              {{ $gettext('Cannot change your password') }}
+            </div>
             <ul class="list">
-              <i18next tag="li" v-if="passwordError == 'invalid_credentials'" path="Please double-check your password is correct"/>
+              <li v-if="passwordError == 'invalid_credentials'">{{ $gettext('Please double-check your password is correct') }}</li>
             </ul>
           </div>
           <div class="field">
-            <label><i18next path="Old password"/></label>
+            <label>{{ $gettext('Old password') }}</label>
             <password-input required v-model="old_password" />
 
           </div>
           <div class="field">
-            <label><i18next path="New password"/></label>
+            <label>{{ $gettext('New password') }}</label>
             <password-input required v-model="new_password" />
           </div>
           <dangerous-button
             color="yellow"
             :class="['ui', {'loading': isLoading}, 'button']"
             :action="submitPassword">
-            {{ $t('Change password') }}
-            <p slot="modal-header">{{ $t('Change your password?') }}</p>
+            {{ $gettext('Change password') }}
+            <p slot="modal-header">{{ $gettext('Change your password?') }}</p>
             <div slot="modal-content">
-              <p>{{ $t("Changing your password will have the following consequences") }}</p>
+              <p>{{ $gettext("Changing your password will have the following consequences") }}</p>
               <ul>
-                <li>{{ $t('You will be logged out from this session and have to log out with the new one') }}</li>
-                <li>{{ $t('Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password') }}</li>
+                <li>{{ $gettext('You will be logged out from this session and have to log out with the new one') }}</li>
+                <li>{{ $gettext('Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password') }}</li>
               </ul>
             </div>
-            <p slot="modal-confirm">{{ $t('Disable access') }}</p>
+            <p slot="modal-confirm">{{ $gettext('Disable access') }}</p>
           </dangerous-button>
         </form>
         <div class="ui hidden divider" />
diff --git a/front/src/components/auth/Signup.vue b/front/src/components/auth/Signup.vue
index e4e5cebbce950b7470204f710e06ffe01e7632e7..6bd32d75bd8dd220b6046ad9b9d028518dbc3935 100644
--- a/front/src/components/auth/Signup.vue
+++ b/front/src/components/auth/Signup.vue
@@ -2,22 +2,22 @@
   <div class="main pusher" v-title="'Sign Up'">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $t("Create a funkwhale account") }}</h2>
+        <h2>{{ $gettext("Create a funkwhale account") }}</h2>
         <form
           :class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
           @submit.prevent="submit()">
           <p class="ui message" v-if="!$store.state.instance.settings.users.registration_enabled.value">
-            {{ $t('Registration are closed on this instance, you will need an invitation code to signup.') }}
+            {{ $gettext('Registration are closed on this instance, you will need an invitation code to signup.') }}
           </p>
 
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $t("We cannot create your account") }}</div>
+            <div class="header">{{ $gettext("We cannot create your account") }}</div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
           <div class="field">
-            <label>{{ $t("Username") }}</label>
+            <label>{{ $gettext("Username") }}</label>
             <input
             ref="username"
             required
@@ -27,7 +27,7 @@
             v-model="username">
           </div>
           <div class="field">
-            <label>{{ $t("Email") }}</label>
+            <label>{{ $gettext("Email") }}</label>
             <input
             ref="email"
             required
@@ -36,20 +36,20 @@
             v-model="email">
           </div>
           <div class="field">
-            <label>{{ $t("Password") }}</label>
+            <label>{{ $gettext("Password") }}</label>
             <password-input v-model="password" />
           </div>
           <div class="field">
-            <label v-if="!$store.state.instance.settings.users.registration_enabled.value">{{ $t("Invitation code") }}</label>
-            <label v-else>{{ $t("Invitation code (optional)") }}</label>
+            <label v-if="!$store.state.instance.settings.users.registration_enabled.value">{{ $gettext("Invitation code") }}</label>
+            <label v-else>{{ $gettext("Invitation code (optional)") }}</label>
             <input
             :required="!$store.state.instance.settings.users.registration_enabled.value"
             type="text"
-            :placeholder="$t('Enter your invitation code (case insensitive)')"
+            :placeholder="$gettext('Enter your invitation code (case insensitive)')"
             v-model="invitation">
           </div>
           <button :class="['ui', 'green', {'loading': isLoading}, 'button']" type="submit">
-            {{ $t("Create my account") }}
+            {{ $gettext("Create my account") }}
           </button>
         </form>
       </div>
diff --git a/front/src/components/auth/SubsonicTokenForm.vue b/front/src/components/auth/SubsonicTokenForm.vue
index 1fa4b5d1de90be9430ff4e81923b8f11d3184220..3ec6e621f8260d997ad146aa7cd7a738ee367125 100644
--- a/front/src/components/auth/SubsonicTokenForm.vue
+++ b/front/src/components/auth/SubsonicTokenForm.vue
@@ -1,24 +1,24 @@
 <template>
   <form class="ui form" @submit.prevent="requestNewToken()">
-    <h2>{{ $t('Subsonic API password') }}</h2>
+    <h2>{{ $gettext('Subsonic API password') }}</h2>
     <p class="ui message" v-if="!subsonicEnabled">
-      {{ $t('The Subsonic API is not available on this Funkwhale instance.') }}
+      {{ $gettext('The Subsonic API is not available on this Funkwhale instance.') }}
     </p>
     <p>
-      {{ $t('Funkwhale is compatible with other music players that support the Subsonic API.') }}
-      {{ $t('You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.') }}
+      {{ $gettext('Funkwhale is compatible with other music players that support the Subsonic API.') }}
+      {{ $gettext('You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.') }}
     </p>
     <p>
-      {{ $t('However, accessing Funkwhale from those clients require a separate password you can set below.') }}
+      {{ $gettext('However, accessing Funkwhale from those clients require a separate password you can set below.') }}
     </p>
     <p><a href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients" target="_blank">
-      {{ $t('Discover how to use Funkwhale from other apps') }}
+      {{ $gettext('Discover how to use Funkwhale from other apps') }}
     </a></p>
     <div v-if="success" class="ui positive message">
       <div class="header">{{ successMessage }}</div>
     </div>
     <div v-if="subsonicEnabled && errors.length > 0" class="ui negative message">
-      <div class="header">{{ $t('Error') }}</div>
+      <div class="header">{{ $gettext('Error') }}</div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
       </ul>
@@ -32,25 +32,25 @@
         color="grey"
         :class="['ui', {'loading': isLoading}, 'button']"
         :action="requestNewToken">
-        {{ $t('Request a new password') }}
-        <p slot="modal-header">{{ $t('Request a new Subsonic API password?') }}</p>
-        <p slot="modal-content">{{ $t('This will log you out from existing devices that use the current password.') }}</p>
-        <p slot="modal-confirm">{{ $t('Request a new password') }}</p>
+        {{ $gettext('Request a new password') }}
+        <p slot="modal-header">{{ $gettext('Request a new Subsonic API password?') }}</p>
+        <p slot="modal-content">{{ $gettext('This will log you out from existing devices that use the current password.') }}</p>
+        <p slot="modal-confirm">{{ $gettext('Request a new password') }}</p>
       </dangerous-button>
       <button
         v-else
         color="grey"
         :class="['ui', {'loading': isLoading}, 'button']"
-        @click="requestNewToken">{{ $t('Request a password') }}</button>
+        @click="requestNewToken">{{ $gettext('Request a password') }}</button>
         <dangerous-button
           v-if="token"
           color="yellow"
           :class="['ui', {'loading': isLoading}, 'button']"
           :action="disable">
-          {{ $t('Disable Subsonic access') }}
-          <p slot="modal-header">{{ $t('Disable Subsonic API access?') }}</p>
-          <p slot="modal-content">{{ $t('This will completely disable access to the Subsonic API using from account.') }}</p>
-          <p slot="modal-confirm">{{ $t('Disable access') }}</p>
+          {{ $gettext('Disable Subsonic access') }}
+          <p slot="modal-header">{{ $gettext('Disable Subsonic API access?') }}</p>
+          <p slot="modal-content">{{ $gettext('This will completely disable access to the Subsonic API using from account.') }}</p>
+          <p slot="modal-confirm">{{ $gettext('Disable access') }}</p>
         </dangerous-button>
     </template>
   </form>
@@ -92,7 +92,7 @@ export default {
       })
     },
     requestNewToken () {
-      this.successMessage = this.$t('Password updated')
+      this.successMessage = this.$gettext('Password updated')
       this.success = false
       this.errors = []
       this.isLoading = true
@@ -108,7 +108,7 @@ export default {
       })
     },
     disable () {
-      this.successMessage = this.$t('Access disabled')
+      this.successMessage = this.$gettext('Access disabled')
       this.success = false
       this.errors = []
       this.isLoading = true
diff --git a/front/src/components/common/ActionTable.vue b/front/src/components/common/ActionTable.vue
index 097fb29385eb495d00bc349d88f733c5d9ac5813..b1f97101eea78a5ad5d877945153edc3b362a597 100644
--- a/front/src/components/common/ActionTable.vue
+++ b/front/src/components/common/ActionTable.vue
@@ -6,7 +6,7 @@
           <div class="ui small form">
             <div class="ui inline fields">
               <div class="field">
-                <label>{{ $t('Actions') }}</label>
+                <label>{{ $gettext('Actions') }}</label>
                 <select class="ui dropdown" v-model="currentActionName">
                   <option v-for="action in actions" :value="action.name">
                     {{ action.label }}
@@ -19,41 +19,75 @@
                   @click="launchAction"
                   :disabled="checked.length === 0"
                   :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']">
-                  {{ $t('Go') }}</div>
+                  {{ $gettext('Go') }}</div>
                 <dangerous-button
                   v-else-if="!currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
                   confirm-color="green"
                   color=""
                   @confirm="launchAction">
-                  {{ $t('Go') }}
-                  <p slot="modal-header">{{ $t('Do you want to launch action "{% action %}" on {% total %} elements?', {action: currentActionName, total: objectsData.count}) }}
+                  {{ $gettext('Go') }}
+                  <p slot="modal-header">
+                    <translate
+                      :translate-n="objectsData.count"
+                      :translate-params="{count: objectsData.count, action: currentActionName}"
+                      translate-plural="Do you want to launch %{ action } on %{ count } elements?">
+                      Do you want to launch %{ action } on %{ count } element?
+                    </translate>
+                  </p>
                   <p slot="modal-content">
-                    {{ $t('This may affect a lot of elements, please double check this is really what you want.')}}
+                    {{ $gettext('This may affect a lot of elements, please double check this is really what you want.')}}
                   </p>
-                  <p slot="modal-confirm">{{ $t('Launch') }}</p>
+                  <p slot="modal-confirm">{{ $gettext('Launch') }}</p>
                 </dangerous-button>
               </div>
               <div class="count field">
-                <span v-if="selectAll">{{ $t('{% count %} on {% total %} selected', {count: objectsData.count, total: objectsData.count}) }}</span>
-                <span v-else>{{ $t('{% count %} on {% total %} selected', {count: checked.length, total: objectsData.count}) }}</span>
+                <translate
+                  tag="span"
+                  v-if="selectAll"
+                  :translate-n="objectsData.count"
+                  :translate-params="{count: objectsData.count, total: objectsData.count}"
+                  translate-plural="%{ count } on %{ total } selected">
+                  %{ count } on %{ total } selected
+                </translate>
+                <translate
+                  tag="span"
+                  v-else
+                  :translate-n="checked.length"
+                  :translate-params="{count: checked.length, total: objectsData.count}"
+                  translate-plural="%{ count } on %{ total } selected">
+                  %{ count } on %{ total } selected
+                </translate>
                 <template v-if="!currentAction.isDangerous && checkable.length > 0 && checkable.length === checked.length">
                   <a @click="selectAll = true" v-if="!selectAll">
-                    {{ $t('Select all {% total %} elements', {total: objectsData.count}) }}
+                    <translate
+                      :translate-n="objectsData.count"
+                      :translate-params="{total: objectsData.count}"
+                      translate-plural="Select all %{ total } elements">
+                      Select all %{ total } elements
+                    </translate>
                   </a>
                   <a @click="selectAll = false" v-else>
-                    {{ $t('Select only current page') }}
+                    {{ $gettext('Select only current page') }}
                   </a>
                 </template>
               </div>
             </div>
             <div v-if="actionErrors.length > 0" class="ui negative message">
-              <div class="header">{{ $t('Error while applying action') }}</div>
+              <div class="header">{{ $gettext('Error while applying action') }}</div>
               <ul class="list">
                 <li v-for="error in actionErrors">{{ error }}</li>
               </ul>
             </div>
             <div v-if="actionResult" class="ui positive message">
-              <p>{{ $t('Action {% action %} was launched successfully on {% count %} objects.', {action: actionResult.action, count: actionResult.updated}) }}</p>
+              <p>
+                <translate
+                  :translate-n="actionResult.updated"
+                  :translate-params="{count: actionResult.updated, action: actionResult.action}"
+                  translate-plural="Action %{ action } was launched successfully on %{ count } elements">
+                  Action %{ action } was launched successfully on %{ count } element
+                </translate>
+              </p>
+
               <slot name="action-success-footer" :result="actionResult">
               </slot>
             </div>
@@ -81,7 +115,6 @@
             :disabled="checkable.indexOf(obj.id) === -1"
             @click="toggleCheck($event, obj.id, index)"
             :checked="checked.indexOf(obj.id) > -1"><label>&nbsp;</label>
-          </div>
         </td>
         <slot name="row-cells" :obj="obj"></slot>
       </tr>
diff --git a/front/src/components/common/DangerousButton.vue b/front/src/components/common/DangerousButton.vue
index 52fcdca6136528bea7cb7b36125074654e62800e..a17aa857e61a25e3d4c5cf6b50b4cce32c506c5f 100644
--- a/front/src/components/common/DangerousButton.vue
+++ b/front/src/components/common/DangerousButton.vue
@@ -4,7 +4,9 @@
 
     <modal class="small" :show.sync="showModal">
       <div class="header">
-        <slot name="modal-header"><i18next path="Do you want to confirm this action?"/></slot>
+        <slot name="modal-header">
+          {{ $gettext('Do you want to confirm this action?') }}
+        </slot>
       </div>
       <div class="scrolling content">
         <div class="description">
@@ -12,9 +14,13 @@
         </div>
       </div>
       <div class="actions">
-        <div class="ui cancel button"><i18next path="Cancel"/></div>
+        <div class="ui cancel button">
+          {{ $gettext('Cancel') }}
+        </div>
         <div :class="['ui', 'confirm', confirmButtonColor, 'button']" @click="confirm">
-          <slot name="modal-confirm"><i18next path="Confirm"/></slot>
+          <slot name="modal-confirm">
+            {{ $gettext('Confirm') }}
+          </slot>
         </div>
       </div>
     </modal>
diff --git a/front/src/components/discussion/Comment.vue b/front/src/components/discussion/Comment.vue
index 0c0678d86221ea023fa1a319ce541e63b293304f..f88b84234c668b7042c018244b359546a28bdc6c 100644
--- a/front/src/components/discussion/Comment.vue
+++ b/front/src/components/discussion/Comment.vue
@@ -11,13 +11,15 @@
         <span
           @click="collapsed = false"
           v-if="truncated && collapsed"
-          class="expand"
-          path="Expand"/>
-        <i18next
+          class="expand">
+          {{ $gettext('Expand') }}
+        </span>
+        <span
           @click="collapsed = true"
           v-if="truncated && !collapsed"
-          class="collapse"
-          path="Collapse"/>
+          class="collapse">
+          {{ $gettext('Collapse') }}
+        </span>
       </div>
     </div>
   </div>
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index 65d97cef9bf49a91e9e649257b758c05883b6750..aaa73dc7cd10ed93cb8e42c59d3ec45407ddfc6a 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -2,13 +2,18 @@
   <div class="main pusher" v-title="'Your Favorites'">
     <div class="ui vertical center aligned stripe segment">
       <div :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
-        <div class="ui text loader"><i18next path="Loading your favorites..."/></div>
+        <div class="ui text loader">
+          {{ $gettext('Loading your favorites...') }}
+        </div>
       </div>
       <h2 v-if="results" class="ui center aligned icon header">
         <i class="circular inverted heart pink icon"></i>
-        <i18next path="{%0%} favorites">
-          {{ $store.state.favorites.count }}
-        </i18next>
+        <translate
+          translate-plural="%{ count } favorites"
+          :translate-n="$store.state.favorites.count"
+          :translate-params="{count: $store.state.favorites.count}">
+          1 favorite
+        </translate>
       </h2>
       <radio-button type="favorites"></radio-button>
     </div>
@@ -16,7 +21,7 @@
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <i18next tag="label" path="Ordering"/>
+            <label>{{ $gettext('Ordering') }}</label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -24,14 +29,14 @@
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Ordering direction"/>
+            <label>{{ $gettext('Ordering direction') }}</label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+"><i18next path="Ascending"/></option>
-              <option value="-"><i18next path="Descending"/></option>
+              <option value="+">{{ $gettext('Ascending') }}</option>
+              <option value="-">{{ $gettext('Descending') }}</option>
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Results per page"/>
+            <label>{{ $gettext('Results per page') }}</label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
diff --git a/front/src/components/favorites/TrackFavoriteIcon.vue b/front/src/components/favorites/TrackFavoriteIcon.vue
index d8d62afaaa363e520c45e600d18a18fded34cf1d..88402159c934b06ca7b6c5514ed9babbd5ef1b96 100644
--- a/front/src/components/favorites/TrackFavoriteIcon.vue
+++ b/front/src/components/favorites/TrackFavoriteIcon.vue
@@ -1,8 +1,8 @@
  <template>
   <button @click="$store.dispatch('favorites/toggle', track.id)" v-if="button" :class="['ui', 'pink', {'inverted': isFavorite}, {'favorited': isFavorite}, 'button']">
     <i class="heart icon"></i>
-    <i18next v-if="isFavorite" path="In favorites"/>
-    <i18next v-else path="Add to favorites"/>
+    <translate v-if="isFavorite">In favorites</translate>
+    <translate v-else>Add to favorites</translate>
   </button>
   <i v-else @click="$store.dispatch('favorites/toggle', track.id)" :class="['favorite-icon', 'heart', {'pink': isFavorite}, {'favorited': isFavorite}, 'link', 'icon']" :title="title"></i>
 </template>
@@ -16,9 +16,9 @@ export default {
   computed: {
     title () {
       if (this.isFavorite) {
-        return this.$t('Remove from favorites')
+        return this.$gettext('Remove from favorites')
       } else {
-        return this.$t('Add to favorites')
+        return this.$gettext('Add to favorites')
       }
     },
     isFavorite () {
diff --git a/front/src/components/federation/LibraryCard.vue b/front/src/components/federation/LibraryCard.vue
index e7ef7a516e1eab553583f965b3443d134c7169d4..24b43221fdc2eb4da16fd7da5ca479881e67c88d 100644
--- a/front/src/components/federation/LibraryCard.vue
+++ b/front/src/components/federation/LibraryCard.vue
@@ -13,39 +13,42 @@
     </div>
     <div class="content">
       <span class="right floated" v-if="following">
-        <i class="check icon"></i><i18next path="Following"/>
+        <i class="check icon"></i>{{ $gettext('Following') }}
       </span>
       <span class="right floated" v-else-if="manuallyApprovesFollowers">
-        <i class="lock icon"></i><i18next path="Followers only"/>
+        <i class="lock icon"></i>{{ $gettext('Followers only') }}
       </span>
       <span class="right floated" v-else>
-        <i class="open lock icon"></i><i18next path="Open"/>
+        <i class="open lock icon"></i>{{ $gettext('Open') }}
       </span>
       <span v-if="totalItems">
         <i class="music icon"></i>
-        <i18next path="{%0%} tracks">
-          {{ totalItems }}
-        </i18next>
+        <translate
+          translate-plural="%{ count } tracks"
+          :translate-n="totalItems"
+          :translate-params="{count: totalItems}">
+          1 track
+        </translate>
       </span>
     </div>
     <div class="extra content">
       <template v-if="awaitingApproval">
         <i class="clock icon"></i>
-        <i18next path="Follow request pending approval"/>
+        {{ $gettext('Follow request pending approval') }}
       </template>
       <div
         v-if="!library"
         @click="follow"
         :disabled="isLoading"
         :class="['ui', 'basic', {loading: isLoading}, 'green', 'button']">
-        <i18next v-if="manuallyApprovesFollowers" path="Send a follow request"/>
-        <i18next v-else path="Follow"/>
+        <translate v-if="manuallyApprovesFollowers">Send a follow request</translate>
+        <translate v-else>Follow</translate>
       </div>
       <router-link
         v-else
         class="ui basic button"
         :to="{name: 'federation.libraries.detail', params: {id: library.uuid }}">
-        <i18next path="Detail"/>
+        {{ $gettext('Detail') }}
       </router-link>
     </div>
   </div>
diff --git a/front/src/components/federation/LibraryFollowTable.vue b/front/src/components/federation/LibraryFollowTable.vue
index 4a7fe59f06c483d940a80d0cd57d89f8916b794f..9f4a6baf5c367e2be34fb099d4258861714d8387 100644
--- a/front/src/components/federation/LibraryFollowTable.vue
+++ b/front/src/components/federation/LibraryFollowTable.vue
@@ -8,7 +8,9 @@
         <div class="ui four wide inline field">
           <div class="ui checkbox">
             <input v-model="pending" type="checkbox">
-            <label><i18next path="Pending approval"/></label>
+            <label>
+              {{ $gettext('Pending approval') }}
+            </label>
           </div>
         </div>
       </div>
@@ -17,10 +19,10 @@
     <table v-if="result" class="ui very basic single line unstackable table">
       <thead>
         <tr>
-          <i18next tag="th" path="Actor"/>
-          <i18next tag="th" path="Creation date"/>
-          <i18next tag="th" path="Status"/>
-          <i18next tag="th" path="Actions"/>
+          <th>{{ $gettext('Actor') }}</th>
+          <th>{{ $gettext('Creation date') }}</th>
+          <th>{{ $gettext('Status') }}</th>
+          <th>{{ $gettext('Actions') }}</th>
         </tr>
       </thead>
       <tbody>
@@ -33,36 +35,49 @@
           </td>
           <td>
             <template v-if="follow.approved === true">
-              <i class="check icon"></i><i18next path="Approved"/>
+              <i class="check icon"></i>
+              {{ $gettext('Approved') }}
             </template>
             <template v-else-if="follow.approved === false">
-              <i class="x icon"></i><i18next path="Refused"/>
+              <i class="x icon"></i>
+              {{ $gettext('Refused') }}
             </template>
             <template v-else>
-              <i class="clock icon"></i><i18next path="Pending"/>
+              <i class="clock icon"></i>
+              {{ $gettext('Pending') }}
             </template>
           </td>
           <td>
             <dangerous-button v-if="follow.approved !== false" class="tiny basic labeled icon" color='red' @confirm="updateFollow(follow, false)">
-              <i class="x icon"></i><i18next path="Deny"/>
-              <p slot="modal-header"><i18next path="Deny access?"/></p>
+              <i class="x icon"></i>
+              {{ $gettext('Deny') }}
+              <p slot="modal-header">
+                {{ $gettext('Deny access?') }}
+              </p>
               <p slot="modal-content">
-                <i18next path="By confirming, {%0%}@{%1%} will be denied access to your library.">
-                  {{ follow.actor.preferred_username }}
-                  {{ follow.actor.domain }}
-                </i18next>
+                <translate
+                  :translate-params="{username: follow.actor.preferred_username + '@' + follow.actor.domain}">
+                  By confirming, %{ username } will be denied access to your library.
+                </translate>
+              </p>
+              <p slot="modal-confirm">
+                {{ $gettext('Deny') }}
               </p>
-              <p slot="modal-confirm"><i18next path="Deny"/></p>
             </dangerous-button>
             <dangerous-button v-if="follow.approved !== true" class="tiny basic labeled icon" color='green' @confirm="updateFollow(follow, true)">
-              <i class="check icon"></i> <i18next path="Approve"/>
-              <p slot="modal-header"><i18next path="Approve access?"/></p>
+              <i class="check icon"></i>
+              {{ $gettext('Approve') }}
+              <p slot="modal-header">
+                {{ $gettext('Approve access?') }}
+              </p>
               <p slot="modal-content">
-                <i18next path="By confirming, {%0%}@{%1%} will be granted access to your library.">
-                  {{ follow.actor.preferred_username }}
-                  {{ follow.actor.domain }}
-                </i18next>
-              <p slot="modal-confirm"><i18next path="Approve"/></p>
+                <translate
+                  :translate-params="{username: follow.actor.preferred_username + '@' + follow.actor.domain}">
+                  By confirming, %{ username } will be granted access to your library.
+                </translate>
+              <p slot="modal-confirm">
+                {{ $gettext('Approve') }}
+              </p>
             </dangerous-button>
           </td>
         </tr>
@@ -80,11 +95,10 @@
             ></pagination>
           </th>
           <th v-if="result && result.results.length > 0">
-            <i18next path="Showing results {%0%}-{%1%} on {%2%}">
-              {{ ((page-1) * paginateBy) + 1 }}
-              {{ ((page-1) * paginateBy) + result.results.length }}
-              {{ result.count }}
-            </i18next>
+            <translate
+              :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+              Showing results %{ start }-%{ end } on %{ total }
+            </translate>
           </th>
           <th></th>
           <th></th>
diff --git a/front/src/components/federation/LibraryForm.vue b/front/src/components/federation/LibraryForm.vue
index 7547c3718faebaecee8187bb2314d09ca62718a6..c790c0ef753356c19f7c5539940db9493cd0cb59 100644
--- a/front/src/components/federation/LibraryForm.vue
+++ b/front/src/components/federation/LibraryForm.vue
@@ -1,9 +1,15 @@
 <template>
   <form class="ui form" @submit.prevent="fetchInstanceInfo">
-    <h3 class="ui header"><i18next path="Federate with a new instance"/></h3>
-    <p><i18next path="Use this form to scan an instance and setup federation."/></p>
+    <h3 class="ui header">
+      {{ $gettext('Federate with a new instance') }}
+    </h3>
+    <p>
+      {{ $gettext('Use this form to scan an instance and setup federation.') }}
+    </p>
     <div v-if="errors.length > 0 || scanErrors.length > 0" class="ui negative message">
-      <div class="header"><i18next path="Error while scanning library"/></div>
+      <div class="header">
+        {{ $gettext('Error while scanning library') }}
+      </div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
         <li v-for="error in scanErrors">{{ error }}</li>
@@ -11,7 +17,9 @@
     </div>
     <div class="ui two fields">
       <div class="ui field">
-        <label><i18next path="Library name"/></label>
+        <label>
+          {{ $gettext('Library name') }}
+        </label>
         <input v-model="libraryUsername" type="text" placeholder="library@demo.funkwhale.audio" />
       </div>
       <div class="ui field">
@@ -21,7 +29,7 @@
           :disabled="isLoading"
           :class="['ui', 'icon', {loading: isLoading}, 'button']">
           <i class="search icon"></i>
-          <i18next path="Launch scan"/>
+          {{ $gettext('Launch scan') }}
         </button>
       </div>
     </div>
diff --git a/front/src/components/federation/LibraryTrackTable.vue b/front/src/components/federation/LibraryTrackTable.vue
index 43b52c835bb84ec3434f5a8fca110e70ecd24ff7..058c213768096d8861b75e0ad45024c5cf1f4f95 100644
--- a/front/src/components/federation/LibraryTrackTable.vue
+++ b/front/src/components/federation/LibraryTrackTable.vue
@@ -3,16 +3,16 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $t('Search') }}</label>
+          <label>{{ $gettext('Search') }}</label>
           <input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
         </div>
         <div class="ui field">
-          <label>{{ $t('Import status') }}</label>
+          <label>{{ $gettext('Import status') }}</label>
           <select class="ui dropdown" v-model="importedFilter">
-            <option :value="null">{{ $t('Any') }}</option>
-            <option :value="'imported'">{{ $t('Imported') }}</option>
-            <option :value="'not_imported'">{{ $t('Not imported') }}</option>
-            <option :value="'import_pending'">{{ $t('Import pending') }}</option>
+            <option :value="null">{{ $gettext('Any') }}</option>
+            <option :value="'imported'">{{ $gettext('Imported') }}</option>
+            <option :value="'not_imported'">{{ $gettext('Not imported') }}</option>
+            <option :value="'import_pending'">{{ $gettext('Import pending') }}</option>
           </select>
         </div>
       </div>
@@ -29,25 +29,28 @@
         :action-url="'federation/library-tracks/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $t('Status') }}</th>
-          <th>{{ $t('Title') }}</th>
-          <th>{{ $t('Artist') }}</th>
-          <th>{{ $t('Album') }}</th>
-          <th>{{ $t('Published date') }}</th>
-          <th v-if="showLibrary">{{ $t('Library') }}</th>
+          <th>{{ $gettext('Status') }}</th>
+          <th>{{ $gettext('Title') }}</th>
+          <th>{{ $gettext('Artist') }}</th>
+          <th>{{ $gettext('Album') }}</th>
+          <th>{{ $gettext('Published date') }}</th>
+          <th v-if="showLibrary">{{ $gettext('Library') }}</th>
         </template>
         <template slot="action-success-footer" slot-scope="scope">
           <router-link
             v-if="scope.result.action === 'import'"
             :to="{name: 'library.import.batches.detail', params: {id: scope.result.result.batch.id }}">
-            {{ $t('Import #{% id %} launched', {id: scope.result.result.batch.id}) }}
+            <translate
+              :translate-params="{id: scope.result.result.batch.id}">
+              Import #%{ id } launched
+            </translate>
           </router-link>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
-            <span v-if="scope.obj.status === 'imported'" class="ui basic green label">{{ $t('In library') }}</span>
-            <span v-else-if="scope.obj.status === 'import_pending'" class="ui basic yellow label">{{ $t('Import pending') }}</span>
-            <span v-else class="ui basic label">{{ $t('Not imported') }}</span>
+            <span v-if="scope.obj.status === 'imported'" class="ui basic green label">{{ $gettext('In library') }}</span>
+            <span v-else-if="scope.obj.status === 'import_pending'" class="ui basic yellow label">{{ $gettext('Import pending') }}</span>
+            <span v-else class="ui basic label">{{ $gettext('Not imported') }}</span>
           </td>
           <td>
             <span :title="scope.obj.title">{{ scope.obj.title|truncate(30) }}</span>
@@ -78,7 +81,10 @@
         ></pagination>
 
       <span v-if="result && result.results.length > 0">
-        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+        <translate
+          :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+          Showing results %{ start }-%{ end } on %{ total }
+        </translate>
       </span>
     </div>
   </div>
@@ -150,10 +156,11 @@ export default {
       }
     },
     actions () {
+      let msg = this.$gettext('Import')
       return [
         {
           name: 'import',
-          label: this.$t('Import'),
+          label: msg,
           filterCheckable: (obj) => { return obj.status === 'not_imported' }
         }
       ]
diff --git a/front/src/components/forms/PasswordInput.vue b/front/src/components/forms/PasswordInput.vue
index 624a92d87c8d204ec9b8e40b247e1e7835ed5951..363f1f82f496558e9bdf73cc3c08b95006202e26 100644
--- a/front/src/components/forms/PasswordInput.vue
+++ b/front/src/components/forms/PasswordInput.vue
@@ -6,7 +6,7 @@
     :type="passwordInputType"
     @input="$emit('input', $event.target.value)"
     :value="value">
-    <span @click="showPassword = !showPassword" :title="$t('Show/hide password')" class="ui icon button">
+    <span @click="showPassword = !showPassword" :title="$gettext('Show/hide password')" class="ui icon button">
       <i class="eye icon"></i>
     </span>
   </div>
diff --git a/front/src/components/instance/Stats.vue b/front/src/components/instance/Stats.vue
index ac144ceb3a910e50e59ea9f9efd0575d2c037a0a..fbb9e07fa60312e9b8cd77d2036a5cd123f8a4bf 100644
--- a/front/src/components/instance/Stats.vue
+++ b/front/src/components/instance/Stats.vue
@@ -2,26 +2,28 @@
   <div>
     <div v-if="stats" class="ui stackable two column grid">
       <div class="column">
-        <h3 class="ui left aligned header"><i18next path="User activity"/></h3>
+        <h3 class="ui left aligned header">
+          {{ $gettext('User activity') }}
+        </h3>
         <div v-if="stats" class="ui mini horizontal statistics">
           <div class="statistic">
             <div class="value">
               <i class="green user icon"></i>
               {{ stats.users }}
             </div>
-            <i18next tag="div" class="label" path="users"/>
+            <div class="label">{{ $gettext('users') }}</div>
           </div>
           <div class="statistic">
             <div class="value">
               <i class="orange sound icon"></i> {{ stats.listenings }}
             </div>
-            <i18next tag="div" class="label" path="tracks listened"/>
+            <div class="label">{{ $gettext('tracks listened') }}</div>
           </div>
           <div class="statistic">
             <div class="value">
               <i class="pink heart icon"></i> {{ stats.trackFavorites }}
             </div>
-            <i18next tag="div" class="label" path="Tracks favorited"/>
+            <div class="label">{{ $gettext('Tracks favorited') }}</div>
           </div>
         </div>
       </div>
@@ -32,25 +34,25 @@
             <div class="value">
               {{ parseInt(stats.musicDuration) }}
             </div>
-            <i18next tag="div" class="label" path="hours of music"/>
+            <div class="label">{{ $gettext('Hours of music') }}</div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.artists }}
             </div>
-            <i18next tag="div" class="label" path="Artists"/>
+            <div class="label">{{ $gettext('Artists') }}</div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.albums }}
             </div>
-            <i18next tag="div" class="label" path="Albums"/>
+            <div class="label">{{ $gettext('Albums') }}</div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.tracks }}
             </div>
-            <i18next tag="div" class="label" path="tracks"/>
+            <div class="label">{{ $gettext('tracks') }}</div>
           </div>
         </div>
       </div>
diff --git a/front/src/components/library/Album.vue b/front/src/components/library/Album.vue
index 9a4288b8ac46a8e584fa8dbf23bae3a96fa4e5cc..76c283a6c967157ae58236daef4adab18618d043 100644
--- a/front/src/components/library/Album.vue
+++ b/front/src/components/library/Album.vue
@@ -10,30 +10,39 @@
             <i class="circular inverted sound yellow icon"></i>
             <div class="content">
               {{ album.title }}
-              <i18next tag="div" class="sub header" path="Album containing {%0%} tracks, by {%1%}">
-                {{ album.tracks.length }}
-                <router-link :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
-                  {{ album.artist.name }}
-                </router-link>
-              </i18next>
+              <translate
+                tag="div"
+                translate-plural="Album containing %{ count } tracks, by %{ artist }"
+                :translate-n="album.tracks.length"
+                :translate-params="{count: album.tracks.length, artist: album.artist.name}">
+                Album containing %{ count } track, by %{ artist }
+              </translate>
+            </div>
+            <div class="ui basic buttons">
+              <router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
+                {{ $gettext('Artist page') }}
+              </router-link>
             </div>
           </h2>
           <div class="ui hidden divider"></div>
-          </button>
-          <play-button class="orange" :tracks="album.tracks"><i18next path="Play all"/></play-button>
+          <play-button class="orange" :tracks="album.tracks">
+            {{ $gettext('Play all') }}
+          </play-button>
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            <i18next path="Search on Wikipedia"/>
+            {{ $gettext('Search on Wikipedia') }}
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            <i18next path="View on MusicBrainz"/>
+            {{ $gettext('View on MusicBrainz') }}
           </a>
         </div>
       </div>
       <div class="ui vertical stripe segment">
-        <h2><i18next path="Tracks"/></h2>
+        <h2>
+          {{ $gettext('Tracks') }}
+        </h2>
         <track-table v-if="album" :display-position="true" :tracks="album.tracks"></track-table>
       </div>
     </template>
diff --git a/front/src/components/library/Artist.vue b/front/src/components/library/Artist.vue
index 171b80e8b7b1f9a2ad8c7a65e9dc3c227e6e1b5a..390f431fa27a353d606db52f163de076b2c99ddc 100644
--- a/front/src/components/library/Artist.vue
+++ b/front/src/components/library/Artist.vue
@@ -11,22 +11,29 @@
             <div class="content">
               {{ artist.name }}
               <div class="sub header" v-if="albums">
-                {{ $t('{% track_count %} tracks in {% album_count %} albums', {track_count: totalTracks, album_count: albums.length})}}
+                <translate
+                  tag="div"
+                  translate-plural="%{ count } tracks in %{ albumsCount } albums"
+                  :translate-n="totalTracks"
+                  :translate-params="{count: totalTracks, albumsCount: albums.length}">
+                  %{ count } track in %{ albumsCount } albums
+                </translate>
               </div>
             </div>
           </h2>
           <div class="ui hidden divider"></div>
           <radio-button type="artist" :object-id="artist.id"></radio-button>
-          </button>
-          <play-button class="orange" :artist="artist.id"><i18next path="Play all albums"/></play-button>
+          <play-button class="orange" :artist="artist.id">
+            {{ $gettext('Play all albums') }}
+          </play-button>
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            <i18next path="Search on Wikipedia"/>
+            {{ $gettext('Search on Wikipedia') }}
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            <i18next path="View on MusicBrainz"/>
+            {{ $gettext('View on MusicBrainz') }}
           </a>
         </div>
       </div>
@@ -34,7 +41,9 @@
         <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
       </div>
       <div v-else-if="albums" class="ui vertical stripe segment">
-        <h2><i18next path="Albums by this artist"/></h2>
+        <h2>
+          {{ $gettext('Albums by this artist') }}
+        </h2>
         <div class="ui stackable doubling three column grid">
           <div class="column" :key="album.id" v-for="album in albums">
             <album-card :mode="'rich'" class="fluid" :album="album"></album-card>
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index 9f324eef34535705afba60225bd81495a0d7686d..527a1105736e512f9b476c057d97ecb5391186a3 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -1,15 +1,19 @@
 <template>
   <div v-title="'Artists'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header"><i18next path="Browsing artists"/></h2>
+      <h2 class="ui header">
+        {{ $gettext('Browsing artists') }}
+      </h2>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <label><i18next path="Search"/></label>
+            <label>
+              {{ $gettext('Search') }}
+            </label>
             <input type="text" v-model="query" placeholder="Enter an artist name..."/>
           </div>
           <div class="field">
-            <i18next tag="label" path="Ordering"/>
+            <label>{{ $gettext('Ordering') }}</label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -17,14 +21,14 @@
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Ordering direction"/>
+            <label>{{ $gettext('Ordering direction') }}</label>
             <select class="ui dropdown" v-model="orderingDirection">
               <option value="+">Ascending</option>
               <option value="-">Descending</option>
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Results per page"/>
+            <label>{{ $gettext('Results per page') }}</label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
diff --git a/front/src/components/library/Home.vue b/front/src/components/library/Home.vue
index 2be693fab2ed7a2a58934b36945f21ec8ca4e41d..8fbb7d299f88dcabe3d9e310a16bf4297a1c7d00 100644
--- a/front/src/components/library/Home.vue
+++ b/front/src/components/library/Home.vue
@@ -6,20 +6,26 @@
     <div class="ui vertical stripe segment">
       <div class="ui stackable three column grid">
         <div class="column">
-          <h2 class="ui header"><i18next path="Latest artists"/></h2>
+          <h2 class="ui header">
+            {{ $gettext('Latest artists') }}
+          </h2>
           <div :class="['ui', {'active': isLoadingArtists}, 'inline', 'loader']"></div>
           <div v-if="artists.length > 0" v-for="artist in artists.slice(0, 3)" :key="artist.id" class="ui cards">
             <artist-card :artist="artist"></artist-card>
           </div>
         </div>
         <div class="column">
-          <h2 class="ui header"><i18next path="Radios"/></h2>
+          <h2 class="ui header">
+            {{ $gettext('Radios') }}
+          </h2>
           <radio-card :type="'favorites'"></radio-card>
           <radio-card :type="'random'"></radio-card>
           <radio-card :type="'less-listened'"></radio-card>
         </div>
         <div class="column">
-          <h2 class="ui header"><i18next path="Music requests"/></h2>
+          <h2 class="ui header">
+            {{ $gettext('Music requests') }}
+          </h2>
           <request-form v-if="$store.state.auth.authenticated"></request-form>
         </div>
       </div>
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index 5360de16cd0d959a9dbda011dd5d408ac7f97115..71e270c367c244467b53993e20c397dab03cfa83 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -1,16 +1,24 @@
 <template>
   <div class="main library pusher">
     <div class="ui secondary pointing menu">
-      <router-link class="ui item" to="/library" exact><i18next path="Browse"/></router-link>
-      <router-link class="ui item" to="/library/artists" exact><i18next path="Artists"/></router-link>
-      <router-link class="ui item" to="/library/radios" exact><i18next path="Radios"/></router-link>
-      <router-link class="ui item" to="/library/playlists" exact><i18next path="Playlists"/></router-link>
+      <router-link class="ui item" to="/library" exact>
+        {{ $gettext('Browse') }}
+      </router-link>
+      <router-link class="ui item" to="/library/artists" exact>
+        {{ $gettext('Artists') }}
+      </router-link>
+      <router-link class="ui item" to="/library/radios" exact>
+        {{ $gettext('Radios') }}
+      </router-link>
+      <router-link class="ui item" to="/library/playlists" exact>
+        {{ $gettext('Playlists') }}
+      </router-link>
       <div class="ui secondary right menu">
         <router-link v-if="showImports" class="ui item" to="/library/import/launch" exact>
-          <i18next path="Import"/>
+          {{ $gettext('Import') }}
         </router-link>
         <router-link v-if="showImports" class="ui item" to="/library/import/batches">
-          <i18next path="Import batches"/>
+          {{ $gettext('Import batches') }}
         </router-link>
       </div>
     </div>
diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue
index 794e3a13b602fa7908393dbf0cedefbf15239106..1413deae86c3605c5dd317e51f9bbf2601b3ecd9 100644
--- a/front/src/components/library/Radios.vue
+++ b/front/src/components/library/Radios.vue
@@ -1,19 +1,21 @@
 <template>
   <div v-title="'Radios'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header"><i18next path="Browsing radios"/></h2>
+      <h2 class="ui header">
+        {{ $gettext('Browsing radios') }}
+      </h2>
       <router-link class="ui green basic button" to="/library/radios/build" exact>
-        <i18next path="Create your own radio"/>
+        {{ $gettext('Create your own radio') }}
       </router-link>
       <div class="ui hidden divider"></div>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <i18next tag="label" path="Search"/>
+            <label>{{ $gettext('Search') }}</label>
             <input type="text" v-model="query" placeholder="Enter a radio name..."/>
           </div>
           <div class="field">
-            <i18next tag="label" path="Ordering"/>
+            <label>{{ $gettext('Ordering') }}</label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -21,14 +23,18 @@
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Ordering direction"/>
+            <label>{{ $gettext('Ordering direction') }}</label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+"><i18next path="Ascending"/></option>
-              <option value="-"><i18next path="Descending"/></option>
+              <option value="+">
+                {{ $gettext('Ascending') }}
+              </option>
+              <option value="-">
+                {{ $gettext('Descending') }}
+              </option>
             </select>
           </div>
           <div class="field">
-            <i18next tag="label" path="Results per page"/>
+            <label>{{ $gettext('Results per page') }}</label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
diff --git a/front/src/components/library/Track.vue b/front/src/components/library/Track.vue
index af364e94d6af39e4d459e1cbdc9ab105560d2f5f..04f449044667a2f19dd575de1adc7338b63fc512 100644
--- a/front/src/components/library/Track.vue
+++ b/front/src/components/library/Track.vue
@@ -11,18 +11,25 @@
             <div class="content">
               {{ track.title }}
               <div class="sub header">
-                <i18next path="From album {%0%} by {%1%}">
-                  <router-link :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
-                    {{ track.album.title }}
-                  </router-link><router-link :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
-                    {{ track.artist.name }}
-                  </router-link>
-                </i18next>
+                <translate :translate-params="{album: track.album.title, artist: track.artist.name}">
+                  From album %{ album } by %{ artist }
+                </translate>
+              </div>
+              <br>
+              <div class="ui basic buttons">
+                <router-link class="ui button" :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
+                  {{ $gettext('Album page') }}
+                </router-link>
+                <router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
+                  {{ $gettext('Artist page') }}
+                </router-link>
               </div>
             </div>
           </h2>
 
-          <play-button class="orange" :track="track"><i18next path="Play"/></play-button>
+          <play-button class="orange" :track="track">
+            {{ $gettext('Play') }}
+          </play-button>
           <track-favorite-icon :track="track" :button="true"></track-favorite-icon>
           <track-playlist-icon
             :button="true"
@@ -31,70 +38,72 @@
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            <i18next path="Search on Wikipedia"/>
+            {{ $gettext('Search on Wikipedia') }}
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            <i18next path="View on MusicBrainz"/>
+            {{ $gettext('View on MusicBrainz') }}
           </a>
           <a v-if="downloadUrl" :href="downloadUrl" target="_blank" class="ui button">
             <i class="download icon"></i>
-            <i18next path="Download"/>
+            {{ $gettext('Download') }}
           </a>
         </div>
       </div>
       <div v-if="file" class="ui vertical stripe center aligned segment">
-        <h2 class="ui header">{{ $t('Track information') }}</h2>
+        <h2 class="ui header">{{ $gettext('Track information') }}</h2>
         <table class="ui very basic collapsing celled center aligned table">
           <tbody>
             <tr>
               <td>
-                {{ $t('Duration') }}
+                {{ $gettext('Duration') }}
               </td>
               <td v-if="file.duration">
                 {{ time.parse(file.duration) }}
               </td>
               <td v-else>
-                {{ $t('N/A') }}
+                {{ $gettext('N/A') }}
               </td>
             </tr>
             <tr>
               <td>
-                {{ $t('Size') }}
+                {{ $gettext('Size') }}
               </td>
               <td v-if="file.size">
                 {{ file.size | humanSize }}
               </td>
               <td v-else>
-                {{ $t('N/A') }}
+                {{ $gettext('N/A') }}
               </td>
             </tr>
             <tr>
               <td>
-                {{ $t('Bitrate') }}
+                {{ $gettext('Bitrate') }}
               </td>
               <td v-if="file.bitrate">
                 {{ file.bitrate | humanSize }}/s
               </td>
               <td v-else>
-                {{ $t('N/A') }}
+                {{ $gettext('N/A') }}
               </td>
             </tr>
           </tbody>
         </table>
       </div>
       <div class="ui vertical stripe center aligned segment">
-        <h2><i18next path="Lyrics"/></h2>
+        <h2>
+          {{ $gettext('Lyrics') }}
+        </h2>
         <div v-if="isLoadingLyrics" class="ui vertical segment">
           <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
         </div>
         <div v-if="lyrics" v-html="lyrics.content_rendered">
         </div>
         <template v-if="!isLoadingLyrics & !lyrics">
-          <i18next tag="p" path="No lyrics available for this track."/>
+          <p>{{ $gettext('No lyrics available for this track.') }}</p>
           <a class="ui button" target="_blank" :href="lyricsSearchUrl">
             <i class="search icon"></i>
-            <i18next path="Search on lyrics.wikia.com"/>
+            {{ $gettext('Search on lyrics.wikia.com') }}
           </a>
         </template>
       </div>
diff --git a/front/src/components/library/import/ArtistImport.vue b/front/src/components/library/import/ArtistImport.vue
index b24be6dc831af6ad3b417aef6cce9a65907e0137..0db28eb878ea9daffae4c8755acae96e0a86e2dd 100644
--- a/front/src/components/library/import/ArtistImport.vue
+++ b/front/src/components/library/import/ArtistImport.vue
@@ -4,7 +4,9 @@
       <a :href="getMusicbrainzUrl('artist', metadata.id)" target="_blank" title="View on MusicBrainz">{{ metadata.name }}</a>
     </h3>
     <form class="ui form" @submit.prevent="">
-      <h6 class="ui header"><i18next path="Filter album types"/></h6>
+      <h6 class="ui header">
+        {{ $gettext('Filter album types') }}
+      </h6>
       <div class="inline fields">
         <div class="field" v-for="t in availableReleaseTypes">
           <div class="ui checkbox">
@@ -13,7 +15,7 @@
           </div>
         </div>
         <div class="field">
-          <i18next tag="label" path="Query template"/>
+          <label>{{ $gettext('Query template') }}</label>
           <input v-model="customQueryTemplate" />
         </div>
       </div>
diff --git a/front/src/components/library/import/BatchDetail.vue b/front/src/components/library/import/BatchDetail.vue
index f0e6502f02dfbb4cde87a5ea87e6e5925d1400bf..2390ea7015b30d4e98e14e9db7d5860cfcdf7c49 100644
--- a/front/src/components/library/import/BatchDetail.vue
+++ b/front/src/components/library/import/BatchDetail.vue
@@ -8,7 +8,7 @@
         <tbody>
           <tr>
             <td>
-              <strong>{{ $t('Import batch') }}</strong>
+              <strong>{{ $gettext('Import batch') }}</strong>
             </td>
             <td>
               #{{ batch.id }}
@@ -16,7 +16,7 @@
           </tr>
           <tr>
             <td>
-              <strong>{{ $t('Launch date') }}</strong>
+              <strong>{{ $gettext('Launch date') }}</strong>
             </td>
             <td>
               <human-date :date="batch.creation_date"></human-date>
@@ -24,22 +24,22 @@
           </tr>
           <tr v-if="batch.user">
             <td>
-              <strong>{{ $t('Submitted by') }}</strong>
+              <strong>{{ $gettext('Submitted by') }}</strong>
             </td>
             <td>
               <username :username="batch.user.username" />
             </td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $t('Pending') }}</strong></td>
+            <td><strong>{{ $gettext('Pending') }}</strong></td>
             <td>{{ stats.pending }}</td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $t('Skipped') }}</strong></td>
+            <td><strong>{{ $gettext('Skipped') }}</strong></td>
             <td>{{ stats.skipped }}</td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $t('Errored') }}</strong></td>
+            <td><strong>{{ $gettext('Errored') }}</strong></td>
             <td>
               {{ stats.errored }}
               <button
@@ -47,12 +47,12 @@
                 v-if="stats.errored > 0"
                 class="ui tiny basic icon button">
                 <i class="redo icon" />
-                {{ $t('Rerun errored jobs')}}
+                {{ $gettext('Rerun errored jobs')}}
               </button>
             </td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $t('Finished') }}</strong></td>
+            <td><strong>{{ $gettext('Finished') }}</strong></td>
             <td>{{ stats.finished }}/{{ stats.count}}</td>
           </tr>
         </tbody>
@@ -60,17 +60,17 @@
       <div class="ui inline form">
         <div class="fields">
           <div class="ui field">
-            <label>{{ $t('Search') }}</label>
+            <label>{{ $gettext('Search') }}</label>
             <input type="text" v-model="jobFilters.search" placeholder="Search by source..." />
           </div>
           <div class="ui field">
-            <label>{{ $t('Status') }}</label>
+            <label>{{ $gettext('Status') }}</label>
             <select class="ui dropdown" v-model="jobFilters.status">
-              <option :value="null">{{ $t('Any') }}</option>
-              <option :value="'pending'">{{ $t('Pending') }}</option>
-              <option :value="'errored'">{{ $t('Errored') }}</option>
-              <option :value="'finished'">{{ $t('Success') }}</option>
-              <option :value="'skipped'">{{ $t('Skipped') }}</option>
+              <option :value="null">{{ $gettext('Any') }}</option>
+              <option :value="'pending'">{{ $gettext('Pending') }}</option>
+              <option :value="'errored'">{{ $gettext('Errored') }}</option>
+              <option :value="'finished'">{{ $gettext('Success') }}</option>
+              <option :value="'skipped'">{{ $gettext('Skipped') }}</option>
             </select>
           </div>
         </div>
@@ -78,11 +78,11 @@
       <table v-if="jobResult" class="ui unstackable table">
         <thead>
           <tr>
-            <th>{{ $t('Job ID') }}</th>
-            <th>{{ $t('Recording MusicBrainz ID') }}</th>
-            <th>{{ $t('Source') }}</th>
-            <th>{{ $t('Status') }}</th>
-            <th>{{ $t('Track') }}</th>
+            <th>{{ $gettext('Job ID') }}</th>
+            <th>{{ $gettext('Recording MusicBrainz ID') }}</th>
+            <th>{{ $gettext('Source') }}</th>
+            <th>{{ $gettext('Status') }}</th>
+            <th>{{ $gettext('Track') }}</th>
           </tr>
         </thead>
         <tbody>
@@ -103,7 +103,7 @@
                 <button
                   @click="rerun({batches: [], jobs: [job.id]})"
                   v-if="job.status === 'errored'"
-                  :title="$t('Rerun job')"
+                  :title="$gettext('Rerun job')"
                   class="ui tiny basic icon button">
                   <i class="redo icon" />
                 </button>
@@ -126,7 +126,10 @@
               ></pagination>
             </th>
             <th v-if="jobResult && jobResult.results.length > 0">
-              {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((jobFilters.page-1) * jobFilters.paginateBy) + 1 , end: ((jobFilters.page-1) * jobFilters.paginateBy) + jobResult.results.length, total: jobResult.count})}}
+              <translate
+                :translate-params="{start: ((jobFilters.page-1) * jobFilters.paginateBy) + 1, end: ((jobFilters.page-1) * jobFilters.paginateBy) + jobResult.results.length, total: jobResult.count}">
+                Showing results %{ start }-%{ end } on %{ total }
+              </translate>
             <th>
             <th></th>
             <th></th>
diff --git a/front/src/components/library/import/BatchList.vue b/front/src/components/library/import/BatchList.vue
index bf5a0ca47599e79b56559563aca8291fb3aff0e4..bbc553e12a8bc0d5d204d6f4f45fe9844163e901 100644
--- a/front/src/components/library/import/BatchList.vue
+++ b/front/src/components/library/import/BatchList.vue
@@ -5,25 +5,25 @@
       <div class="ui inline form">
         <div class="fields">
           <div class="ui field">
-            <label>{{ $t('Search') }}</label>
+            <label>{{ $gettext('Search') }}</label>
             <input type="text" v-model="filters.search" placeholder="Search by submitter, source..." />
           </div>
           <div class="ui field">
-            <label>{{ $t('Status') }}</label>
+            <label>{{ $gettext('Status') }}</label>
             <select class="ui dropdown" v-model="filters.status">
-              <option :value="null">{{ $t('Any') }}</option>
-              <option :value="'pending'">{{ $t('Pending') }}</option>
-              <option :value="'errored'">{{ $t('Errored') }}</option>
-              <option :value="'finished'">{{ $t('Success') }}</option>
+              <option :value="null">{{ $gettext('Any') }}</option>
+              <option :value="'pending'">{{ $gettext('Pending') }}</option>
+              <option :value="'errored'">{{ $gettext('Errored') }}</option>
+              <option :value="'finished'">{{ $gettext('Success') }}</option>
             </select>
           </div>
           <div class="ui field">
-            <label>{{ $t('Import source') }}</label>
+            <label>{{ $gettext('Import source') }}</label>
             <select class="ui dropdown" v-model="filters.source">
-              <option :value="null">{{ $t('Any') }}</option>
-              <option :value="'shell'">{{ $t('CLI') }}</option>
-              <option :value="'api'">{{ $t('API') }}</option>
-              <option :value="'federation'">{{ $t('Federation') }}</option>
+              <option :value="null">{{ $gettext('Any') }}</option>
+              <option :value="'shell'">{{ $gettext('CLI') }}</option>
+              <option :value="'api'">{{ $gettext('API') }}</option>
+              <option :value="'federation'">{{ $gettext('Federation') }}</option>
             </select>
           </div>
         </div>
@@ -32,12 +32,12 @@
       <table v-if="result && result.results.length > 0" class="ui unstackable table">
         <thead>
           <tr>
-            <th>{{ $t('ID') }}</th>
-            <th>{{ $t('Launch date') }}</th>
-            <th>{{ $t('Jobs') }}</th>
-            <th>{{ $t('Status') }}</th>
-            <th>{{ $t('Source') }}</th>
-            <th>{{ $t('Submitted by') }}</th>
+            <th>{{ $gettext('ID') }}</th>
+            <th>{{ $gettext('Launch date') }}</th>
+            <th>{{ $gettext('Jobs') }}</th>
+            <th>{{ $gettext('Status') }}</th>
+            <th>{{ $gettext('Source') }}</th>
+            <th>{{ $gettext('Submitted by') }}</th>
           </tr>
         </thead>
         <tbody>
@@ -71,7 +71,10 @@
               ></pagination>
             </th>
             <th v-if="result && result.results.length > 0">
-              {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((filters.page-1) * filters.paginateBy) + 1 , end: ((filters.page-1) * filters.paginateBy) + result.results.length, total: result.count})}}
+              <translate
+                :translate-params="{start: ((filters.page-1) * filters.paginateBy) + 1, end: ((filters.page-1) * filters.paginateBy) + result.results.length, total: result.count}">
+                Showing results %{ start }-%{ end } on %{ total }
+              </translate>
             <th>
             <th></th>
             <th></th>
diff --git a/front/src/components/library/import/FileUpload.vue b/front/src/components/library/import/FileUpload.vue
index fb88c006d3a53a33116d64a1044ee1c0b7802dac..635fd9bfe91a2418dec1df82a9256e773c4e58c6 100644
--- a/front/src/components/library/import/FileUpload.vue
+++ b/front/src/components/library/import/FileUpload.vue
@@ -2,8 +2,8 @@
   <div>
     <div v-if="batch" class="ui container">
       <div class="ui message">
-        {{ $t('Ensure your music files are properly tagged before uploading them.') }}
-        <a href="http://picard.musicbrainz.org/" target='_blank'>{{ $t('We recommend using Picard for that purpose.') }}</a>
+        {{ $gettext('Ensure your music files are properly tagged before uploading them.') }}
+        <a href="http://picard.musicbrainz.org/" target='_blank'>{{ $gettext('We recommend using Picard for that purpose.') }}</a>
       </div>
       <file-upload-widget
         :class="['ui', 'icon', 'left', 'floated', 'button']"
@@ -20,31 +20,30 @@
         @input-file="inputFile"
         ref="upload">
         <i class="upload icon"></i>
-        <i18next path="Select files to upload..."/>
+        {{ $gettext('Select files to upload...') }}
     </file-upload-widget>
       <button
         :class="['ui', 'right', 'floated', 'icon', {disabled: files.length === 0}, 'button']"
         v-if="!$refs.upload || !$refs.upload.active" @click.prevent="startUpload()">
         <i class="play icon" aria-hidden="true"></i>
-        <i18next path="Start Upload"/>
+        {{ $gettext('Start Upload') }}
       </button>
       <button type="button" class="ui right floated icon yellow button" v-else @click.prevent="$refs.upload.active = false">
         <i class="pause icon" aria-hidden="true"></i>
-        <i18next path="Stop Upload"/>
+        {{ $gettext('Stop Upload') }}
       </button>
     </div>
     <div class="ui hidden clearing divider"></div>
-    <i18next v-if="batch" path="Once all your files are uploaded, simply head over {%0%} to check the import status.">
-      <router-link :to="{name: 'library.import.batches.detail', params: {id: batch.id }}">
-        <i18next path="import detail page"/>
-      </router-link>
-    </i18next>
+    <template v-if="batch">{{ $gettext('Once all your files are uploaded, simply click the following button to check the import status.') }}</template>
+    <router-link class="ui basic button" v-if="batch" :to="{name: 'library.import.batches.detail', params: {id: batch.id }}">
+      {{ $gettext('Import detail page') }}
+    </router-link>
     <table class="ui single line table">
       <thead>
         <tr>
-          <i18next tag="th" path="File name"/>
-          <i18next tag="th" path="Size"/>
-          <i18next tag="th" path="Status"/>
+          <th>{{ $gettext('File name') }}</th>
+          <th>{{ $gettext('Size') }}</th>
+          <th>{{ $gettext('Status') }}</th>
         </tr>
       </thead>
       <tbody>
@@ -55,10 +54,10 @@
             <span v-if="file.error" class="ui red label">
               {{ file.error }}
             </span>
-            <i18next v-else-if="file.success" class="ui green label" path="Success"/>
-            <i18next v-else-if="file.active" class="ui yellow label" path="Uploading..."/>
+            <span v-else-if="file.success" class="ui green label">{{ $gettext('Success') }}</span>
+            <span v-else-if="file.active" class="ui yellow label">{{ $gettext('Uploading...') }}</span>
             <template v-else>
-              <i18next class="ui label" path="Pending"/>
+              <span class="ui label">{{ $gettext('Pending') }}</span>
               <button class="ui tiny basic red icon button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
             </template>
           </td>
diff --git a/front/src/components/library/import/Main.vue b/front/src/components/library/import/Main.vue
index eac1239a836a60d4393545253db392dc2b9ac12b..9482df3a10792222626917d910f103c4b49df80c 100644
--- a/front/src/components/library/import/Main.vue
+++ b/front/src/components/library/import/Main.vue
@@ -4,34 +4,44 @@
       <div class="ui top three attached ordered steps">
         <a @click="currentStep = 0" :class="['step', {'active': currentStep === 0}, {'completed': currentStep > 0}]">
           <div class="content">
-            <i18next tag="div" class="title" path="Import source"/>
-            <i18next tag="div" class="description" path="Uploaded files or external source"/>
+            <div class="title">{{ $gettext('Import source') }}</div>
+            <div class="description">{{ $gettext('Uploaded files or external source') }}</div>
           </div>
         </a>
         <a @click="currentStep = 1" :class="['step', {'active': currentStep === 1}, {'completed': currentStep > 1}]">
           <div class="content">
-            <i18next tag="div" class="title" path="Metadata"/>
-            <i18next tag="div" class="description" path="Grab corresponding metadata"/>
+            <div class="title">{{ $gettext('Metadata') }}</div>
+            <div class="description">{{ $gettext('Grab corresponding metadata') }}</div>
           </div>
         </a>
         <a @click="currentStep = 2" :class="['step', {'active': currentStep === 2}, {'completed': currentStep > 2}]">
           <div class="content">
-            <i18next tag="div" class="title" path="Music"/>
-            <i18next tag="div" class="description" path="Select relevant sources or files for import"/>
+            <div class="title">{{ $gettext('Music') }}</div>
+            <div class="description">{{ $gettext('Select relevant sources or files for import') }}</div>
           </div>
         </a>
       </div>
       <div class="ui hidden divider"></div>
       <div class="ui centered buttons">
-        <button @click="currentStep -= 1" :disabled="currentStep === 0" class="ui icon button"><i class="left arrow icon"></i><i18next path="Previous step"/></button>
-        <button @click="nextStep()" v-if="currentStep < 2" class="ui icon button"><i18next path="Next step"/><i class="right arrow icon"></i></button>
+        <button @click="currentStep -= 1" :disabled="currentStep === 0" class="ui icon button"><i class="left arrow icon"></i>
+          {{ $gettext('Previous step') }}
+        </button>
+        <button @click="nextStep()" v-if="currentStep < 2" class="ui icon button">
+          {{ $gettext('Next step') }}
+          <i class="right arrow icon"></i>
+        </button>
         <button
           @click="$refs.import.launchImport()"
           v-if="currentStep === 2 && currentSource != 'upload'"
           :class="['ui', 'positive', 'icon', {'loading': isImporting}, 'button']"
           :disabled="isImporting || importData.count === 0"
           >
-            <i18next path="Import {%0%} tracks">{{ importData.count }}</i18next>
+            <translate
+              :translate-params="{count: importData.count || 0}"
+              :translate-n="importData.count || 0"
+              translate-plural="Import %{ count } tracks">
+              Import %{ count } track
+            </translate>
             <i class="check icon"></i>
           </button>
         <button
@@ -40,20 +50,20 @@
           :class="['ui', 'positive', 'icon', {'disabled': !importBatch}, 'button']"
           :disabled="!importBatch"
           >
-            {{ $t('Finish import' )}}
+            {{ $gettext('Finish import' )}}
             <i class="check icon"></i>
           </button>
       </div>
       <div class="ui hidden divider"></div>
       <div class="ui attached segment">
         <template v-if="currentStep === 0">
-          <i18next tag="p" path="First, choose where you want to import the music from"/>
+          <p>{{ $gettext('First, choose where you want to import the music from') }}</p>
           <form class="ui form">
             <div class="field">
               <div class="ui radio checkbox">
                 <input type="radio" id="external" value="external" v-model="currentSource">
                 <label for="external">
-                  <i18next path="External source. Supported backends"/>
+                  {{ $gettext('External source. Supported backends') }}
                   <div v-for="backend in backends" class="ui basic label">
                     <i v-if="backend.icon" :class="[backend.icon, 'icon']"></i>
                     {{ backend.label }}
@@ -64,7 +74,7 @@
             <div class="field">
               <div class="ui radio checkbox">
                 <input type="radio" id="upload" value="upload" v-model="currentSource">
-                <i18next tag="label" for="upload" path="File upload" />
+                <label for="upload">{{ $gettext('File upload') }}</label>
               </div>
             </div>
           </form>
@@ -73,7 +83,7 @@
           <div class="column">
             <form class="ui form" @submit.prevent="">
               <div class="field">
-                <i18next tag="label" path="Search an entity you want to import:"/>
+                <label>{{ $gettext('Search an entity you want to import:') }}</label>
                 <metadata-search
                   :mb-type="mbType"
                   :mb-id="mbId"
@@ -81,29 +91,35 @@
                   @type-changed="updateType"></metadata-search>
               </div>
             </form>
-            <i18next tag="div" class="ui horizontal divider" path="Or"/>
+            <div class="ui horizontal divider">{{ $gettext('Or') }}</div>
             <form class="ui form" @submit.prevent="">
               <div class="field">
-                <i18next tag="label" path="Input a MusicBrainz ID manually:"/>
+                <label>{{ $gettext('Input a MusicBrainz ID manually:') }}</label>
                 <input type="text" v-model="currentId" />
               </div>
             </form>
             <div class="ui hidden divider"></div>
             <template v-if="currentType && currentId">
-              <h4 class="ui header"><i18next path="You will import:"/></h4>
+              <h4 class="ui header">
+                {{ $gettext('You will import:') }}
+              </h4>
               <component
                 :mbId="currentId"
                 :is="metadataComponent"
                 @metadata-changed="this.updateMetadata"
                 ></component>
             </template>
-            <i18next tag="p" path="You can also skip this step and enter metadata manually."/>
+            <p>{{ $gettext('You can also skip this step and enter metadata manually.') }}</p>
           </div>
           <div class="column">
             <h5 class="ui header">What is metadata?</h5>
-            <i18next tag="p" path="Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the {%0%} project, which you can think about as the Wikipedia of music.">
-              <a href="http://musicbrainz.org/" target="_blank">MusicBrainz</a>
-            </i18next>
+            <template v-translate>
+              Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the
+              <a href="https://musicbrainz.org" target="_blank">
+                MusicBrainz
+              </a>
+              project, which you can think about as the Wikipedia of music.
+            </template>
           </div>
         </div>
         <div v-if="currentStep === 2">
@@ -128,8 +144,10 @@
       </div>
     </div>
     <div class="ui vertical stripe segment" v-if="currentRequest">
-      <h3 class="ui header"><i18next path="Music request"/></h3>
-      <i18next tag="p" path="This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."/>
+      <h3 class="ui header">
+        {{ $gettext('Music request') }}
+      </h3>
+      <p>{{ $gettext('This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled.') }}</p>
       <request-card :request="currentRequest" :import-action="false"></request-card>
 
     </div>
diff --git a/front/src/components/library/import/ReleaseImport.vue b/front/src/components/library/import/ReleaseImport.vue
index 4b792b3921a2fa091c4db82962e24e0d93258bb4..c351088f3a76d8d5ce59bf05d645d974dd700355 100644
--- a/front/src/components/library/import/ReleaseImport.vue
+++ b/front/src/components/library/import/ReleaseImport.vue
@@ -1,16 +1,18 @@
 <template>
   <div>
     <h3 class="ui dividing block header">
-      <i18next path="Album {%0%} ({%1%} tracks) by {%2%}">
-        <a :href="getMusicbrainzUrl('release', metadata.id)" target="_blank" title="View on MusicBrainz">{{ metadata.title }}</a>
-        ({{ tracks.length}} tracks)
-        <a :href="getMusicbrainzUrl('artist', metadata['artist-credit'][0]['artist']['id'])" target="_blank" title="View on MusicBrainz">{{ metadata['artist-credit-phrase'] }}</a>
-      </i18next>
+      <translate
+        tag="div"
+        translate-plural="Album %{ title } (%{ count } tracks) by %{ artist }"
+        :translate-n="tracks.length"
+        :translate-params="{count: tracks.length, title: metadata.title, artist: metadata['artist-credit-phrase']}">
+        Album %{ title } (%{ count } track) by %{ artist }
+      </translate>
       <div class="ui divider"></div>
       <div class="sub header">
         <div class="ui toggle checkbox">
           <input type="checkbox" v-model="enabled" />
-          <i18next tag="label" path="Import this release"/>
+          <label>{{ $gettext('Import this release') }}</label>
         </div>
       </div>
     </h3>
diff --git a/front/src/components/library/import/TrackImport.vue b/front/src/components/library/import/TrackImport.vue
index 7f07763bf3e260d69344efeef6bd2f4e5453453b..1b7378b1a94dd528b66e0d6b1172bca8182af84d 100644
--- a/front/src/components/library/import/TrackImport.vue
+++ b/front/src/components/library/import/TrackImport.vue
@@ -9,13 +9,13 @@
       </h5>
       <div class="ui toggle checkbox">
         <input type="checkbox" v-model="enabled" />
-        <i18next tag="label" path="Import this track"/>
+        <label>{{ $gettext('Import this track') }}</label>
       </div>
     </div>
     <div class="three wide column" v-if="enabled">
       <form class="ui mini form" @submit.prevent="">
         <div class="field">
-          <i18next tag="label" path="Source"/>
+          <label>{{ $gettext('Source') }}</label>
           <select v-model="currentBackendId">
             <option v-for="backend in backends" :value="backend.id">
               {{ backend.label }}
@@ -28,10 +28,10 @@
         <button @click="currentResultIndex -= 1" class="ui basic tiny icon button" :disabled="currentResultIndex === 0">
           <i class="left arrow icon"></i>
         </button>
-        <i18next path="Result {%0%}/{%1%}">
-          {{ currentResultIndex + 1 }}
-          {{ results.length }}
-        </i18next>
+        {{ results.total }}
+        <translate :translate-params="{current: currentResultIndex + 1, total: results.length}">
+          Result %{ current }/%{ total }
+        </translate>
         <button @click="currentResultIndex += 1" class="ui basic tiny icon button" :disabled="currentResultIndex + 1 === results.length">
           <i class="right arrow icon"></i>
         </button>
@@ -40,9 +40,9 @@
     <div class="four wide column" v-if="enabled">
       <form class="ui mini form" @submit.prevent="">
         <div class="field">
-          <i18next tag="label" path="Search query"/>
+          <label>{{ $gettext('Search query') }}</label>
           <input type="text" v-model="query" />
-          <i18next tag="label" path="Imported URL"/>
+          <label>{{ $gettext('Imported URL') }}</label>
           <input type="text" v-model="importedUrl" />
         </div>
       </form>
diff --git a/front/src/components/library/radios/Builder.vue b/front/src/components/library/radios/Builder.vue
index 6c81f532f2a9478ca7a66ecfbeb255e103ba2c09..6dadcc9be05fb1fbbf767b65d6167dfe7673b118 100644
--- a/front/src/components/library/radios/Builder.vue
+++ b/front/src/components/library/radios/Builder.vue
@@ -2,30 +2,40 @@
   <div class="ui vertical stripe segment" v-title="'Radio Builder'">
     <div>
       <div>
-        <h2 class="ui header"><i18next path="Builder"/></h2>
-        <i18next tag="p" path="You can use this interface to build your own custom radio, which will play tracks according to your criteria"/>
+        <h2 class="ui header">
+          {{ $gettext('Builder') }}
+        </h2>
+        <p>{{ $gettext('You can use this interface to build your own custom radio, which will play tracks according to your criteria.') }}</p>
           <div class="ui form">
           <div class="inline fields">
             <div class="field">
-              <i18next tag="label" for="name" path="Radio name"/>
+              <label for="name">{{ $gettext('Radio name') }}</label>
               <input id="name" type="text" v-model="radioName" placeholder="My awesome radio" />
             </div>
             <div class="field">
               <input id="public" type="checkbox" v-model="isPublic" />
-              <i18next tag="label" for="public" path="Display publicly"/>
+              <label for="public">{{ $gettext('Display publicly') }}</label>
             </div>
-            <button :disabled="!canSave" @click="save" class="ui green button"><i18next path="Save"/></button>
+            <button :disabled="!canSave" @click="save" class="ui green button">
+              {{ $gettext('Save') }}
+            </button>
             <radio-button v-if="id" type="custom" :custom-radio-id="id"></radio-button>
           </div>
         </div>
         <div class="ui form">
-          <p><i18next path="Add filters to customize your radio"/></p>
+          <p>
+            {{ $gettext('Add filters to customize your radio') }}
+          </p>
           <div class="inline field">
             <select class="ui dropdown" v-model="currentFilterType">
-              <option value=""><i18next path="Select a filter"/></option>
+              <option value="">
+                {{ $gettext('Select a filter') }}
+              </option>
               <option v-for="f in availableFilters" :value="f.type">{{ f.label }}</option>
             </select>
-            <button :disabled="!currentFilterType" @click="add" class="ui button"><i18next path="Add filter"/></button>
+            <button :disabled="!currentFilterType" @click="add" class="ui button">
+              {{ $gettext('Add filter') }}
+            </button>
           </div>
           <p v-if="currentFilter">
             {{ currentFilter.help_text }}
@@ -34,11 +44,11 @@
         <table class="ui table">
           <thead>
             <tr>
-              <i18next tag="th" class="two wide" path="Filter name"/>
-              <i18next tag="th" class="one wide" path="Exclude"/>
-              <i18next tag="th" class="six wide" path="Config"/>
-              <i18next tag="th" class="five wide" path="Candidates"/>
-              <i18next tag="th" class="two wide" path="Actions"/>
+              <th class="two wide">{{ $gettext('Filter name') }}</th>
+              <th class="one wide">{{ $gettext('Exclude') }}</th>
+              <th class="six wide">{{ $gettext('Config') }}</th>
+              <th class="five wide">{{ $gettext('Candidates') }}</th>
+              <th class="two wide">{{ $gettext('Actions') }}</th>
             </tr>
           </thead>
           <tbody>
@@ -54,9 +64,13 @@
           </tbody>
         </table>
         <template v-if="checkResult">
-          <i18next tag="h3" class="ui header" path="{%0%} tracks matching combined filters">
-            {{ checkResult.candidates.count }}
-          </i18next>
+          <h3
+            class="ui header"
+            v-translate="{count: checkResult.candidates.count}"
+            :translate-n="checkResult.candidates.count"
+            translate-plural="%{ count } tracks matching combined filters">
+            %{ count } track matching combined filters
+          </h3>
           <track-table v-if="checkResult.candidates.sample" :tracks="checkResult.candidates.sample"></track-table>
         </template>
       </div>
diff --git a/front/src/components/library/radios/Filter.vue b/front/src/components/library/radios/Filter.vue
index 0d268dc60faad8355650aed431ad4174d22ce464..9c31b59b0bbabb9107c5ce9e82919997cad84d21 100644
--- a/front/src/components/library/radios/Filter.vue
+++ b/front/src/components/library/radios/Filter.vue
@@ -42,7 +42,7 @@
       </span>
       <modal v-if="checkResult" :show.sync="showCandidadesModal">
         <div class="header">
-          <i18next path="Track matching filter"/>
+          {{ $gettext('Track matching filter') }}
         </div>
         <div class="content">
           <div class="description">
@@ -51,13 +51,13 @@
         </div>
         <div class="actions">
           <div class="ui black deny button">
-            <i18next path="Cancel"/>
+            {{ $gettext('Cancel') }}
           </div>
         </div>
       </modal>
     </td>
     <td>
-      <button @click="$emit('delete', index)" class="ui basic red button"><i18next path="Remove"/></button>
+      <button @click="$emit('delete', index)" class="ui basic red button">{{ $gettext('Remove') }}</button>
     </td>
   </tr>
 </template>
diff --git a/front/src/components/manage/library/FilesTable.vue b/front/src/components/manage/library/FilesTable.vue
index 2788006f4a4b1c63ca3e5ff8445cad38f2ef7880..604c8fc384a120e0d7f3daf61aeba6744b4c34a4 100644
--- a/front/src/components/manage/library/FilesTable.vue
+++ b/front/src/components/manage/library/FilesTable.vue
@@ -3,11 +3,11 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $t('Search') }}</label>
+          <label>{{ $gettext('Search') }}</label>
           <input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering"/>
+          <label>{{ $gettext('Ordering') }}</label>
           <select class="ui dropdown" v-model="ordering">
             <option v-for="option in orderingOptions" :value="option[0]">
               {{ option[1] }}
@@ -15,7 +15,7 @@
           </select>
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering direction"/>
+          <label>{{ $gettext('Ordering direction') }}</label>
           <select class="ui dropdown" v-model="orderingDirection">
             <option value="+">Ascending</option>
             <option value="-">Descending</option>
@@ -35,14 +35,14 @@
         :action-url="'manage/library/track-files/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $t('Title') }}</th>
-          <th>{{ $t('Artist') }}</th>
-          <th>{{ $t('Album') }}</th>
-          <th>{{ $t('Import date') }}</th>
-          <th>{{ $t('Type') }}</th>
-          <th>{{ $t('Bitrate') }}</th>
-          <th>{{ $t('Duration') }}</th>
-          <th>{{ $t('Size') }}</th>
+          <th>{{ $gettext('Title') }}</th>
+          <th>{{ $gettext('Artist') }}</th>
+          <th>{{ $gettext('Album') }}</th>
+          <th>{{ $gettext('Import date') }}</th>
+          <th>{{ $gettext('Type') }}</th>
+          <th>{{ $gettext('Bitrate') }}</th>
+          <th>{{ $gettext('Duration') }}</th>
+          <th>{{ $gettext('Size') }}</th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
@@ -61,25 +61,25 @@
             {{ scope.obj.audio_mimetype }}
           </td>
           <td v-else>
-            {{ $t('N/A') }}
+            {{ $gettext('N/A') }}
           </td>
           <td v-if="scope.obj.bitrate">
             {{ scope.obj.bitrate | humanSize }}/s
           </td>
           <td v-else>
-            {{ $t('N/A') }}
+            {{ $gettext('N/A') }}
           </td>
           <td v-if="scope.obj.duration">
             {{ time.parse(scope.obj.duration) }}
           </td>
           <td v-else>
-            {{ $t('N/A') }}
+            {{ $gettext('N/A') }}
           </td>
           <td v-if="scope.obj.size">
             {{ scope.obj.size | humanSize }}
           </td>
           <td v-else>
-            {{ $t('N/A') }}
+            {{ $gettext('N/A') }}
           </td>
         </template>
       </action-table>
@@ -95,7 +95,10 @@
         ></pagination>
 
       <span v-if="result && result.results.length > 0">
-        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+        <translate
+          :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+          Showing results %{ start }-%{ end } on %{ total }
+        </translate>
       </span>
     </div>
   </div>
@@ -178,10 +181,11 @@ export default {
       }
     },
     actions () {
+      let msg = this.$gettext('Delete')
       return [
         {
           name: 'delete',
-          label: this.$t('Delete'),
+          label: msg,
           isDangerous: true
         }
       ]
diff --git a/front/src/components/manage/library/RequestsTable.vue b/front/src/components/manage/library/RequestsTable.vue
index e51b911a762a46504f4783f376e63ac4180c4ce8..7707afbf84cf92e06ac593f8b985467bc930f45c 100644
--- a/front/src/components/manage/library/RequestsTable.vue
+++ b/front/src/components/manage/library/RequestsTable.vue
@@ -3,11 +3,11 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $t('Search') }}</label>
+          <label>{{ $gettext('Search') }}</label>
           <input type="text" v-model="search" placeholder="Search by artist, username, comment..." />
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering"/>
+          <label>{{ $gettext('Ordering') }}</label>
           <select class="ui dropdown" v-model="ordering">
             <option v-for="option in orderingOptions" :value="option[0]">
               {{ option[1] }}
@@ -15,20 +15,20 @@
           </select>
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering direction"/>
+          <label>{{ $gettext('Ordering direction') }}</label>
           <select class="ui dropdown" v-model="orderingDirection">
             <option value="+">Ascending</option>
             <option value="-">Descending</option>
           </select>
         </div>
         <div class="field">
-          <label>{{ $t("Status") }}</label>
+          <label>{{ $gettext("Status") }}</label>
           <select class="ui dropdown" v-model="status">
-            <option :value="null">{{ $t('All') }}</option>
-            <option :value="'pending'">{{ $t('Pending') }}</option>
-            <option :value="'accepted'">{{ $t('Accepted') }}</option>
-            <option :value="'imported'">{{ $t('Imported') }}</option>
-            <option :value="'closed'">{{ $t('Closed') }}</option>
+            <option :value="null">{{ $gettext('All') }}</option>
+            <option :value="'pending'">{{ $gettext('Pending') }}</option>
+            <option :value="'accepted'">{{ $gettext('Accepted') }}</option>
+            <option :value="'imported'">{{ $gettext('Imported') }}</option>
+            <option :value="'closed'">{{ $gettext('Closed') }}</option>
           </select>
         </div>
       </div>
@@ -45,48 +45,48 @@
         :action-url="'manage/requests/import-requests/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $t('User') }}</th>
-          <th>{{ $t('Status') }}</th>
-          <th>{{ $t('Artist') }}</th>
-          <th>{{ $t('Albums') }}</th>
-          <th>{{ $t('Comment') }}</th>
-          <th>{{ $t('Creation date') }}</th>
-          <th>{{ $t('Import date') }}</th>
-          <th>{{ $t('Actions') }}</th>
+          <th>{{ $gettext('User') }}</th>
+          <th>{{ $gettext('Status') }}</th>
+          <th>{{ $gettext('Artist') }}</th>
+          <th>{{ $gettext('Albums') }}</th>
+          <th>{{ $gettext('Comment') }}</th>
+          <th>{{ $gettext('Creation date') }}</th>
+          <th>{{ $gettext('Import date') }}</th>
+          <th>{{ $gettext('Actions') }}</th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
             {{ scope.obj.user.username }}
           </td>
           <td>
-            <span class="ui green basic label" v-if="scope.obj.status === 'imported'">{{ $t('Imported') }}</span>
-            <span class="ui pink basic label" v-else-if="scope.obj.status === 'accepted'">{{ $t('Accepted') }}</span>
-            <span class="ui yellow basic label" v-else-if="scope.obj.status === 'pending'">{{ $t('Pending') }}</span>
-            <span class="ui red basic label" v-else-if="scope.obj.status === 'closed'">{{ $t('Closed') }}</span>
+            <span class="ui green basic label" v-if="scope.obj.status === 'imported'">{{ $gettext('Imported') }}</span>
+            <span class="ui pink basic label" v-else-if="scope.obj.status === 'accepted'">{{ $gettext('Accepted') }}</span>
+            <span class="ui yellow basic label" v-else-if="scope.obj.status === 'pending'">{{ $gettext('Pending') }}</span>
+            <span class="ui red basic label" v-else-if="scope.obj.status === 'closed'">{{ $gettext('Closed') }}</span>
           </td>
           <td>
             <span :title="scope.obj.artist_name">{{ scope.obj.artist_name|truncate(30) }}</span>
           </td>
           <td>
             <span v-if="scope.obj.albums" :title="scope.obj.albums">{{ scope.obj.albums|truncate(30) }}</span>
-            <template v-else>{{ $t('N/A') }}</template>
+            <template v-else>{{ $gettext('N/A') }}</template>
           </td>
           <td>
             <span v-if="scope.obj.comment" :title="scope.obj.comment">{{ scope.obj.comment|truncate(30) }}</span>
-            <template v-else>{{ $t('N/A') }}</template>
+            <template v-else>{{ $gettext('N/A') }}</template>
           </td>
           <td>
             <human-date :date="scope.obj.creation_date"></human-date>
           </td>
           <td>
             <human-date v-if="scope.obj.imported_date" :date="scope.obj.creation_date"></human-date>
-            <template v-else>{{ $t('N/A') }}</template>
+            <template v-else>{{ $gettext('N/A') }}</template>
           </td>
           <td>
             <router-link
               class="ui tiny basic button"
               :to="{name: 'library.import.launch', query: {request: scope.obj.id}}"
-              v-if="scope.obj.status === 'pending'">{{ $t('Create import') }}</router-link>
+              v-if="scope.obj.status === 'pending'">{{ $gettext('Create import') }}</router-link>
           </td>
         </template>
       </action-table>
@@ -102,7 +102,10 @@
         ></pagination>
 
       <span v-if="result && result.results.length > 0">
-        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+        <translate
+          :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+          Showing results %{ start }-%{ end } on %{ total }
+        </translate>
       </span>
     </div>
   </div>
@@ -183,21 +186,25 @@ export default {
       }
     },
     actions () {
+      // somehow, extraction fails otherwise
+      let deleteLabel = this.$gettext('Delete')
+      let markImportedLabel = this.$gettext('Mark as imported')
+      let markClosedLabel = this.$gettext('Mark as closed')
       return [
         {
           name: 'delete',
-          label: this.$t('Delete'),
+          label: deleteLabel,
           isDangerous: true
         },
         {
           name: 'mark_imported',
-          label: this.$t('Mark as imported'),
+          label: markImportedLabel,
           filterCheckable: (obj) => { return ['pending', 'accepted'].indexOf(obj.status) > -1 },
           isDangerous: true
         },
         {
           name: 'mark_closed',
-          label: this.$t('Mark as closed'),
+          label: markClosedLabel,
           filterCheckable: (obj) => { return ['pending', 'accepted'].indexOf(obj.status) > -1 },
           isDangerous: true
         }
diff --git a/front/src/components/manage/users/InvitationForm.vue b/front/src/components/manage/users/InvitationForm.vue
index d9f0969e67853c841cbb0a10ebd9735792723d97..28111d4e8e02ed14deaf1299430c3d9cf5b4f06c 100644
--- a/front/src/components/manage/users/InvitationForm.vue
+++ b/front/src/components/manage/users/InvitationForm.vue
@@ -2,19 +2,19 @@
   <div>
     <form class="ui form" @submit.prevent="submit">
       <div v-if="errors.length > 0" class="ui negative message">
-        <div class="header">{{ $t('Error while creating invitation') }}</div>
+        <div class="header">{{ $gettext('Error while creating invitation') }}</div>
         <ul class="list">
           <li v-for="error in errors">{{ error }}</li>
         </ul>
       </div>
       <div class="inline fields">
         <div class="ui field">
-          <label>{{ $t('Invitation code')}}</label>
-          <input type="text" v-model="code" :placeholder="$t('Leave empty for a random code')" />
+          <label>{{ $gettext('Invitation code')}}</label>
+          <input type="text" v-model="code" :placeholder="$gettext('Leave empty for a random code')" />
         </div>
         <div class="ui field">
           <button :class="['ui', {loading: isLoading}, 'button']" :disabled="isLoading" type="submit">
-            {{ $t('Get a new invitation') }}
+            {{ $gettext('Get a new invitation') }}
           </button>
         </div>
       </div>
@@ -24,8 +24,8 @@
       <table class="ui ui basic table">
         <thead>
           <tr>
-            <th>{{ $t('Code') }}</th>
-            <th>{{ $t('Share link') }}</th>
+            <th>{{ $gettext('Code') }}</th>
+            <th>{{ $gettext('Share link') }}</th>
           </tr>
         </thead>
         <tbody>
@@ -35,7 +35,7 @@
           </tr>
         </tbody>
       </table>
-      <button class="ui basic button" @click="invitations = []">{{ $t('Clear') }}</button>
+      <button class="ui basic button" @click="invitations = []">{{ $gettext('Clear') }}</button>
     </div>
   </div>
 </template>
diff --git a/front/src/components/manage/users/InvitationsTable.vue b/front/src/components/manage/users/InvitationsTable.vue
index e8d0a2406aaf465d7e30803b05ace126b621606d..ce34ddbceba455ce48b02b2b048c05cf4e53397a 100644
--- a/front/src/components/manage/users/InvitationsTable.vue
+++ b/front/src/components/manage/users/InvitationsTable.vue
@@ -3,11 +3,11 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $t('Search') }}</label>
+          <label>{{ $gettext('Search') }}</label>
           <input type="text" v-model="search" placeholder="Search by username, email, code..." />
         </div>
         <div class="field">
-          <label>{{ $t("Ordering") }}</label>
+          <label>{{ $gettext("Ordering") }}</label>
           <select class="ui dropdown" v-model="ordering">
             <option v-for="option in orderingOptions" :value="option[0]">
               {{ option[1] }}
@@ -15,11 +15,11 @@
           </select>
         </div>
         <div class="field">
-          <label>{{ $t("Status") }}</label>
+          <label>{{ $gettext("Status") }}</label>
           <select class="ui dropdown" v-model="isOpen">
-            <option :value="null">{{ $t('All') }}</option>
-            <option :value="true">{{ $t('Open') }}</option>
-            <option :value="false">{{ $t('Expired/used') }}</option>
+            <option :value="null">{{ $gettext('All') }}</option>
+            <option :value="true">{{ $gettext('Open') }}</option>
+            <option :value="false">{{ $gettext('Expired/used') }}</option>
           </select>
         </div>
       </div>
@@ -36,20 +36,20 @@
         :action-url="'manage/users/invitations/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $t('Owner') }}</th>
-          <th>{{ $t('Status') }}</th>
-          <th>{{ $t('Creation date') }}</th>
-          <th>{{ $t('Expiration date') }}</th>
-          <th>{{ $t('Code') }}</th>
+          <th>{{ $gettext('Owner') }}</th>
+          <th>{{ $gettext('Status') }}</th>
+          <th>{{ $gettext('Creation date') }}</th>
+          <th>{{ $gettext('Expiration date') }}</th>
+          <th>{{ $gettext('Code') }}</th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
             <router-link :to="{name: 'manage.users.users.detail', params: {id: scope.obj.id }}">{{ scope.obj.owner.username }}</router-link>
           </td>
           <td>
-            <span v-if="scope.obj.users.length > 0" class="ui green basic label">{{ $t('Used') }}</span>
-            <span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label">{{ $t('Expired') }}</span>
-            <span v-else class="ui basic label">{{ $t('Not used') }}</span>
+            <span v-if="scope.obj.users.length > 0" class="ui green basic label">{{ $gettext('Used') }}</span>
+            <span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label">{{ $gettext('Expired') }}</span>
+            <span v-else class="ui basic label">{{ $gettext('Not used') }}</span>
           </td>
           <td>
             <human-date :date="scope.obj.creation_date"></human-date>
@@ -74,7 +74,10 @@
         ></pagination>
 
       <span v-if="result && result.results.length > 0">
-        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+        <translate
+          :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+          Showing results %{ start }-%{ end } on %{ total }
+        </translate>
       </span>
     </div>
   </div>
@@ -155,10 +158,11 @@ export default {
       }
     },
     actions () {
+      let deleteLabel = this.$gettext('Delete')
       return [
         {
           name: 'delete',
-          label: this.$t('Delete'),
+          label: deleteLabel,
           filterCheckable: (obj) => {
             return obj.users.length === 0 && moment().isBefore(obj.expiration_date)
           }
diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue
index 855fbe2b5da53abf717dc819a5f2560c5e35bd54..af2e25671c81da962705ea94efb35eed7e80fb0c 100644
--- a/front/src/components/manage/users/UsersTable.vue
+++ b/front/src/components/manage/users/UsersTable.vue
@@ -3,11 +3,11 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $t('Search') }}</label>
+          <label>{{ $gettext('Search') }}</label>
           <input type="text" v-model="search" placeholder="Search by username, email, name..." />
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering"/>
+          <label>{{ $gettext('Ordering') }}</label>
           <select class="ui dropdown" v-model="ordering">
             <option v-for="option in orderingOptions" :value="option[0]">
               {{ option[1] }}
@@ -15,10 +15,10 @@
           </select>
         </div>
         <div class="field">
-          <i18next tag="label" path="Ordering direction"/>
+          <label>{{ $gettext('Ordering direction') }}</label>
           <select class="ui dropdown" v-model="orderingDirection">
-            <option value="+">{{ $t('Ascending') }}</option>
-            <option value="-">{{ $t('Descending') }}</option>
+            <option value="+">{{ $gettext('Ascending') }}</option>
+            <option value="-">{{ $gettext('Descending') }}</option>
           </select>
         </div>
       </div>
@@ -35,13 +35,13 @@
         :action-url="'manage/library/track-files/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $t('Username') }}</th>
-          <th>{{ $t('Email') }}</th>
-          <th>{{ $t('Account status') }}</th>
-          <th>{{ $t('Sign-up') }}</th>
-          <th>{{ $t('Last activity') }}</th>
-          <th>{{ $t('Permissions') }}</th>
-          <th>{{ $t('Status') }}</th>
+          <th>{{ $gettext('Username') }}</th>
+          <th>{{ $gettext('Email') }}</th>
+          <th>{{ $gettext('Account status') }}</th>
+          <th>{{ $gettext('Sign-up') }}</th>
+          <th>{{ $gettext('Last activity') }}</th>
+          <th>{{ $gettext('Permissions') }}</th>
+          <th>{{ $gettext('Status') }}</th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
@@ -51,15 +51,15 @@
             <span>{{ scope.obj.email }}</span>
           </td>
           <td>
-            <span v-if="scope.obj.is_active" class="ui basic green label">{{ $t('Active') }}</span>
-            <span v-else class="ui basic grey label">{{ $t('Inactive') }}</span>
+            <span v-if="scope.obj.is_active" class="ui basic green label">{{ $gettext('Active') }}</span>
+            <span v-else class="ui basic grey label">{{ $gettext('Inactive') }}</span>
           </td>
           <td>
             <human-date :date="scope.obj.date_joined"></human-date>
           </td>
           <td>
             <human-date v-if="scope.obj.last_activity" :date="scope.obj.last_activity"></human-date>
-            <template v-else>{{ $t('N/A') }}</template>
+            <template v-else>{{ $gettext('N/A') }}</template>
           </td>
           <td>
             <template v-for="p in permissions">
@@ -67,9 +67,9 @@
             </template>
           </td>
           <td>
-            <span v-if="scope.obj.is_superuser" class="ui pink label">{{ $t('Admin') }}</span>
-            <span v-else-if="scope.obj.is_staff" class="ui purple label">{{ $t('Staff member') }}</span>
-            <span v-else class="ui basic label">{{ $t('regular user') }}</span>
+            <span v-if="scope.obj.is_superuser" class="ui pink label">{{ $gettext('Admin') }}</span>
+            <span v-else-if="scope.obj.is_staff" class="ui purple label">{{ $gettext('Staff member') }}</span>
+            <span v-else class="ui basic label">{{ $gettext('regular user') }}</span>
           </td>
         </template>
       </action-table>
@@ -85,7 +85,10 @@
         ></pagination>
 
       <span v-if="result && result.results.length > 0">
-        {{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
+        <translate
+          :translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
+          Showing results %{ start }-%{ end } on %{ total }
+        </translate>
       </span>
     </div>
   </div>
@@ -161,19 +164,19 @@ export default {
       return [
         {
           'code': 'upload',
-          'label': this.$t('Upload')
+          'label': this.$gettext('Upload')
         },
         {
           'code': 'library',
-          'label': this.$t('Library')
+          'label': this.$gettext('Library')
         },
         {
           'code': 'federation',
-          'label': this.$t('Federation')
+          'label': this.$gettext('Federation')
         },
         {
           'code': 'settings',
-          'label': this.$t('Settings')
+          'label': this.$gettext('Settings')
         }
       ]
     },
@@ -191,7 +194,7 @@ export default {
       return [
         // {
         //   name: 'delete',
-        //   label: this.$t('Delete'),
+        //   label: this.$gettext('Delete'),
         //   isDangerous: true
         // }
       ]
diff --git a/front/src/components/metadata/ArtistCard.vue b/front/src/components/metadata/ArtistCard.vue
index c88438c0c0917e0394450affa04e1c1e5ec1958f..8f9aa741a04ce4593c82e13350d980f7a6edbfd3 100644
--- a/front/src/components/metadata/ArtistCard.vue
+++ b/front/src/components/metadata/ArtistCard.vue
@@ -16,7 +16,7 @@
                   {{ group['first-release-date'] }}
                 </td>
                 <td colspan="3">
-                  <a :href="getMusicbrainzUrl('release-group', group.id)" class="discrete link" target="_blank" :title="$t('View on MusicBrainz')">
+                  <a :href="getMusicbrainzUrl('release-group', group.id)" class="discrete link" target="_blank" :title="$gettext('View on MusicBrainz')">
                     {{ group.title }}
                   </a>
                 </td>
diff --git a/front/src/components/metadata/ReleaseCard.vue b/front/src/components/metadata/ReleaseCard.vue
index 68bcd1284c06e84d0152d6e38b4803c32368236b..13f5b53ab49cf53f90d9e096681a30cefed402b3 100644
--- a/front/src/components/metadata/ReleaseCard.vue
+++ b/front/src/components/metadata/ReleaseCard.vue
@@ -19,7 +19,7 @@
                   {{ track.position }}
                 </td>
                 <td colspan="3">
-                  <a :href="getMusicbrainzUrl('recording', track.id)" class="discrete link" target="_blank" :title="$t('View on MusicBrainz')">
+                  <a :href="getMusicbrainzUrl('recording', track.id)" class="discrete link" target="_blank" :title="$gettext('View on MusicBrainz')">
                     {{ track.recording.title }}
                   </a>
                 </td>
diff --git a/front/src/components/metadata/Search.vue b/front/src/components/metadata/Search.vue
index 3fcd8484edbe20c830c1f0078a0491554b9bfef8..1ba30b7c2135c46579914a535ee624f9bf5f7ec7 100644
--- a/front/src/components/metadata/Search.vue
+++ b/front/src/components/metadata/Search.vue
@@ -12,7 +12,7 @@
     </div>
     <div class="ui fluid search">
       <div class="ui icon input">
-        <input class="prompt" :placeholder="$t('Enter your search query...')" type="text">
+        <input class="prompt" :placeholder="$gettext('Enter your search query...')" type="text">
         <i class="search icon"></i>
       </div>
       <div class="results"></div>
@@ -122,15 +122,15 @@ export default {
       return [
         {
           value: 'artist',
-          label: this.$t('Artist')
+          label: this.$gettext('Artist')
         },
         {
           value: 'release',
-          label: this.$t('Album')
+          label: this.$gettext('Album')
         },
         {
           value: 'recording',
-          label: this.$t('Track')
+          label: this.$gettext('Track')
         }
       ]
     }
diff --git a/front/src/components/playlists/Card.vue b/front/src/components/playlists/Card.vue
index 19932a4e80a983c545bcd256fc2da00dba115b2f..6af726be0b3bdec9f63d2efd08218a6d9ccf54c5 100644
--- a/front/src/components/playlists/Card.vue
+++ b/front/src/components/playlists/Card.vue
@@ -11,15 +11,18 @@
       </div>
       <div class="meta">
         <i class="clock icon"></i>
-        <i18next path="Updated {%0%}">
-          <human-date :date="playlist.modification_date" />
-        </i18next>
+        <human-date :date="playlist.modification_date" />
       </div>
     </div>
     <div class="extra content">
       <span>
         <i class="sound icon"></i>
-        {{ $t('{%count%} tracks', { count: playlist.tracks_count }) }}
+        <translate
+          translate-plural="%{ count } tracks"
+          :translate-n="playlist.tracks_count"
+          :translate-params="{count: playlist.tracks_count}">
+          %{ count} track
+        </translate>
       </span>
       <play-button class="mini basic orange right floated" :playlist="playlist">Play all</play-button>
     </div>
diff --git a/front/src/components/playlists/Editor.vue b/front/src/components/playlists/Editor.vue
index c036737ce032461e8b02cd00d12593e27c960504..012fb2b45732f46523595ceddf851fb2c9e742d6 100644
--- a/front/src/components/playlists/Editor.vue
+++ b/front/src/components/playlists/Editor.vue
@@ -2,16 +2,16 @@
   <div class="ui text container">
     <playlist-form @updated="$emit('playlist-updated', $event)" :title="false" :playlist="playlist"></playlist-form>
     <h3 class="ui top attached header">
-      {{ $t('Playlist editor') }}
+      {{ $gettext('Playlist editor') }}
     </h3>
     <div class="ui attached segment">
       <template v-if="status === 'loading'">
         <div class="ui active tiny inline loader"></div>
-        {{ $t('Syncing changes to server...') }}
+        {{ $gettext('Syncing changes to server...') }}
       </template>
       <template v-else-if="status === 'errored'">
         <i class="red close icon"></i>
-        {{ $t('An error occured while saving your changes') }}
+        {{ $gettext('An error occured while saving your changes') }}
         <div v-if="errors.length > 0" class="ui negative message">
           <ul class="list">
             <li v-for="error in errors">{{ error }}</li>
@@ -19,7 +19,7 @@
         </div>
       </template>
       <template v-else-if="status === 'saved'">
-        <i class="green check icon"></i> {{ $t('Changes synced with server') }}
+        <i class="green check icon"></i> {{ $gettext('Changes synced with server') }}
       </template>
     </div>
     <div class="ui bottom attached segment">
@@ -29,14 +29,21 @@
         :class="['ui', {disabled: queueTracks.length === 0}, 'labeled', 'icon', 'button']"
         title="Copy tracks from current queue to playlist">
           <i class="plus icon"></i>
-          {{ $t('Insert from queue ({%count%} tracks)', { count: queueTracks.length }) }}
+          <translate
+            translate-plural="Insert from queue (%{ count } tracks)"
+            :translate-n="queueTracks.length"
+            :translate-params="{count: queueTracks.length}">
+            Insert from queue (%{ count } track)
+          </translate>
         </div>
 
       <dangerous-button :disabled="plts.length === 0" class="labeled right floated icon" color='yellow' :action="clearPlaylist">
-        <i class="eraser icon"></i> {{ $t('Clear playlist') }}
-        <p slot="modal-header">{{ $t('Do you want to clear the playlist "{%name%}"?', { name: playlist.name }) }}</p>
-        <p slot="modal-content">{{ $t('This will remove all tracks from this playlist and cannot be undone.') }}</p>
-        <p slot="modal-confirm">{{ $t('Clear playlist') }}</p>
+        <i class="eraser icon"></i> {{ $gettext('Clear playlist') }}
+        <p slot="modal-header">
+          <translate :translate-params="{playlist: playlist.name}">Do you want to clear the playlist "%{ playlist }"?</translate>
+        </p>
+        <p slot="modal-content">{{ $gettext('This will remove all tracks from this playlist and cannot be undone.') }}</p>
+        <p slot="modal-confirm">{{ $gettext('Clear playlist') }}</p>
       </dangerous-button>
       <div class="ui hidden divider"></div>
       <template v-if="plts.length > 0">
diff --git a/front/src/components/playlists/Form.vue b/front/src/components/playlists/Form.vue
index d19cd687668dca71fae2fabd8926426ae24986b4..2d26594c7941d47d6f99e3ab00e95d573fb9d2d4 100644
--- a/front/src/components/playlists/Form.vue
+++ b/front/src/components/playlists/Form.vue
@@ -1,29 +1,29 @@
 <template>
   <form class="ui form" @submit.prevent="submit()">
-    <h4 v-if="title" class="ui header">{{ $t('Create a new playlist') }}</h4>
+    <h4 v-if="title" class="ui header">{{ $gettext('Create a new playlist') }}</h4>
     <div v-if="success" class="ui positive message">
       <div class="header">
         <template v-if="playlist">
-          {{ $t('Playlist updated') }}
+          {{ $gettext('Playlist updated') }}
         </template>
         <template v-else>
-          {{ $t('Playlist created') }}
+          {{ $gettext('Playlist created') }}
         </template>
       </div>
     </div>
     <div v-if="errors.length > 0" class="ui negative message">
-      <div class="header">{{ $t('We cannot create the playlist') }}</div>
+      <div class="header">{{ $gettext('We cannot create the playlist') }}</div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
       </ul>
     </div>
     <div class="three fields">
       <div class="field">
-        <label>{{ $t('Playlist name') }}</label>
+        <label>{{ $gettext('Playlist name') }}</label>
         <input v-model="name" required type="text" placeholder="My awesome playlist" />
       </div>
       <div class="field">
-        <label>{{ $t('Playlist visibility') }}</label>
+        <label>{{ $gettext('Playlist visibility') }}</label>
         <select class="ui dropdown" v-model="privacyLevel">
           <option :value="c.value" v-for="c in privacyLevelChoices">{{ c.label }}</option>
         </select>
@@ -31,8 +31,8 @@
       <div class="field">
         <label>&nbsp;</label>
         <button :class="['ui', 'fluid', {'loading': isLoading}, 'button']" type="submit">
-          <template v-if="playlist">{{ $t('Update playlist') }}</template>
-          <template v-else>{{ $t('Create playlist') }}</template>
+          <template v-if="playlist">{{ $gettext('Update playlist') }}</template>
+          <template v-else>{{ $gettext('Create playlist') }}</template>
         </button>
       </div>
     </div>
@@ -73,15 +73,15 @@ export default {
       return [
         {
           value: 'me',
-          label: this.$t('Nobody except me')
+          label: this.$gettext('Nobody except me')
         },
         {
           value: 'instance',
-          label: this.$t('Everyone on this instance')
+          label: this.$gettext('Everyone on this instance')
         },
         {
           value: 'everyone',
-          label: this.$t('Everyone')
+          label: this.$gettext('Everyone')
         }
       ]
     }
diff --git a/front/src/components/playlists/PlaylistModal.vue b/front/src/components/playlists/PlaylistModal.vue
index da25241ce55dd697f2039797ce8e7d372e5b5ab3..feca8396f2b49fe2f5639207cfb0f66c7ea0a635 100644
--- a/front/src/components/playlists/PlaylistModal.vue
+++ b/front/src/components/playlists/PlaylistModal.vue
@@ -1,33 +1,37 @@
 <template>
   <modal @update:show="update" :show="$store.state.playlists.showModal">
     <div class="header">
-      {{ $t('Manage playlists') }}
+      {{ $gettext('Manage playlists') }}
     </div>
     <div class="scrolling content">
       <div class="description">
         <template v-if="track">
-          <h4 class="ui header">{{ $t('Current track') }}</h4>
-          <div v-html='trackDisplay'></div>
+          <h4 class="ui header">{{ $gettext('Current track') }}</h4>
+          <div
+            v-translate="{artist: track.artist.name, title: track.title}"
+            :template-params="{artist: track.artist.name, title: track.title}">
+            "%{ title }", by %{ artist }
+          </div>
           <div class="ui divider"></div>
         </template>
 
         <playlist-form></playlist-form>
         <div class="ui divider"></div>
         <div v-if="errors.length > 0" class="ui negative message">
-          <div class="header">{{ $t('We cannot add the track to a playlist') }}</div>
+          <div class="header">{{ $gettext('We cannot add the track to a playlist') }}</div>
           <ul class="list">
             <li v-for="error in errors">{{ error }}</li>
           </ul>
         </div>
         </div>
-        <h4 class="ui header">{{ $t('Available playlists') }}</h4>
+        <h4 class="ui header">{{ $gettext('Available playlists') }}</h4>
         <table class="ui unstackable very basic table">
           <thead>
             <tr>
               <th></th>
-              <th>{{ $t('Name') }}</th>
-              <th class="sorted descending">{{ $t('Last modification') }}</th>
-              <th>{{ $t('Tracks') }}</th>
+              <th>{{ $gettext('Name') }}</th>
+              <th class="sorted descending">{{ $gettext('Last modification') }}</th>
+              <th>{{ $gettext('Tracks') }}</th>
               <th></th>
             </tr>
           </thead>
@@ -46,9 +50,9 @@
                 <div
                   v-if="track"
                   class="ui green icon basic small right floated button"
-                  :title="$t('Add to this playlist')"
+                  :title="$gettext('Add to this playlist')"
                   @click="addToPlaylist(playlist.id)">
-                  <i class="plus icon"></i> {{ $t('Add track') }}
+                  <i class="plus icon"></i> {{ $gettext('Add track') }}
                 </div>
               </td>
             </tr>
@@ -57,7 +61,7 @@
       </div>
     </div>
     <div class="actions">
-      <div class="ui cancel button">{{ $t('Cancel') }}</div>
+      <div class="ui cancel button">{{ $gettext('Cancel') }}</div>
     </div>
   </modal>
 </template>
@@ -110,12 +114,6 @@ export default {
       let p = _.sortBy(this.playlists, [(e) => { return e.modification_date }])
       p.reverse()
       return p
-    },
-    trackDisplay () {
-      return this.$t('"{%title%}" by {%artist%}', {
-        title: this.track.title,
-        artist: this.track.artist.name }
-      )
     }
   },
   watch: {
diff --git a/front/src/components/playlists/TrackPlaylistIcon.vue b/front/src/components/playlists/TrackPlaylistIcon.vue
index cd74b4d275996380122b2a134e7519f2e9f3a343..c669acb92596711f51b9e61e223eae9477ee7e88 100644
--- a/front/src/components/playlists/TrackPlaylistIcon.vue
+++ b/front/src/components/playlists/TrackPlaylistIcon.vue
@@ -4,13 +4,13 @@
     v-if="button"
     :class="['ui', 'button']">
     <i class="list icon"></i>
-    {{ $t('Add to playlist...') }}
+    {{ $gettext('Add to playlist...') }}
   </button>
   <i
     v-else
     @click="$store.commit('playlists/chooseTrack', track)"
     :class="['playlist-icon', 'list', 'link', 'icon']"
-    :title="$t('Add to playlist...')">
+    :title="$gettext('Add to playlist...')">
   </i>
 </template>
 
diff --git a/front/src/components/radios/Button.vue b/front/src/components/radios/Button.vue
index abdc660fcedf463ad4abfb4abbd9fff90317bb80..5a112e9618bf7e01b9bc47378772114d0f5072ed 100644
--- a/front/src/components/radios/Button.vue
+++ b/front/src/components/radios/Button.vue
@@ -1,8 +1,8 @@
 <template>
   <button @click="toggleRadio" :class="['ui', 'blue', {'inverted': running}, 'button']">
     <i class="ui feed icon"></i>
-    <template v-if="running">{{ $t('Stop') }}</template>
-    <template v-else>{{ $t('Start') }}</template>
+    <template v-if="running">{{ $gettext('Stop') }}</template>
+    <template v-else>{{ $gettext('Start') }}</template>
     radio
   </button>
 </template>
diff --git a/front/src/components/radios/Card.vue b/front/src/components/radios/Card.vue
index bab1e2a1de26a284c09a5314bd8db9346775cc04..db92f8276d913f947dd82810b0a126985c91a7c2 100644
--- a/front/src/components/radios/Card.vue
+++ b/front/src/components/radios/Card.vue
@@ -18,7 +18,7 @@
           class="ui basic yellow button"
           v-if="$store.state.auth.authenticated && type === 'custom' && customRadio.user === $store.state.auth.profile.id"
           :to="{name: 'library.radios.edit', params: {id: customRadioId }}">
-          {{ $t('Edit...') }}
+          {{ $gettext('Edit...') }}
         </router-link>
         <radio-button class="right floated button" :type="type" :custom-radio-id="customRadioId"></radio-button>
       </div>
diff --git a/front/src/components/requests/Card.vue b/front/src/components/requests/Card.vue
index 7743bb6d4403653fedbc355983ec0bb4e7adecf1..8bddc94f37b95e25948ea77e3d275e4cad2fde45 100644
--- a/front/src/components/requests/Card.vue
+++ b/front/src/components/requests/Card.vue
@@ -23,7 +23,7 @@
       <button
         @click="createImport"
         v-if="request.status === 'pending' && importAction && $store.state.auth.availablePermissions['library']"
-        class="ui mini basic green right floated button">{{ $t('Create import') }}</button>
+        class="ui mini basic green right floated button">{{ $gettext('Create import') }}</button>
 
     </div>
   </div>
diff --git a/front/src/components/requests/Form.vue b/front/src/components/requests/Form.vue
index b03e1545ba4d7861fc6b02c71e752f630127fb98..d2e5a439038ef5e2312cc80c18d8620551eb4eb5 100644
--- a/front/src/components/requests/Form.vue
+++ b/front/src/components/requests/Form.vue
@@ -1,30 +1,30 @@
 <template>
   <div>
     <form v-if="!over" class="ui form" @submit.prevent="submit">
-      <p>{{ $t('Something\'s missing in the library? Let us know what you would like to listen!') }}</p>
+      <p>{{ $gettext('Something\'s missing in the library? Let us know what you would like to listen!') }}</p>
       <div class="required field">
-        <label>{{ $t('Artist name') }}</label>
+        <label>{{ $gettext('Artist name') }}</label>
         <input v-model="currentArtistName" placeholder="The Beatles, Mickael Jackson…" required maxlength="200">
       </div>
       <div class="field">
-        <label>{{ $t('Albums') }}</label>
-        <p>{{ $t('Leave this field empty if you\'re requesting the whole discography.') }}</p>
+        <label>{{ $gettext('Albums') }}</label>
+        <p>{{ $gettext('Leave this field empty if you\'re requesting the whole discography.') }}</p>
         <input v-model="currentAlbums" placeholder="The White Album, Thriller…" maxlength="2000">
       </div>
       <div class="field">
-        <label>{{ $t('Comment') }}</label>
+        <label>{{ $gettext('Comment') }}</label>
         <textarea v-model="currentComment" rows="3" placeholder="Use this comment box to add details to your request if needed" maxlength="2000"></textarea>
       </div>
-      <button class="ui submit button" type="submit">{{ $t('Submit') }}</button>
+      <button class="ui submit button" type="submit">{{ $gettext('Submit') }}</button>
     </form>
     <div v-else class="ui success message">
       <div class="header">Request submitted!</div>
-      <p>{{ $t('We\'ve received your request, you\'ll get some groove soon ;)') }}</p>
-      <button @click="reset" class="ui button">{{ $t('Submit another request') }}</button>
+      <p>{{ $gettext('We\'ve received your request, you\'ll get some groove soon ;)') }}</p>
+      <button @click="reset" class="ui button">{{ $gettext('Submit another request') }}</button>
     </div>
     <div v-if="requests.length > 0">
       <div class="ui divider"></div>
-      <h3 class="ui header">{{ $t('Pending requests') }}</h3>
+      <h3 class="ui header">{{ $gettext('Pending requests') }}</h3>
       <div class="ui list">
         <div v-for="request in requests" class="item">
           <div class="content">
diff --git a/front/src/locales.js b/front/src/locales.js
new file mode 100644
index 0000000000000000000000000000000000000000..1253354c59f93c2d530daa8413ae9dcf9ed392a3
--- /dev/null
+++ b/front/src/locales.js
@@ -0,0 +1,13 @@
+/* eslint-disable */
+export default {
+  "locales": [
+    {
+      "code": "en_US",
+      "label": "English (United-States)"
+    },
+    {
+      "code": "fr_FR",
+      "label": "Français"
+    }
+  ]
+}
diff --git a/front/src/main.js b/front/src/main.js
index 181fd66b3b63197660794af819c24fa1751866fb..7f60c602c9559f71484c9e45d1bed0c9334adad5 100644
--- a/front/src/main.js
+++ b/front/src/main.js
@@ -11,11 +11,12 @@ import router from './router'
 import axios from 'axios'
 import {VueMasonryPlugin} from 'vue-masonry'
 import VueLazyload from 'vue-lazyload'
-import i18next from 'i18next'
-import i18nextFetch from 'i18next-fetch-backend'
-import VueI18Next from '@panter/vue-i18next'
 import store from './store'
+import GetTextPlugin from 'vue-gettext'
 import { sync } from 'vuex-router-sync'
+import translations from './translations.json'
+import locales from '@/locales'
+
 import filters from '@/filters' // eslint-disable-line
 import globals from '@/components/globals' // eslint-disable-line
 
@@ -28,8 +29,31 @@ window.$ = window.jQuery = require('jquery')
 // require('./semantic/semantic.css')
 require('semantic-ui-css/semantic.js')
 require('masonry-layout')
+let availableLanguages = (function () {
+  let l = {}
+  locales.locales.forEach(c => {
+    l[c.code] = c.label
+  })
+  return l
+})()
+let defaultLanguage = 'en_US'
+if (availableLanguages[store.state.ui.currentLanguage]) {
+  defaultLanguage = store.state.ui.currentLanguage
+}
+Vue.use(GetTextPlugin, {
+  availableLanguages: availableLanguages,
+  defaultLanguage: defaultLanguage,
+  languageVmMixin: {
+    computed: {
+      currentKebabCase: function () {
+        return this.current.toLowerCase().replace('_', '-')
+      }
+    }
+  },
+  translations: translations,
+  silent: true
+})
 
-Vue.use(VueI18Next)
 Vue.use(VueMasonryPlugin)
 Vue.use(VueLazyload)
 Vue.config.productionTip = false
@@ -96,35 +120,17 @@ axios.interceptors.response.use(function (response) {
     }
   }
   if (error.backendErrors.length === 0) {
-    error.backendErrors.push(i18next.t('An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running'))
+    error.backendErrors.push('An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running')
   }
   // Do something with response error
   return Promise.reject(error)
 })
 
-// i18n
-i18next
-  .use(i18nextFetch)
-  .init({
-    lng: navigator.language,
-    fallbackLng: ['en'],
-    preload: [navigator.language, 'en'],
-    backend: {
-      loadPath: '/static/translations/{%lng%}.json'
-    },
-    interpolation: {
-      prefix: '{%',
-      suffix: '%}'
-    }
-  })
-const i18n = new VueI18Next(i18next)
-
 /* eslint-disable no-new */
 new Vue({
   el: '#app',
   router,
   store,
-  i18n,
   template: '<App/>',
   components: { App }
 })
diff --git a/front/src/store/index.js b/front/src/store/index.js
index 0c2908d83d5cceee72997111f099aeda555d2337..46075d84756137bc5bb5be7c4b2cf8be2708d33a 100644
--- a/front/src/store/index.js
+++ b/front/src/store/index.js
@@ -36,6 +36,10 @@ export default new Vuex.Store({
       key: 'instance',
       paths: ['instance.events', 'instance.instanceUrl']
     }),
+    createPersistedState({
+      key: 'ui',
+      paths: ['ui.currentLanguage']
+    }),
     createPersistedState({
       key: 'radios',
       paths: ['radios'],
diff --git a/front/src/store/ui.js b/front/src/store/ui.js
index c336803475c5c6c79776e501dd94a8884a9198c6..6641f4c0dec891c0d7f75fa26e3e974c135b38e7 100644
--- a/front/src/store/ui.js
+++ b/front/src/store/ui.js
@@ -3,6 +3,7 @@ import axios from 'axios'
 export default {
   namespaced: true,
   state: {
+    currentLanguage: 'en_US',
     lastDate: new Date(),
     maxMessages: 100,
     messageDisplayDuration: 10000,
@@ -13,6 +14,9 @@ export default {
     }
   },
   mutations: {
+    currentLanguage: (state, value) => {
+      state.currentLanguage = value
+    },
     computeLastDate: (state) => {
       state.lastDate = new Date()
     },
diff --git a/front/src/translations.json b/front/src/translations.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/front/src/translations.json
@@ -0,0 +1 @@
+{}
diff --git a/front/src/views/admin/Settings.vue b/front/src/views/admin/Settings.vue
index 10e32968d7dd46fd164002b020c46437d6391c14..0579ee14290e32bf7eedb80938eb3120ca4b7a7b 100644
--- a/front/src/views/admin/Settings.vue
+++ b/front/src/views/admin/Settings.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main pusher"  v-title="$t('Instance settings')">
+  <div class="main pusher"  v-title="$gettext('Instance settings')">
     <div class="ui vertical stripe segment">
       <div class="ui text container">
         <div :class="['ui', {'loading': isLoading}, 'form']"></div>
@@ -13,7 +13,7 @@
           </div>
           <div class="four wide column">
             <div class="ui sticky vertical secondary menu">
-              <div class="header item">{{ $t('Sections') }}</div>
+              <div class="header item">{{ $gettext('Sections') }}</div>
               <a :class="['menu', {active: group.id === current}, 'item']"
                 @click.prevent="scrollTo(group.id)"
                 :href="'#' + group.id"
@@ -71,9 +71,18 @@ export default {
   },
   computed: {
     groups () {
+      // somehow, extraction fails if in the return block directly
+      let instanceLabel = this.$gettext('Instance information')
+      let usersLabel = this.$gettext('Users')
+      let importsLabel = this.$gettext('Imports')
+      let playlistsLabel = this.$gettext('Playlists')
+      let federationLabel = this.$gettext('Federation')
+      let subsonicLabel = this.$gettext('Subsonic')
+      let statisticsLabel = this.$gettext('Statistics')
+      let errorLabel = this.$gettext('Error reporting')
       return [
         {
-          label: this.$t('Instance information'),
+          label: instanceLabel,
           id: 'instance',
           settings: [
             'instance__name',
@@ -82,7 +91,7 @@ export default {
           ]
         },
         {
-          label: this.$t('Users'),
+          label: usersLabel,
           id: 'users',
           settings: [
             'users__registration_enabled',
@@ -91,21 +100,21 @@ export default {
           ]
         },
         {
-          label: this.$t('Imports'),
+          label: importsLabel,
           id: 'imports',
           settings: [
             'providers_youtube__api_key'
           ]
         },
         {
-          label: this.$t('Playlists'),
+          label: playlistsLabel,
           id: 'playlists',
           settings: [
             'playlists__max_tracks'
           ]
         },
         {
-          label: this.$t('Federation'),
+          label: federationLabel,
           id: 'federation',
           settings: [
             'federation__enabled',
@@ -116,14 +125,14 @@ export default {
           ]
         },
         {
-          label: this.$t('Subsonic'),
+          label: subsonicLabel,
           id: 'subsonic',
           settings: [
             'subsonic__enabled'
           ]
         },
         {
-          label: this.$t('Statistics'),
+          label: statisticsLabel,
           id: 'statistics',
           settings: [
             'instance__nodeinfo_enabled',
@@ -132,7 +141,7 @@ export default {
           ]
         },
         {
-          label: this.$t('Error reporting'),
+          label: errorLabel,
           id: 'reporting',
           settings: [
             'raven__front_enabled',
diff --git a/front/src/views/admin/library/Base.vue b/front/src/views/admin/library/Base.vue
index cc26c8d6be42f0fe30643ea13bb78bccc1e13838..dd7e6e34451388df5dc1be9f27b248bfb2fabc4d 100644
--- a/front/src/views/admin/library/Base.vue
+++ b/front/src/views/admin/library/Base.vue
@@ -3,14 +3,14 @@
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'manage.library.files'}">{{ $t('Files') }}</router-link>
+        :to="{name: 'manage.library.files'}">{{ $gettext('Files') }}</router-link>
       <router-link
         class="ui item"
         :to="{name: 'manage.library.requests'}">
-          {{ $t('Import requests') }}
+          {{ $gettext('Import requests') }}
           <div
             :class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
-            :title="$t('Pending import requests')">
+            :title="$gettext('Pending import requests')">
             {{ $store.state.ui.notifications.importRequests }}</div>
           </router-link>
     </div>
diff --git a/front/src/views/admin/library/FilesList.vue b/front/src/views/admin/library/FilesList.vue
index 9c52de5767042d9a2ddfe805965ef316d3dc5933..a5add7150f02ef0f03fd15deb5dc8fe788998c58 100644
--- a/front/src/views/admin/library/FilesList.vue
+++ b/front/src/views/admin/library/FilesList.vue
@@ -1,7 +1,7 @@
 <template>
   <div v-title="'Files'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Library files') }}</h2>
+      <h2 class="ui header">{{ $gettext('Library files') }}</h2>
       <div class="ui hidden divider"></div>
       <library-files-table :show-library="true"></library-files-table>
     </div>
diff --git a/front/src/views/admin/library/RequestsList.vue b/front/src/views/admin/library/RequestsList.vue
index 160bf890b99dc42f0cdd07493e06e70f0e3fe93d..bf097fa77f7b54477b8569cf287a0bad9e17e698 100644
--- a/front/src/views/admin/library/RequestsList.vue
+++ b/front/src/views/admin/library/RequestsList.vue
@@ -1,7 +1,7 @@
 <template>
-  <div v-title="$t('Import requests')">
+  <div v-title="$gettext('Import requests')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Import requests') }}</h2>
+      <h2 class="ui header">{{ $gettext('Import requests') }}</h2>
       <div class="ui hidden divider"></div>
       <library-requests-table></library-requests-table>
     </div>
diff --git a/front/src/views/admin/users/Base.vue b/front/src/views/admin/users/Base.vue
index 505ca587fe2bf2726b2a3c30bfbf4b6c540a858f..ae175435530e5cb96215c65d2332db9d24e4e93a 100644
--- a/front/src/views/admin/users/Base.vue
+++ b/front/src/views/admin/users/Base.vue
@@ -1,12 +1,12 @@
 <template>
-  <div class="main pusher"  v-title="$t('Manage users')">
+  <div class="main pusher"  v-title="$gettext('Manage users')">
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'manage.users.users.list'}">{{ $t('Users') }}</router-link>
+        :to="{name: 'manage.users.users.list'}">{{ $gettext('Users') }}</router-link>
       <router-link
         class="ui item"
-        :to="{name: 'manage.users.invitations.list'}">{{ $t('Invitations') }}</router-link>
+        :to="{name: 'manage.users.invitations.list'}">{{ $gettext('Invitations') }}</router-link>
     </div>
     <router-view :key="$route.fullPath"></router-view>
   </div>
diff --git a/front/src/views/admin/users/InvitationsList.vue b/front/src/views/admin/users/InvitationsList.vue
index 230dad6c1971a9c86c08570c404530ecf405c7d4..7a7446f0eba5b3bc73a8dcddf3f1ea9abafb912c 100644
--- a/front/src/views/admin/users/InvitationsList.vue
+++ b/front/src/views/admin/users/InvitationsList.vue
@@ -1,7 +1,7 @@
 <template>
-  <div v-title="$t('Invitations')">
+  <div v-title="$gettext('Invitations')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Invitations') }}</h2>
+      <h2 class="ui header">{{ $gettext('Invitations') }}</h2>
       <invitation-form></invitation-form>
       <div class="ui hidden divider"></div>
       <invitations-table></invitations-table>
diff --git a/front/src/views/admin/users/UsersDetail.vue b/front/src/views/admin/users/UsersDetail.vue
index ea92716ca943570d77e1cd4cd91cb1d9eaa97133..60a4080532e95f89b97c72ff88f2297f14c9c1fb 100644
--- a/front/src/views/admin/users/UsersDetail.vue
+++ b/front/src/views/admin/users/UsersDetail.vue
@@ -19,7 +19,7 @@
             <tbody>
               <tr>
                 <td>
-                  {{ $t('Name') }}
+                  {{ $gettext('Name') }}
                 </td>
                 <td>
                   {{ object.name }}
@@ -27,7 +27,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Email address') }}
+                  {{ $gettext('Email address') }}
                 </td>
                 <td>
                   {{ object.email }}
@@ -35,7 +35,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Sign-up') }}
+                  {{ $gettext('Sign-up') }}
                 </td>
                 <td>
                   <human-date :date="object.date_joined"></human-date>
@@ -43,17 +43,17 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Last activity') }}
+                  {{ $gettext('Last activity') }}
                 </td>
                 <td>
                   <human-date v-if="object.last_activity" :date="object.last_activity"></human-date>
-                  <template v-else>{{ $t('N/A') }}</template>
+                  <template v-else>{{ $gettext('N/A') }}</template>
                 </td>
               </tr>
               <tr>
                 <td>
-                  {{ $t('Account active') }}
-                  <span :data-tooltip="$t('Determine if the user account is active or not. Inactive users cannot login or user the service.')"><i class="question circle icon"></i></span>
+                  {{ $gettext('Account active') }}
+                  <span :data-tooltip="$gettext('Determine if the user account is active or not. Inactive users cannot login or user the service.')"><i class="question circle icon"></i></span>
                 </td>
                 <td>
                   <div class="ui toggle checkbox">
@@ -66,7 +66,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Permissions') }}
+                  {{ $gettext('Permissions') }}
                 </td>
                 <td>
                   <select
@@ -82,7 +82,7 @@
           </table>
         </div>
         <div class="ui hidden divider"></div>
-        <button @click="fetchData" class="ui basic button">{{ $t('Refresh') }}</button>
+        <button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
       </div>
     </template>
   </div>
@@ -145,19 +145,19 @@ export default {
       return [
         {
           'code': 'upload',
-          'label': this.$t('Upload')
+          'label': this.$gettext('Upload')
         },
         {
           'code': 'library',
-          'label': this.$t('Library')
+          'label': this.$gettext('Library')
         },
         {
           'code': 'federation',
-          'label': this.$t('Federation')
+          'label': this.$gettext('Federation')
         },
         {
           'code': 'settings',
-          'label': this.$t('Settings')
+          'label': this.$gettext('Settings')
         }
       ]
     }
diff --git a/front/src/views/admin/users/UsersList.vue b/front/src/views/admin/users/UsersList.vue
index b22d4aaf836f0b45be74d2a894c59963eac8a145..5e9625ee00a7fe2dfd6edd5353df7e52ad89edad 100644
--- a/front/src/views/admin/users/UsersList.vue
+++ b/front/src/views/admin/users/UsersList.vue
@@ -1,7 +1,7 @@
 <template>
-  <div v-title="$t('Users')">
+  <div v-title="$gettext('Users')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Users') }}</h2>
+      <h2 class="ui header">{{ $gettext('Users') }}</h2>
       <div class="ui hidden divider"></div>
       <users-table></users-table>
     </div>
diff --git a/front/src/views/auth/EmailConfirm.vue b/front/src/views/auth/EmailConfirm.vue
index 7ffa3c8d1bb96073f34985000bd624a22fe0523e..f01aa5216eeafb6618fdbe9ff3e0ffee93db1591 100644
--- a/front/src/views/auth/EmailConfirm.vue
+++ b/front/src/views/auth/EmailConfirm.vue
@@ -1,30 +1,30 @@
 <template>
-  <div class="main pusher" v-title="$t('Confirm your email')">
+  <div class="main pusher" v-title="$gettext('Confirm your email')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $t('Confirm your email') }}</h2>
+        <h2>{{ $gettext('Confirm your email') }}</h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $t('Error while confirming your email') }}</div>
+            <div class="header">{{ $gettext('Error while confirming your email') }}</div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
           <div class="field">
-            <label>{{ $t('Confirmation code') }}</label>
+            <label>{{ $gettext('Confirmation code') }}</label>
             <input type="text" required v-model="key" />
           </div>
           <router-link :to="{path: '/login'}">
-            {{ $t('Back to login') }}
+            {{ $gettext('Back to login') }}
           </router-link>
           <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-            {{ $t('Confirm your email') }}</button>
+            {{ $gettext('Confirm your email') }}</button>
         </form>
         <div v-else class="ui positive message">
-          <div class="header">{{ $t('Email confirmed') }}</div>
-          <p>{{ $t('Your email address was confirmed, you can now use the service without limitations.') }}</p>
+          <div class="header">{{ $gettext('Email confirmed') }}</div>
+          <p>{{ $gettext('Your email address was confirmed, you can now use the service without limitations.') }}</p>
           <router-link :to="{name: 'login'}">
-            {{ $t('Proceed to login') }}
+            {{ $gettext('Proceed to login') }}
           </router-link>
         </div>
       </div>
diff --git a/front/src/views/auth/PasswordReset.vue b/front/src/views/auth/PasswordReset.vue
index f6b445e00fa4e5f38e852b0f5c9c50c407ffd820..d56e85804a51fc0dfb722ccf2ead562838e78d2c 100644
--- a/front/src/views/auth/PasswordReset.vue
+++ b/front/src/views/auth/PasswordReset.vue
@@ -1,31 +1,31 @@
 <template>
-  <div class="main pusher" v-title="$t('Reset your password')">
+  <div class="main pusher" v-title="$gettext('Reset your password')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $t('Reset your password') }}</h2>
+        <h2>{{ $gettext('Reset your password') }}</h2>
         <form class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $t('Error while asking for a password reset') }}</div>
+            <div class="header">{{ $gettext('Error while asking for a password reset') }}</div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
-          <p>{{ $t('Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.') }}</p>
+          <p>{{ $gettext('Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.') }}</p>
           <div class="field">
-            <label>{{ $t('Account\'s email') }}</label>
+            <label>{{ $gettext('Account\'s email') }}</label>
             <input
               required
               ref="email"
               type="email"
               autofocus
-              :placeholder="$t('Input the email address binded to your account')"
+              :placeholder="$gettext('Input the email address binded to your account')"
               v-model="email">
           </div>
           <router-link :to="{path: '/login'}">
-            {{ $t('Back to login') }}
+            {{ $gettext('Back to login') }}
           </router-link>
           <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-            {{ $t('Ask for a password reset') }}</button>
+            {{ $gettext('Ask for a password reset') }}</button>
         </form>
       </div>
     </div>
diff --git a/front/src/views/auth/PasswordResetConfirm.vue b/front/src/views/auth/PasswordResetConfirm.vue
index 102ed6126d1275cc4cbfd9789dff54a0ba40784c..05225f093931778a57067056208b9e0964121d4f 100644
--- a/front/src/views/auth/PasswordResetConfirm.vue
+++ b/front/src/views/auth/PasswordResetConfirm.vue
@@ -1,35 +1,35 @@
 <template>
-  <div class="main pusher" v-title="$t('Change your password')">
+  <div class="main pusher" v-title="$gettext('Change your password')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $t('Change your password') }}</h2>
+        <h2>{{ $gettext('Change your password') }}</h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $t('Error while changing your password') }}</div>
+            <div class="header">{{ $gettext('Error while changing your password') }}</div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
           <template v-if="token && uid">
             <div class="field">
-              <label>{{ $t('New password') }}</label>
+              <label>{{ $gettext('New password') }}</label>
               <password-input v-model="newPassword" />
             </div>
             <router-link :to="{path: '/login'}">
-              {{ $t('Back to login') }}
+              {{ $gettext('Back to login') }}
             </router-link>
             <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-              {{ $t('Update your password') }}</button>
+              {{ $gettext('Update your password') }}</button>
           </template>
           <template v-else>
-            <p>{{ $t('If the email address provided in the previous step is valid and binded to a user account, you should receive an email with reset instructions in the next couple of minutes.') }}</p>
+            <p>{{ $gettext('If the email address provided in the previous step is valid and binded to a user account, you should receive an email with reset instructions in the next couple of minutes.') }}</p>
           </template>
         </form>
         <div v-else class="ui positive message">
-          <div class="header">{{ $t('Password updated successfully') }}</div>
-          <p>{{ $t('Your password has been updated successfully.') }}</p>
+          <div class="header">{{ $gettext('Password updated successfully') }}</div>
+          <p>{{ $gettext('Your password has been updated successfully.') }}</p>
           <router-link :to="{name: 'login'}">
-            {{ $t('Proceed to login') }}
+            {{ $gettext('Proceed to login') }}
           </router-link>
         </div>
       </div>
diff --git a/front/src/views/federation/Base.vue b/front/src/views/federation/Base.vue
index 951fe9f0f1c49abe2d43274151008f9d97113439..5b126950273044663c37882db16d5dc89fdef3ce 100644
--- a/front/src/views/federation/Base.vue
+++ b/front/src/views/federation/Base.vue
@@ -3,16 +3,16 @@
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'federation.libraries.list'}">{{ $t('Libraries') }}</router-link>
+        :to="{name: 'federation.libraries.list'}">{{ $gettext('Libraries') }}</router-link>
       <router-link
         class="ui item"
-        :to="{name: 'federation.tracks.list'}">{{ $t('Tracks') }}</router-link>
+        :to="{name: 'federation.tracks.list'}">{{ $gettext('Tracks') }}</router-link>
         <div class="ui secondary right menu">
           <router-link
             class="ui item"
             :to="{name: 'federation.followers.list'}">
-            {{ $t('Followers') }}
-            <div class="ui teal label" :title="$t('Pending requests')">{{ requestsCount }}</div>
+            {{ $gettext('Followers') }}
+            <div class="ui teal label" :title="$gettext('Pending requests')">{{ requestsCount }}</div>
           </router-link>
         </div>
     </div>
diff --git a/front/src/views/federation/LibraryDetail.vue b/front/src/views/federation/LibraryDetail.vue
index 7a842b679a967ed288f9805d1dbf684f2256d859..fb4e27990a129202dfd407f1b5929341acb78c9a 100644
--- a/front/src/views/federation/LibraryDetail.vue
+++ b/front/src/views/federation/LibraryDetail.vue
@@ -19,18 +19,18 @@
             <tbody>
               <tr>
                 <td >
-                  {{ $t('Follow status') }}
-                  <span :data-tooltip="$t('This indicate if the remote library granted you access')"><i class="question circle icon"></i></span>
+                  {{ $gettext('Follow status') }}
+                  <span :data-tooltip="$gettext('This indicate if the remote library granted you access')"><i class="question circle icon"></i></span>
                 </td>
                 <td>
                   <template v-if="object.follow.approved === null">
-                    <i class="loading icon"></i> {{ $t('Pending approval') }}
+                    <i class="loading icon"></i> {{ $gettext('Pending approval') }}
                   </template>
                   <template v-else-if="object.follow.approved === true">
-                    <i class="check icon"></i> {{ $t('Following') }}
+                    <i class="check icon"></i> {{ $gettext('Following') }}
                   </template>
                   <template v-else-if="object.follow.approved === false">
-                    <i class="x icon"></i> {{ $t('Not following') }}
+                    <i class="x icon"></i> {{ $gettext('Not following') }}
                   </template>
                 </td>
                 <td>
@@ -38,8 +38,8 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Federation') }}
-                  <span :data-tooltip="$t('Use this flag to enable/disable federation with this library')"><i class="question circle icon"></i></span>
+                  {{ $gettext('Federation') }}
+                  <span :data-tooltip="$gettext('Use this flag to enable/disable federation with this library')"><i class="question circle icon"></i></span>
                 </td>
                 <td>
                   <div class="ui toggle checkbox">
@@ -54,8 +54,8 @@
               </tr>
               <tr>
                 <td>
-                  {{ $t('Auto importing') }}
-                  <span :data-tooltip="$t('When enabled, auto importing will automatically import new tracks published in this library')"><i class="question circle icon"></i></span>
+                  {{ $gettext('Auto importing') }}
+                  <span :data-tooltip="$gettext('When enabled, auto importing will automatically import new tracks published in this library')"><i class="question circle icon"></i></span>
                 </td>
                 <td>
                   <div class="ui toggle checkbox">
@@ -82,19 +82,24 @@
               </tr>
               -->
               <tr>
-                <td>{{ $t('Library size') }}</td>
+                <td>{{ $gettext('Library size') }}</td>
                 <td>
                   <template v-if="object.tracks_count">
-                    {{ $t('{%count%} tracks', { count: object.tracks_count }) }}
+                    <translate
+                      translate-plural="%{ count } tracks"
+                      :translate-n="object.tracks_count"
+                      :translate-params="{count: object.tracks_count}">
+                      %{ count } track
+                    </translate>
                   </template>
                   <template v-else>
-                    {{ $t('Unkwnown') }}
+                    {{ $gettext('Unkwnown') }}
                   </template>
                 </td>
                 <td></td>
               </tr>
               <tr>
-                <td>{{ $t('Last fetched') }}</td>
+                <td>{{ $gettext('Last fetched') }}</td>
                 <td>
                   <human-date v-if="object.fetched_date" :date="object.fetched_date"></human-date>
                   <template v-else>Never</template>
@@ -102,10 +107,10 @@
                     @click="scan"
                     v-if="!scanTrigerred"
                     :class="['ui', 'basic', {loading: isScanLoading}, 'button']">
-                    <i class="sync icon"></i> {{ $t('Trigger scan') }}
+                    <i class="sync icon"></i> {{ $gettext('Trigger scan') }}
                   </button>
                   <button v-else class="ui success button">
-                    <i class="check icon"></i> {{ $t('Scan triggered!') }}
+                    <i class="check icon"></i> {{ $gettext('Scan triggered!') }}
                   </button>
 
                 </td>
@@ -115,10 +120,10 @@
           </table>
         </div>
         <div class="ui hidden divider"></div>
-        <button @click="fetchData" class="ui basic button">{{ $t('Refresh') }}</button>
+        <button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
       </div>
       <div class="ui vertical stripe segment">
-        <h2>{{ $t('Tracks available in this library') }}</h2>
+        <h2>{{ $gettext('Tracks available in this library') }}</h2>
         <library-track-table v-if="!isLoading" :filters="{library: id}"></library-track-table>
       </div>
     </template>
diff --git a/front/src/views/federation/LibraryFollowersList.vue b/front/src/views/federation/LibraryFollowersList.vue
index ce79e478747267637a418cb31f979a0421d1f98a..98210006049b49edcb8a0db2ed8fb899df4a3d74 100644
--- a/front/src/views/federation/LibraryFollowersList.vue
+++ b/front/src/views/federation/LibraryFollowersList.vue
@@ -1,9 +1,9 @@
 <template>
   <div v-title="'Followers'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Browsing followers') }}</h2>
+      <h2 class="ui header">{{ $gettext('Browsing followers') }}</h2>
       <p>
-        {{ $t('Be careful when accepting follow requests, as it means the follower will have access to your entire library.') }}
+        {{ $gettext('Be careful when accepting follow requests, as it means the follower will have access to your entire library.') }}
       </p>
       <div class="ui hidden divider"></div>
       <library-follow-table></library-follow-table>
diff --git a/front/src/views/federation/LibraryList.vue b/front/src/views/federation/LibraryList.vue
index d067705fa7d52a399a9f71cc59df9c8071aaa6ad..d5ec9789e3964f9716f89431a1251e1c737a3b32 100644
--- a/front/src/views/federation/LibraryList.vue
+++ b/front/src/views/federation/LibraryList.vue
@@ -1,22 +1,22 @@
 <template>
   <div v-title="'Libraries'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Browsing libraries') }}</h2>
+      <h2 class="ui header">{{ $gettext('Browsing libraries') }}</h2>
       <router-link
         class="ui basic green button"
         :to="{name: 'federation.libraries.scan'}">
         <i class="plus icon"></i>
-        {{ $t('Add a new library') }}
+        {{ $gettext('Add a new library') }}
       </router-link>
       <div class="ui hidden divider"></div>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <label>{{ $t('Search') }}</label>
+            <label>{{ $gettext('Search') }}</label>
             <input class="search" type="text" v-model="query" placeholder="Enter an library domain name..."/>
           </div>
           <div class="field">
-            <label>{{ $t('Ordering') }}</label>
+            <label>{{ $gettext('Ordering') }}</label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -24,14 +24,14 @@
             </select>
           </div>
           <div class="field">
-            <label>{{ $t('Ordering direction') }}</label>
+            <label>{{ $gettext('Ordering direction') }}</label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+">{{ $t('Ascending') }}</option>
-              <option value="-">{{ $t('Descending') }}</option>
+              <option value="+">{{ $gettext('Ascending') }}</option>
+              <option value="-">{{ $gettext('Descending') }}</option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $t('Results per page') }}</label>
+            <label>{{ $gettext('Results per page') }}</label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
diff --git a/front/src/views/federation/LibraryTrackList.vue b/front/src/views/federation/LibraryTrackList.vue
index b9a78527f16eac6eae0fb6365ede85cd35f0248f..566be694982dc23a3df452d726108dedd2d7cdf6 100644
--- a/front/src/views/federation/LibraryTrackList.vue
+++ b/front/src/views/federation/LibraryTrackList.vue
@@ -1,7 +1,7 @@
 <template>
   <div v-title="'Federated tracks'">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Browsing federated tracks') }}</h2>
+      <h2 class="ui header">{{ $gettext('Browsing federated tracks') }}</h2>
       <div class="ui hidden divider"></div>
       <library-track-table :show-library="true"></library-track-table>
     </div>
diff --git a/front/src/views/instance/Timeline.vue b/front/src/views/instance/Timeline.vue
index a5647b7bf919759dc443d4f95d0def829ffb20d5..0f3f3e6d1b291aa5a4d6c7af2d89fe363b03ef9d 100644
--- a/front/src/views/instance/Timeline.vue
+++ b/front/src/views/instance/Timeline.vue
@@ -2,10 +2,10 @@
   <div class="main pusher" v-title="'Instance Timeline'">
     <div class="ui vertical center aligned stripe segment">
       <div v-if="isLoading" :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
-        <div class="ui text loader">{{ $t('Loading timeline...') }}</div>
+        <div class="ui text loader">{{ $gettext('Loading timeline...') }}</div>
       </div>
       <div v-else class="ui text container">
-        <h1 class="ui header">{{ $t('Recent activity on this instance') }}</h1>
+        <h1 class="ui header">{{ $gettext('Recent activity on this instance') }}</h1>
         <div class="ui feed">
           <component
             class="event"
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index 7a378fa67c5bd6ed40417113b188bcc2c4d45ab9..b76f2f2c00767359ed7288fd286fe78a4f2c2b7a 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -1,6 +1,6 @@
 <template>
   <div>
-    <div v-if="isLoading" class="ui vertical segment" v-title="$t('Playlist')">
+    <div v-if="isLoading" class="ui vertical segment" v-title="$gettext('Playlist')">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
     <div v-if="!isLoading && playlist" class="ui head vertical center aligned stripe segment" v-title="playlist.name">
@@ -9,28 +9,33 @@
           <i class="circular inverted list yellow icon"></i>
           <div class="content">
             {{ playlist.name }}
-            <i18next tag="div" class="sub header" path="Playlist containing {%0%} tracks, by {%1%}">
-              {{ playlistTracks.length }}
-              <username :username="playlist.user.username"></username>
-            </i18next>
+            <translate
+              tag="div"
+              class="sub header"
+              translate-plural="Playlist containing %{ count } tracks, by %{ username }"
+              :translate-n="playlistTracks.length"
+              :translate-params="{count: playlistTracks.length, username: playlist.user.username}">
+              Playlist containing %{ count } track, by %{ username }
+            </translate>
           </div>
         </h2>
         <div class="ui hidden divider"></div>
-        </button>
-        <play-button class="orange" :tracks="tracks">{{ $t('Play all') }}</play-button>
+        <play-button class="orange" :tracks="tracks">{{ $gettext('Play all') }}</play-button>
         <button
           class="ui icon button"
           v-if="playlist.user.id === $store.state.auth.profile.id"
           @click="edit = !edit">
           <i class="pencil icon"></i>
-          <template v-if="edit">{{ $t('End edition') }}</template>
-          <template v-else>{{ $t('Edit...') }}</template>
+          <template v-if="edit">{{ $gettext('End edition') }}</template>
+          <template v-else>{{ $gettext('Edit...') }}</template>
         </button>
-        <dangerous-button class="labeled icon" :action="deletePlaylist">
-          <i class="trash icon"></i> {{ $t('Delete') }}
-          <p slot="modal-header">{{ $t('Do you want to delete the playlist "{% playlist %}"?', {playlist: playlist.name}) }}</p>
-          <p slot="modal-content">{{ $t('This will completely delete this playlist and cannot be undone.') }}</p>
-          <p slot="modal-confirm">{{ $t('Delete playlist') }}</p>
+        <dangerous-button v-if="playlist.user.id === $store.state.auth.profile.id" class="labeled icon" :action="deletePlaylist">
+          <i class="trash icon"></i> {{ $gettext('Delete') }}
+          <p slot="modal-header">
+            <translate :translate-params="{playlist: playlist.name}">Do you want to delete the playlist "%{ playlist }"?</translate>
+          </p>
+          <p slot="modal-content">{{ $gettext('This will completely delete this playlist and cannot be undone.') }}</p>
+          <p slot="modal-confirm">{{ $gettext('Delete playlist') }}</p>
         </dangerous-button>
       </div>
     </div>
diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue
index cc6ad49bb84d8b98edbf00d4d6df009613570c03..ca59612d8354e1fff14b66844e6578fc8695f690 100644
--- a/front/src/views/playlists/List.vue
+++ b/front/src/views/playlists/List.vue
@@ -1,21 +1,21 @@
 <template>
-  <div v-title="$t('Playlists')">
+  <div v-title="$gettext('Playlists')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $t('Browsing playlists') }}</h2>
+      <h2 class="ui header">{{ $gettext('Browsing playlists') }}</h2>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <template v-if="$store.state.auth.authenticated">
           <button
             @click="$store.commit('playlists/chooseTrack', null)"
-            class="ui basic green button">{{ $t('Manage your playlists') }}</button>
+            class="ui basic green button">{{ $gettext('Manage your playlists') }}</button>
           <div class="ui hidden divider"></div>
         </template>
         <div class="fields">
           <div class="field">
-            <label>{{ $t('Search') }}</label>
-            <input type="text" v-model="query" :placeholder="$t('Enter an playlist name...')"/>
+            <label>{{ $gettext('Search') }}</label>
+            <input type="text" v-model="query" :placeholder="$gettext('Enter an playlist name...')"/>
           </div>
           <div class="field">
-            <label>{{ $t('Ordering') }}</label>
+            <label>{{ $gettext('Ordering') }}</label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -23,14 +23,14 @@
             </select>
           </div>
           <div class="field">
-            <label>{{ $t('Ordering direction') }}</label>
+            <label>{{ $gettext('Ordering direction') }}</label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+">{{ $t('Ascending') }}</option>
-              <option value="-">{{ $t('Descending') }}</option>
+              <option value="+">{{ $gettext('Ascending') }}</option>
+              <option value="-">{{ $gettext('Descending') }}</option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $t('Results per page') }}</label>
+            <label>{{ $gettext('Results per page') }}</label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
diff --git a/front/vendor/vue-i18n-xgettext/extractor.js b/front/vendor/vue-i18n-xgettext/extractor.js
deleted file mode 100644
index e8f2bde4faaa838cc692ef5ff7883f780776b607..0000000000000000000000000000000000000000
--- a/front/vendor/vue-i18n-xgettext/extractor.js
+++ /dev/null
@@ -1,203 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
-
-var _cheerio = require('cheerio');
-
-var _cheerio2 = _interopRequireDefault(_cheerio);
-
-var _pofile = require('pofile');
-
-var _pofile2 = _interopRequireDefault(_pofile);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-var tRegexp = new RegExp('.*\\$t\\([\'\"\`](.*)[\'\"\`]\\).*', 'g');
-
-var Translation = function () {
-  function Translation(filename, lineNumber, msg) {
-    _classCallCheck(this, Translation);
-
-    this.filename = filename;
-    this.lineNumber = lineNumber;
-    this.msg = msg;
-  }
-
-  _createClass(Translation, [{
-    key: 'toPofileItem',
-    value: function toPofileItem() {
-      var item = new _pofile2.default.Item();
-      item.msgid = this.msg;
-      item.msgctxt = null;
-      item.references = [this.filename + ':' + this.lineNumber];
-      item.msgid_plural = null;
-      item.msgstr = [];
-      item.extractedComments = [];
-      return item;
-    }
-  }]);
-
-  return Translation;
-}();
-
-var Extractor = function () {
-  function Extractor(options) {
-    _classCallCheck(this, Extractor);
-
-    this.options = _extends({
-      startDelim: '{{',
-      endDelim: '}}',
-      attributes: ['path']
-    }, options);
-    this.translations = [];
-  }
-
-  _createClass(Extractor, [{
-    key: 'parse',
-    value: function parse(filename, content) {
-      var $ = _cheerio2.default.load(content, {
-        decodeEntities: false,
-        withStartIndices: true
-      });
-
-      var translations = $('template *').map(function (i, el) {
-        var node = $(el);
-        var msg = null;
-        if (node['0'].name === 'i18next') {
-          // here, we extract the translations from <i18next path="string">
-          msg = this.extractTranslationMessageFromI18Next(node);
-        }
-        if (msg) {
-          var truncatedText = content.substr(0, el.startIndex);
-          var lineNumber = truncatedText.split(/\r\n|\r|\n/).length;
-          return new Translation(filename, lineNumber, msg);
-        }
-      }.bind(this)).get();
-      var scriptTranslations = $('script,template').map(function (i, el) {
-        // here, we extract the translations from $t('string')
-        // within scripts and templates
-        var script = $(el).text();
-        var lines = script.split('\n');
-        var _translations = [];
-        lines.forEach(function (line) {
-          var truncatedText = content.substr(0, el.startIndex);
-          var matches;
-          while ((matches = tRegexp.exec(line)) !== null) {
-            var lineNumber = truncatedText.split(/\r\n|\r|\n/).length;
-            _translations.push(new Translation(filename, lineNumber, matches[1]));
-          }
-        })
-        return _translations
-      }.bind(this)).get();
-      this.translations = this.translations.concat(translations);
-      this.translations = this.translations.concat(scriptTranslations);
-    }
-  }, {
-    key: 'extractTranslationMessageFromI18Next',
-    value: function extractTranslationMessageFromI18Next(node) {
-      // extract from attributes
-      var _iteratorNormalCompletion = true;
-      var _didIteratorError = false;
-      var _iteratorError = undefined;
-
-      try {
-        for (var _iterator = this.options.attributes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
-          var attr = _step.value;
-
-          if (node.attr('path')) {
-            return node.attr('path');
-          }
-        }
-      } catch (err) {
-        _didIteratorError = true;
-        _iteratorError = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion && _iterator.return) {
-            _iterator.return();
-          }
-        } finally {
-          if (_didIteratorError) {
-            throw _iteratorError;
-          }
-        }
-      }
-    }
-  }, {
-    key: 'toPofile',
-    value: function toPofile() {
-      var pofile = new _pofile2.default();
-      pofile.headers = {
-        'Last-Translator': 'vue-i18n-xgettext',
-        'Content-Type': 'text/plain; charset=UTF-8',
-        'Content-Transfer-Encoding': '8bit',
-        'MIME-Version': '1.1'
-      };
-
-      var itemMapping = {};
-      var _iteratorNormalCompletion2 = true;
-      var _didIteratorError2 = false;
-      var _iteratorError2 = undefined;
-
-      try {
-        for (var _iterator2 = this.translations[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
-          var translation = _step2.value;
-
-          var _item = translation.toPofileItem();
-          if (!itemMapping[_item.msgid]) {
-            itemMapping[_item.msgid] = _item;
-          } else {
-            var oldItem = itemMapping[_item.msgid];
-            // TODO: deal with plurals/context
-            if (_item.references.length && oldItem.references.indexOf(_item.references[0]) === -1) {
-              oldItem.references.push(_item.references[0]);
-            }
-            if (_item.extractedComments.length && soldItem.extractedComments.indexOf(_item.extractedComments[0]) === -1) {
-              oldItem.extractedComments.push(_item.extractedComments[0]);
-            }
-          }
-        }
-      } catch (err) {
-        _didIteratorError2 = true;
-        _iteratorError2 = err;
-      } finally {
-        try {
-          if (!_iteratorNormalCompletion2 && _iterator2.return) {
-            _iterator2.return();
-          }
-        } finally {
-          if (_didIteratorError2) {
-            throw _iteratorError2;
-          }
-        }
-      }
-
-      for (var msgid in itemMapping) {
-        var item = itemMapping[msgid];
-        pofile.items.push(item);
-      }
-
-      pofile.items.sort(function (a, b) {
-        return a.msgid.localeCompare(b.msgid);
-      });
-      return pofile;
-    }
-  }, {
-    key: 'toString',
-    value: function toString() {
-      return this.toPofile().toString();
-    }
-  }]);
-
-  return Extractor;
-}();
-
-exports.default = Extractor;
-//# sourceMappingURL=extractor.js.map
diff --git a/front/vendor/vue-i18n-xgettext/index.js b/front/vendor/vue-i18n-xgettext/index.js
deleted file mode 100755
index 2b8886e478253edf4461dc44570bd860a131738c..0000000000000000000000000000000000000000
--- a/front/vendor/vue-i18n-xgettext/index.js
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-var _fs = require('fs');
-
-var _fs2 = _interopRequireDefault(_fs);
-
-var _minimist = require('minimist');
-
-var _minimist2 = _interopRequireDefault(_minimist);
-
-var _extractor = require('./extractor.js');
-
-var _extractor2 = _interopRequireDefault(_extractor);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var argv = (0, _minimist2.default)(process.argv.slice(2));
-var files = argv._.sort() || [];
-var attributes = argv.attribute || [];
-var outputFile = argv.output || null;
-
-if (!files || files.length === 0) {
-  console.log('Usage: vue-i18n-xgettext [--attribute ATTRIBUTE] [--output OUTPUT_FILE] FILES');
-  process.exit(1);
-}
-
-var defaultAttributes = ['v-text'];
-var finalAttributes = defaultAttributes;
-if (typeof attributes === 'string') {
-  finalAttributes.push(attributes);
-} else {
-  finalAttributes = finalAttributes.concat(attributes);
-}
-
-var extractor = new _extractor2.default({
-  attributes: finalAttributes
-});
-
-files.forEach(function (filename) {
-  var extension = filename.split('.').pop();
-  if (extension !== 'vue') {
-    console.log('file ' + filename + ' with extension ' + extension + ' will not be processed (skipped)');
-    return;
-  }
-
-  var data = _fs2.default.readFileSync(filename, { encoding: 'utf-8' }).toString();
-
-  try {
-    extractor.parse(filename, data);
-  } catch (e) {
-    console.trace(e);
-    process.exit(1);
-  }
-});
-
-var output = extractor.toString();
-if (outputFile) {
-  _fs2.default.writeFileSync(outputFile, output);
-} else {
-  console.log(output);
-}
-//# sourceMappingURL=index.js.map
diff --git a/front/yarn.lock b/front/yarn.lock
index 7cf3cb116a011e36dab7d92df31341c2da9eee1c..5c69d78893cdf414feba6c5412acbba6edb4f380 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -8,6 +8,34 @@
   dependencies:
     deepmerge "^2.0.0"
 
+"@types/babel-types@*", "@types/babel-types@^7.0.0":
+  version "7.0.4"
+  resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8"
+
+"@types/babylon@^6.16.2":
+  version "6.16.3"
+  resolved "https://registry.yarnpkg.com/@types/babylon/-/babylon-6.16.3.tgz#c2937813a89fcb5e79a00062fc4a8b143e7237bb"
+  dependencies:
+    "@types/babel-types" "*"
+
+"@types/node@*":
+  version "10.5.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.1.tgz#d578446f4abff5c0b49ade9b4e5274f6badaadfc"
+
+"@vue/component-compiler-utils@^1.2.1":
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-1.3.1.tgz#686f0b913d59590ae327b2a1cb4b6d9b931bbe0e"
+  dependencies:
+    consolidate "^0.15.1"
+    hash-sum "^1.0.2"
+    lru-cache "^4.1.2"
+    merge-source-map "^1.1.0"
+    postcss "^6.0.20"
+    postcss-selector-parser "^3.1.1"
+    prettier "^1.13.0"
+    source-map "^0.5.6"
+    vue-template-es2015-compiler "^1.6.0"
+
 abbrev@1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -36,17 +64,23 @@ acorn-dynamic-import@^2.0.0:
   dependencies:
     acorn "^4.0.3"
 
+acorn-globals@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
+  dependencies:
+    acorn "^4.0.4"
+
 acorn-jsx@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
   dependencies:
     acorn "^3.0.4"
 
-acorn@^3.0.4:
+acorn@^3.0.4, acorn@^3.1.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
 
-acorn@^4.0.3:
+acorn@^4.0.3, acorn@^4.0.4, acorn@~4.0.2:
   version "4.0.13"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
 
@@ -54,6 +88,10 @@ acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
   version "5.5.3"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
 
+acorn@^5.5.3:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
+
 after@0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
@@ -227,6 +265,10 @@ arrify@^1.0.0, arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
 
+asap@~2.0.3:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+
 asn1.js@^4.0.0:
   version "4.10.1"
   resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
@@ -1307,30 +1349,34 @@ chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+chalk@^2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+character-parser@^2.1.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0"
+  dependencies:
+    is-regex "^1.0.3"
+
 check-types@^7.3.0:
   version "7.3.0"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
 
-cheerio@^0.22.0:
-  version "0.22.0"
-  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+cheerio@^1.0.0-rc.2:
+  version "1.0.0-rc.2"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db"
   dependencies:
     css-select "~1.2.0"
     dom-serializer "~0.1.0"
     entities "~1.1.1"
     htmlparser2 "^3.9.1"
-    lodash.assignin "^4.0.9"
-    lodash.bind "^4.1.4"
-    lodash.defaults "^4.0.1"
-    lodash.filter "^4.4.0"
-    lodash.flatten "^4.2.0"
-    lodash.foreach "^4.3.0"
-    lodash.map "^4.4.0"
-    lodash.merge "^4.4.0"
-    lodash.pick "^4.2.1"
-    lodash.reduce "^4.4.0"
-    lodash.reject "^4.4.0"
-    lodash.some "^4.4.0"
+    lodash "^4.15.0"
+    parse5 "^3.0.1"
 
 chokidar@^1.4.1:
   version "1.7.0"
@@ -1405,7 +1451,7 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
-clean-css@4.1.x:
+clean-css@4.1.x, clean-css@^4.1.11:
   version "4.1.11"
   resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a"
   dependencies:
@@ -1545,7 +1591,7 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@2.15.x, commander@^2.11.0, commander@^2.13.0, commander@^2.9.0, commander@~2.15.0:
+commander@2.15.x, commander@^2.13.0, commander@^2.9.0, commander@~2.15.0:
   version "2.15.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
 
@@ -1632,6 +1678,21 @@ consolidate@^0.14.0:
   dependencies:
     bluebird "^3.1.1"
 
+consolidate@^0.15.1:
+  version "0.15.1"
+  resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
+  dependencies:
+    bluebird "^3.1.1"
+
+constantinople@^3.0.1:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.2.tgz#d45ed724f57d3d10500017a7d3a889c1381ae647"
+  dependencies:
+    "@types/babel-types" "^7.0.0"
+    "@types/babylon" "^6.16.2"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+
 constants-browserify@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@@ -2116,6 +2177,10 @@ doctrine@^2.0.0:
   dependencies:
     esutils "^2.0.2"
 
+doctypes@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
+
 dom-converter@~0.1:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b"
@@ -2182,6 +2247,12 @@ domutils@^1.5.1:
     dom-serializer "0"
     domelementtype "1"
 
+dot-prop@^4.1.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+  dependencies:
+    is-obj "^1.0.0"
+
 duplexer@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -2195,6 +2266,18 @@ duplexify@^3.4.2, duplexify@^3.5.3:
     readable-stream "^2.0.0"
     stream-shift "^1.0.0"
 
+easygettext@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/easygettext/-/easygettext-2.5.0.tgz#56c695816482cb6ab29a3c3454998274d8845972"
+  dependencies:
+    "@vue/component-compiler-utils" "^1.2.1"
+    acorn "^5.5.3"
+    cheerio "^1.0.0-rc.2"
+    minimist "^1.2.0"
+    pofile "^1.0.10"
+    pug "^2.0.3"
+    vue-template-compiler "^2.5.16"
+
 ecc-jsbn@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@@ -2251,12 +2334,6 @@ encodeurl@~1.0.1, encodeurl@~1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
 
-encoding@^0.1.12:
-  version "0.1.12"
-  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
-  dependencies:
-    iconv-lite "~0.4.13"
-
 end-of-stream@^1.0.0, end-of-stream@^1.1.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -2993,10 +3070,6 @@ fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
 
-fs@0.0.1-security:
-  version "0.0.1-security"
-  resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4"
-
 fsevents@^1.0.0, fsevents@^1.1.2:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8"
@@ -3098,13 +3171,6 @@ getpass@^0.1.1:
   dependencies:
     assert-plus "^1.0.0"
 
-gettext-parser@^1.3.0:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-1.3.1.tgz#74b7a99e4b5fa8daab11fa515e8a582480448a12"
-  dependencies:
-    encoding "^0.1.12"
-    safe-buffer "^5.1.1"
-
 glob-base@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -3548,41 +3614,10 @@ https-proxy-agent@1:
     debug "2"
     extend "3"
 
-i18next-conv@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/i18next-conv/-/i18next-conv-6.0.0.tgz#875a27bfb069db894f7b0a1484e0052100bc9383"
-  dependencies:
-    bluebird "^3.5.1"
-    chalk "^2.1.0"
-    commander "^2.11.0"
-    gettext-parser "^1.3.0"
-    mkdirp "^0.5.1"
-    node-gettext "^2.0.0"
-
-i18next-fetch-backend@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/i18next-fetch-backend/-/i18next-fetch-backend-0.1.0.tgz#18b67920d0e605e616f93bbdf897e59adf9c9c05"
-  dependencies:
-    i18next-xhr-backend "^1.4.3"
-
-i18next-xhr-backend@^1.4.3:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-1.5.1.tgz#50282610780c6a696d880dfa7f4ac1d01e8c3ad5"
-
-i18next@^11.1.1:
-  version "11.1.1"
-  resolved "https://registry.yarnpkg.com/i18next/-/i18next-11.1.1.tgz#df3a683542d7756a8aa8d6b884b61141239c394a"
-
 iconv-lite@0.4.19:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
 
-iconv-lite@~0.4.13:
-  version "0.4.21"
-  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.21.tgz#c47f8733d02171189ebc4a400f3218d348094798"
-  dependencies:
-    safer-buffer "^2.1.0"
-
 icss-replace-symbols@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@@ -3780,6 +3815,13 @@ is-equal-shallow@^0.1.3:
   dependencies:
     is-primitive "^2.0.0"
 
+is-expression@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f"
+  dependencies:
+    acorn "~4.0.2"
+    object-assign "^4.0.1"
+
 is-extendable@^0.1.0, is-extendable@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -3866,6 +3908,10 @@ is-number@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
 
+is-obj@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+
 is-odd@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24"
@@ -3906,10 +3952,20 @@ is-primitive@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
 
+is-promise@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
 is-property@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
 
+is-regex@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  dependencies:
+    has "^1.0.1"
+
 is-resolvable@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -4022,6 +4078,10 @@ js-logger@^1.3.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/js-logger/-/js-logger-1.4.1.tgz#f0230dc5e84e120f213d6e5a6b767a913d290335"
 
+js-stringify@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db"
+
 js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@@ -4105,6 +4165,13 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
+jstransformer@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3"
+  dependencies:
+    is-promise "^2.0.0"
+    promise "^7.0.1"
+
 jwt-decode@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"
@@ -4364,14 +4431,6 @@ lodash.assign@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
 
-lodash.assignin@^4.0.9:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
-
-lodash.bind@^4.1.4:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
-
 lodash.camelcase@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@@ -4396,10 +4455,6 @@ lodash.create@3.1.1:
     lodash._basecreate "^3.0.0"
     lodash._isiterateecall "^3.0.0"
 
-lodash.defaults@^4.0.1:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
-
 lodash.defaultsdeep@4.3.2:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz#6c1a586e6c5647b0e64e2d798141b8836158be8a"
@@ -4411,22 +4466,6 @@ lodash.defaultsdeep@4.3.2:
     lodash.mergewith "^4.0.0"
     lodash.rest "^4.0.0"
 
-lodash.filter@^4.4.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
-
-lodash.flatten@^4.2.0:
-  version "4.4.0"
-  resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
-
-lodash.foreach@^4.3.0:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
-
-lodash.get@^4.4.2:
-  version "4.4.2"
-  resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
-
 lodash.isarguments@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@@ -4451,42 +4490,18 @@ lodash.keysin@^4.0.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-4.2.0.tgz#8cc3fb35c2d94acc443a1863e02fa40799ea6f28"
 
-lodash.map@^4.4.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
-
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
-lodash.merge@^4.4.0:
-  version "4.6.1"
-  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
-
 lodash.mergewith@^4.0.0, lodash.mergewith@^4.6.0:
   version "4.6.1"
   resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
 
-lodash.pick@^4.2.1:
-  version "4.4.0"
-  resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
-
-lodash.reduce@^4.4.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
-
-lodash.reject@^4.4.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
-
 lodash.rest@^4.0.0:
   version "4.0.5"
   resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa"
 
-lodash.some@^4.4.0:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
-
 lodash.tail@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664"
@@ -4503,6 +4518,10 @@ lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lo
   version "4.17.5"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
 
+lodash@^4.15.0:
+  version "4.17.10"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+
 log-symbols@^2.1.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@@ -4554,6 +4573,13 @@ lru-cache@^3.2.0:
   dependencies:
     pseudomap "^1.0.1"
 
+lru-cache@^4.1.2:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
+  dependencies:
+    pseudomap "^1.0.2"
+    yallist "^2.1.2"
+
 lru-cache@~2.6.5:
   version "2.6.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
@@ -4649,6 +4675,12 @@ merge-descriptors@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
 
+merge-source-map@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
+  dependencies:
+    source-map "^0.6.1"
+
 methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -4933,12 +4965,6 @@ no-case@^2.2.0:
   dependencies:
     lower-case "^1.1.1"
 
-node-gettext@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/node-gettext/-/node-gettext-2.0.0.tgz#f1dc1237cdc546f51593da340304b8beba5b8525"
-  dependencies:
-    lodash.get "^4.4.2"
-
 node-gyp@^3.3.1:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60"
@@ -5346,6 +5372,12 @@ parse-json@^2.2.0:
   dependencies:
     error-ex "^1.2.0"
 
+parse5@^3.0.1:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
+  dependencies:
+    "@types/node" "*"
+
 parsejson@0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab"
@@ -5506,9 +5538,9 @@ pluralize@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
 
-pofile@^1.0.2:
-  version "1.0.10"
-  resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.10.tgz#503dda9499403984e83ff4489ba2d80af276172a"
+pofile@^1.0.10:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"
 
 posix-character-classes@^0.1.0:
   version "0.1.1"
@@ -5737,6 +5769,14 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
+postcss-selector-parser@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
+  dependencies:
+    dot-prop "^4.1.1"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
 postcss-svgo@^2.1.1:
   version "2.1.6"
   resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
@@ -5783,6 +5823,14 @@ postcss@^6.0.1:
     source-map "^0.6.1"
     supports-color "^5.3.0"
 
+postcss@^6.0.20:
+  version "6.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -5795,6 +5843,10 @@ preserve@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
 
+prettier@^1.13.0:
+  version "1.13.7"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
+
 pretty-error@^2.0.2:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3"
@@ -5822,6 +5874,12 @@ promise-inflight@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
 
+promise@^7.0.1:
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+  dependencies:
+    asap "~2.0.3"
+
 proto-list@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -5864,6 +5922,99 @@ public-encrypt@^4.0.0:
     parse-asn1 "^5.0.0"
     randombytes "^2.0.1"
 
+pug-attrs@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.3.tgz#a3095f970e64151f7bdad957eef55fb5d7905d15"
+  dependencies:
+    constantinople "^3.0.1"
+    js-stringify "^1.0.1"
+    pug-runtime "^2.0.4"
+
+pug-code-gen@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.1.tgz#0951ec83225d74d8cfc476a7f99a259b5f7d050c"
+  dependencies:
+    constantinople "^3.0.1"
+    doctypes "^1.1.0"
+    js-stringify "^1.0.1"
+    pug-attrs "^2.0.3"
+    pug-error "^1.3.2"
+    pug-runtime "^2.0.4"
+    void-elements "^2.0.1"
+    with "^5.0.0"
+
+pug-error@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.2.tgz#53ae7d9d29bb03cf564493a026109f54c47f5f26"
+
+pug-filters@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.0.tgz#27165555bc04c236e4aa2b0366246dfa021b626e"
+  dependencies:
+    clean-css "^4.1.11"
+    constantinople "^3.0.1"
+    jstransformer "1.0.0"
+    pug-error "^1.3.2"
+    pug-walk "^1.1.7"
+    resolve "^1.1.6"
+    uglify-js "^2.6.1"
+
+pug-lexer@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.0.0.tgz#210c18457ef2e1760242740c5e647bd794cec278"
+  dependencies:
+    character-parser "^2.1.1"
+    is-expression "^3.0.0"
+    pug-error "^1.3.2"
+
+pug-linker@^3.0.5:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.5.tgz#9e9a7ae4005682d027deeb96b000f88eeb83a02f"
+  dependencies:
+    pug-error "^1.3.2"
+    pug-walk "^1.1.7"
+
+pug-load@^2.0.11:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.11.tgz#e648e57ed113fe2c1f45d57858ea2bad6bc01527"
+  dependencies:
+    object-assign "^4.1.0"
+    pug-walk "^1.1.7"
+
+pug-parser@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.0.tgz#e394ad9b3fca93123940aff885c06e44ab7e68e4"
+  dependencies:
+    pug-error "^1.3.2"
+    token-stream "0.0.1"
+
+pug-runtime@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.4.tgz#e178e1bda68ab2e8c0acfc9bced2c54fd88ceb58"
+
+pug-strip-comments@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.3.tgz#f1559592206edc6f85310dacf4afb48a025af59f"
+  dependencies:
+    pug-error "^1.3.2"
+
+pug-walk@^1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.7.tgz#c00d5c5128bac5806bec15d2b7e7cdabe42531f3"
+
+pug@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.3.tgz#71cba82537c95a5eab7ed04696e4221f53aa878e"
+  dependencies:
+    pug-code-gen "^2.0.1"
+    pug-filters "^3.1.0"
+    pug-lexer "^4.0.0"
+    pug-linker "^3.0.5"
+    pug-load "^2.0.11"
+    pug-parser "^5.0.0"
+    pug-runtime "^2.0.4"
+    pug-strip-comments "^1.0.3"
+
 pump@^2.0.0, pump@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
@@ -6364,10 +6515,6 @@ safe-regex@^1.1.0:
   dependencies:
     ret "~0.1.10"
 
-safer-buffer@^2.1.0:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
-
 samsam@1.x, samsam@^1.1.3:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50"
@@ -6949,6 +7096,12 @@ supports-color@^5.3.0:
   dependencies:
     has-flag "^3.0.0"
 
+supports-color@^5.4.0:
+  version "5.4.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+  dependencies:
+    has-flag "^3.0.0"
+
 svgo@^0.7.0:
   version "0.7.2"
   resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
@@ -7090,6 +7243,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
     regex-not "^1.0.2"
     safe-regex "^1.1.0"
 
+token-stream@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a"
+
 toposort@^1.0.0:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.6.tgz#c31748e55d210effc00fdcdc7d6e68d7d7bb9cec"
@@ -7172,7 +7329,7 @@ uglify-js@3.3.x:
     commander "~2.15.0"
     source-map "~0.6.1"
 
-uglify-js@^2.6, uglify-js@^2.8.29:
+uglify-js@^2.6, uglify-js@^2.6.1, uglify-js@^2.8.29:
   version "2.8.29"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
   dependencies:
@@ -7357,23 +7514,18 @@ vm-browserify@0.0.4:
   dependencies:
     indexof "0.0.1"
 
-void-elements@^2.0.0:
+void-elements@^2.0.0, void-elements@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
 
+vue-gettext@^2.0.31:
+  version "2.0.31"
+  resolved "https://registry.yarnpkg.com/vue-gettext/-/vue-gettext-2.0.31.tgz#0f113bea6aed747e2a809ae4a0db83eaac0b07fc"
+
 vue-hot-reload-api@^2.1.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
 
-vue-i18n-xgettext@^0.0.4:
-  version "0.0.4"
-  resolved "https://registry.yarnpkg.com/vue-i18n-xgettext/-/vue-i18n-xgettext-0.0.4.tgz#80ad654e65fb33bb5fcbd96f338f55605ab1a06f"
-  dependencies:
-    cheerio "^0.22.0"
-    fs "0.0.1-security"
-    minimist "^1.2.0"
-    pofile "^1.0.2"
-
 vue-lazyload@^1.1.4:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/vue-lazyload/-/vue-lazyload-1.2.2.tgz#73335ed32db25264f5957df1a21d277823423743"
@@ -7416,14 +7568,14 @@ vue-style-loader@^3.0.0, vue-style-loader@^3.0.1:
     hash-sum "^1.0.2"
     loader-utils "^1.0.2"
 
-vue-template-compiler@^2.3.3:
+vue-template-compiler@^2.3.3, vue-template-compiler@^2.5.16:
   version "2.5.16"
   resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.16.tgz#93b48570e56c720cdf3f051cc15287c26fbd04cb"
   dependencies:
     de-indent "^1.0.2"
     he "^1.1.0"
 
-vue-template-es2015-compiler@^1.2.2:
+vue-template-es2015-compiler@^1.2.2, vue-template-es2015-compiler@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
 
@@ -7431,7 +7583,7 @@ vue-upload-component@^2.7.4:
   version "2.8.5"
   resolved "https://registry.yarnpkg.com/vue-upload-component/-/vue-upload-component-2.8.5.tgz#680de2934f55c6a38da7b382d0f37fd79995dd42"
 
-vue@^2.0.0, vue@^2.3.3:
+vue@^2.0.0, vue@^2.5.16:
   version "2.5.16"
   resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085"
 
@@ -7575,6 +7727,13 @@ window-size@0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
 
+with@^5.0.0:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe"
+  dependencies:
+    acorn "^3.1.0"
+    acorn-globals "^3.0.0"
+
 wordwrap@0.0.2:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
diff --git a/po/.gitkeep b/po/.gitkeep
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/po/funkwhale.pot b/po/funkwhale.pot
deleted file mode 100644
index 9a9dd948f7e18af7ba52984ace95f696d75e17d0..0000000000000000000000000000000000000000
--- a/po/funkwhale.pot
+++ /dev/null
@@ -1,613 +0,0 @@
-msgid ""
-msgstr ""
-"Project-Id-Version: Funkwhale 0.8\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-23 15:49-0700\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-
-msgid "An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running"
-msgstr "An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running"
-
-msgid "Links"
-msgstr "Links"
-
-msgid "About this instance"
-msgstr "About this instance"
-
-msgid "Official website"
-msgstr "Official website"
-
-msgid "Source code"
-msgstr "Source code"
-
-msgid "Issue tracker"
-msgstr "Issue tracker"
-
-msgid "About funkwhale"
-msgstr "About funkwhale"
-
-msgid "Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"
-msgstr "Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"
-
-msgid "The funkwhale logo was kindly designed and provided by Francis Gading."
-msgstr "The funkwhale logo was kindly designed and provided by Francis Gading."
-
-msgid "{%0%} favorited a track {%1%}"
-msgstr "{%0%} favorited a track {%1%}"
-
-msgid "from album {%0%}, by {%1%}"
-msgstr "from album {%0%}, by {%1%}"
-
-msgid ", by {%0%}"
-msgstr ", by {%0%}"
-
-msgid "{%0%} listened to a track {%1%}"
-msgstr "{%0%} listened to a track {%1%}"
-
-msgid "Show {%0%} more tracks"
-msgstr "Show {%0%} more tracks"
-
-msgid "Collapse"
-msgstr "Collapse"
-
-msgid "Play all"
-msgstr "Play all"
-
-msgid "{%0%} tracks"
-msgstr "{%0%} tracks"
-
-msgid "Show {%0%} more albums"
-msgstr "Show {%0%} more albums"
-
-msgid "Title"
-msgstr "Title"
-
-msgid "Artist"
-msgstr "Artist"
-
-msgid "Album"
-msgstr "Album"
-
-msgid "Download..."
-msgstr "Download..."
-
-msgid "Download tracks"
-msgstr "Download tracks"
-
-msgid "There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as {%0%} to easily download a list of tracks."
-msgstr "There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as {%0%} to easily download a list of tracks."
-
-msgid "Simply copy paste the snippet below into a terminal to launch the download."
-msgstr "Simply copy paste the snippet below into a terminal to launch the download."
-
-msgid "Keep your PRIVATE_TOKEN secret as it gives access to your account."
-msgstr "Keep your PRIVATE_TOKEN secret as it gives access to your account."
-
-msgid "Cancel"
-msgstr "Cancel"
-
-msgid "Add to current queue"
-msgstr "Add to current queue"
-
-msgid "Play"
-msgstr "Play"
-
-msgid "Add to queue"
-msgstr "Add to queue"
-
-msgid "Play next"
-msgstr "Play next"
-
-msgid "Play now"
-msgstr "Play now"
-
-msgid "Previous track"
-msgstr "Previous track"
-
-msgid "Play track"
-msgstr "Play track"
-
-msgid "Pause track"
-msgstr "Pause track"
-
-msgid "Next track"
-msgstr "Next track"
-
-msgid "Unmute"
-msgstr "Unmute"
-
-msgid "Mute"
-msgstr "Mute"
-
-msgid "Looping disabled. Click to switch to single-track looping."
-msgstr "Looping disabled. Click to switch to single-track looping."
-
-msgid "Looping on a single track. Click to switch to whole queue looping."
-msgstr "Looping on a single track. Click to switch to whole queue looping."
-
-msgid "Looping on whole queue. Click to disable looping."
-msgstr "Looping on whole queue. Click to disable looping."
-
-msgid "Shuffle your queue"
-msgstr "Shuffle your queue"
-
-msgid "Clear your queue"
-msgstr "Clear your queue"
-
-msgid "Search for some music"
-msgstr "Search for some music"
-
-msgid "Artists"
-msgstr "Artists"
-
-msgid "Sorry, we did not found any artist matching your query"
-msgstr "Sorry, we did not found any artist matching your query"
-
-msgid "Albums"
-msgstr "Albums"
-
-msgid "Sorry, we did not found any album matching your query"
-msgstr "Sorry, we did not found any album matching your query"
-
-msgid "Log in to your Funkwhale account"
-msgstr "Log in to your Funkwhale account"
-
-msgid "We cannot log you in"
-msgstr "We cannot log you in"
-
-msgid "Please double-check your username/password couple is correct"
-msgstr "Please double-check your username/password couple is correct"
-
-msgid "An unknown error happend, this can mean the server is down or cannot be reached"
-msgstr "An unknown error happend, this can mean the server is down or cannot be reached"
-
-msgid "Username or email"
-msgstr "Username or email"
-
-msgid "Password"
-msgstr "Password"
-
-msgid "Login"
-msgstr "Login"
-
-msgid "Create an account"
-msgstr "Create an account"
-
-msgid "Are you sure you want to log out?"
-msgstr "Are you sure you want to log out?"
-
-msgid "You are currently logged in as {%0%}"
-msgstr "You are currently logged in as {%0%}"
-
-msgid "Yes, log me out!"
-msgstr "Yes, log me out!"
-
-msgid "Registered since {%0%}"
-msgstr "Registered since {%0%}"
-
-msgid "This is you!"
-msgstr "This is you!"
-
-msgid "Staff member"
-msgstr "Staff member"
-
-msgid "Settings..."
-msgstr "Settings..."
-
-msgid "Account settings"
-msgstr "Account settings"
-
-msgid "Settings updated"
-msgstr "Settings updated"
-
-msgid "We cannot save your settings"
-msgstr "We cannot save your settings"
-
-msgid "Update settings"
-msgstr "Update settings"
-
-msgid "Change my password"
-msgstr "Change my password"
-
-msgid "Cannot change your password"
-msgstr "Cannot change your password"
-
-msgid "Please double-check your password is correct"
-msgstr "Please double-check your password is correct"
-
-msgid "Old password"
-msgstr "Old password"
-
-msgid "New password"
-msgid "New password"
-
-msgid "Change password"
-msgstr "Change password"
-
-msgid "Activity visibility"
-msgstr "Activity visibility"
-
-msgid "Determine the visibility level of your activity"
-msgstr "Determine the visibility level of your activity"
-
-msgid "Nobody except me"
-msgstr "Nobody except me"
-
-msgid "Everyone on this instance"
-msgstr "Everyone on this instance"
-
-msgid "Create a funkwhale account"
-msgstr "Create a funkwhale account"
-
-msgid "We cannot create your account"
-msgstr "We cannot create your account"
-
-msgid "Username"
-msgstr "Username"
-
-msgid "Email"
-msgstr "Email"
-
-msgid "Create my account"
-msgstr "Create my account"
-
-msgid "Registration is currently disabled on this instance, please try again later."
-msgstr "Registration is currently disabled on this instance, please try again later."
-
-msgid "Do you want to confirm this action?"
-msgstr "Do you want to confirm this action?"
-
-msgid "Confirm"
-msgstr "Confirm"
-
-msgid "Expand"
-msgstr "Expand"
-
-msgid "Loading your favorites..."
-msgstr "Loading your favorites..."
-
-msgid "{%0%} favorites"
-msgstr "{%0%} favorites"
-
-msgid "Ordering"
-msgstr "Ordering"
-
-msgid "Ordering direction"
-msgstr "Ordering direction"
-
-msgid "Ascending"
-msgstr "Ascending"
-
-msgid "Descending"
-msgstr "Descending"
-
-msgid "Results per page"
-msgstr "Results per page"
-
-msgid "Track name"
-msgstr "Track name"
-
-msgid "Album name"
-msgstr "Album name"
-
-msgid "Artist name"
-msgstr "Artist name"
-
-msgid "In favorites"
-msgstr "In favorites"
-
-msgid "Add to favorites"
-msgstr "Add to favorites"
-
-msgid "Remove from favorites"
-msgstr "Remove from favorites"
-
-msgid "User activity"
-msgstr "User activity"
-
-msgid "users"
-msgstr "users"
-
-msgid "tracks listened"
-msgstr "tracks listened"
-
-msgid "Tracks favorited"
-msgstr "Tracks favorited"
-
-msgid "hours of music"
-msgstr "hours of music"
-
-msgid "Artists"
-msgstr "Artists"
-
-msgid "Albums"
-msgstr "Albums"
-
-msgid "tracks"
-msgstr "tracks"
-
-msgid "Filter album types"
-msgstr "Filter album types"
-
-msgid "Query template"
-msgstr "Query template"
-
-msgid "Live"
-msgstr "Live"
-
-msgid "Compilation"
-msgstr "Compilation"
-
-msgid "EP"
-msgstr "EP"
-
-msgid "Single"
-msgstr "Single"
-
-msgid "Other"
-msgstr "Other"
-
-msgid "Job ID"
-msgstr "Job ID"
-
-msgid "Recording MusicBrainz ID"
-msgstr "Recording MusicBrainz ID"
-
-msgid "Source"
-msgstr "Source"
-
-msgid "Track"
-msgstr "Track"
-
-msgid "Previous"
-msgstr "Previous"
-
-msgid "Next"
-msgstr "Next"
-
-msgid "ID"
-msgstr "ID"
-
-msgid "Launch date"
-msgstr "Launch date"
-
-msgid "Jobs"
-msgstr "Jobs"
-
-msgid "Status"
-msgstr "Status"
-
-msgid "Select files to upload..."
-msgstr "Select files to upload..."
-
-msgid "Start Upload"
-msgstr "Start Upload"
-
-msgid "Stop Upload"
-msgstr "Stop Upload"
-
-msgid "Once all your files are uploaded, simply head over {%0%} to check the import status."
-msgstr "Once all your files are uploaded, simply head over {%0%} to check the import status."
-
-msgid "import detail page"
-msgstr "import detail page"
-
-msgid "File name"
-msgstr "File name"
-
-msgid "Size"
-msgstr "Size"
-
-msgid "Success"
-msgstr "Success"
-
-msgid "Uploading..."
-msgstr "Uploading..."
-
-msgid "Pending"
-msgstr "Pending"
-
-msgid "Import source"
-msgstr "Import source"
-
-msgid "Uploaded files or external source"
-msgstr "Uploaded files or external source"
-
-msgid "Metadata"
-msgstr "Metadata"
-
-msgid "Grab corresponding metadata"
-msgstr "Grab corresponding metadata"
-
-msgid "Music"
-msgstr "Music"
-
-msgid "Select relevant sources or files for import"
-msgstr "Select relevant sources or files for import"
-
-msgid "Previous step"
-msgstr "Previous step"
-
-msgid "Next step"
-msgstr "Next step"
-
-msgid "Import {%0%} tracks"
-msgstr "Import {%0%} tracks"
-
-msgid "First, choose where you want to import the music from:"
-msgstr "First, choose where you want to import the music from:"
-
-msgid "External source. Supported backends:"
-msgstr "External source. Supported backends:"
-
-msgid "File upload"
-msgstr "File upload"
-
-msgid "Search an entity you want to import:"
-msgstr "Search an entity you want to import:"
-
-msgid "Input a MusicBrainz ID manually:"
-msgstr "Input a MusicBrainz ID manually:"
-
-msgid "Or"
-msgstr "Or"
-
-msgid "You will import:"
-msgstr "You will import:"
-
-msgid "You can also skip this step and enter metadata manually."
-msgstr "You can also skip this step and enter metadata manually."
-
-msgid "Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the {%0%} project, which you can think about as the Wikipedia of music."
-msgstr "Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the {%0%} project, which you can think about as the Wikipedia of music."
-
-msgid "Music request"
-msgstr "Music request"
-
-msgid "This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."
-msgstr "This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."
-
-msgid "Album {%0%} ({%1%} tracks) by {%2%}"
-msgstr "Album {%0%} ({%1%} tracks) by {%2%}"
-
-msgid "Import this release"
-msgstr "Import this release"
-
-msgid "Import this track"
-msgstr "Import this track"
-
-msgid "Source"
-msgstr "Source"
-
-msgid "Result {%0%}/{%1%}"
-msgstr "Result {%0%}/{%1%}"
-
-msgid "Search query"
-msgstr "Search query"
-
-msgid "Imported URL"
-msgstr "Imported URL"
-
-msgid "Album containing {%0%} tracks, by {%1%}"
-msgstr "Album containing {%0%} tracks, by {%1%}"
-
-msgid "Search on Wikipedia"
-msgstr "Search on Wikipedia"
-
-msgid "View on MusicBrainz"
-msgstr "View on MusicBrainz"
-
-msgid "{%0%} tracks in {%1%} albums"
-msgstr "{%0%} tracks in {%1%} albums"
-
-msgid "Play all albums"
-msgstr "Play all albums"
-
-msgid "Albums by this artist"
-msgstr "Albums by this artist"
-
-msgid "Browsing artists"
-msgstr "Browsing artists"
-
-msgid "Search"
-msgstr "Search"
-
-msgid "Latest artists"
-msgstr "Latest artists"
-
-msgid "Radios"
-msgstr "Radios"
-
-msgid "Music requests"
-msgstr "Music requests"
-
-msgid "Browse"
-msgstr "Browse"
-
-msgid "Playlists"
-msgstr "Playlists"
-
-msgid "Requests"
-msgstr "Requests"
-
-msgid "Import"
-msgstr "Import"
-
-msgid "Import batches"
-msgstr "Import batches"
-
-msgid "Browsing radios"
-msgstr "Browsing radios"
-
-msgid "Create your own radio"
-msgstr "Create your own radio"
-
-msgid "From album {%0%} by {%1%}"
-msgstr "From album {%0%} by {%1%}"
-
-msgid "Download"
-msgstr "Download"
-
-msgid "Lyrics"
-msgstr "Lyrics"
-
-msgid "No lyrics available for this track."
-msgstr "No lyrics available for this track."
-
-msgid "Search on lyrics.wikia.com"
-msgstr "Search on lyrics.wikia.com"
-
-msgid "Builder"
-msgstr "Builder"
-
-msgid "You can use this interface to build your own custom radio, which will play tracks according to your criteria"
-msgstr "You can use this interface to build your own custom radio, which will play tracks according to your criteria"
-
-msgid "Radio name"
-msgstr "Radio name"
-
-msgid "Display publicly"
-msgstr "Display publicly"
-
-msgid "Save"
-msgstr "Save"
-
-msgid "Add filters to customize your radio"
-msgstr "Add filters to customize your radio"
-
-msgid "Select a filter"
-msgstr "Select a filter"
-
-msgid "Add filter"
-msgstr "Add filter"
-
-msgid "Filter name"
-msgstr "Filter name"
-
-msgid "Exclude"
-msgstr "Exclude"
-
-msgid "Config"
-msgstr "Config"
-
-msgid "Candidates"
-msgstr "Candidates"
-
-msgid "Actions"
-msgstr "Actions"
-
-msgid "{%0%} tracks matching combined filters"
-msgstr "{%0%} tracks matching combined filters"
-
-msgid "Track matching filter"
-msgstr "Track matching filter"
-
-msgid "Remove"
-msgstr "Remove"