From 0cfefe7a19da1c60b02eb73a55aac5ed1767a548 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Sun, 1 Jul 2018 15:31:34 +0200
Subject: [PATCH] Use <translate> instead of  to avoid extraction issues

---
 front/src/App.vue                             | 16 +++----
 front/src/components/About.vue                |  6 +--
 front/src/components/Home.vue                 | 48 +++++++++----------
 front/src/components/PageNotFound.vue         |  6 +--
 front/src/components/Sidebar.vue              | 46 +++++++++---------
 front/src/components/admin/SettingsGroup.vue  |  6 +--
 front/src/components/audio/PlayButton.vue     |  8 ++--
 front/src/components/audio/Search.vue         | 10 ++--
 front/src/components/audio/album/Card.vue     |  4 +-
 front/src/components/audio/artist/Card.vue    |  4 +-
 front/src/components/audio/track/Table.vue    |  2 +-
 front/src/components/auth/Login.vue           | 12 ++---
 front/src/components/auth/Logout.vue          |  4 +-
 front/src/components/auth/Profile.vue         |  8 ++--
 front/src/components/auth/Settings.vue        | 28 +++++------
 front/src/components/auth/Signup.vue          |  2 +-
 .../src/components/auth/SubsonicTokenForm.vue | 32 ++++++-------
 front/src/components/common/ActionTable.vue   | 12 ++---
 .../src/components/common/DangerousButton.vue |  6 +--
 front/src/components/discussion/Comment.vue   |  4 +-
 front/src/components/favorites/List.vue       | 12 ++---
 .../src/components/federation/LibraryCard.vue | 10 ++--
 .../federation/LibraryFollowTable.vue         | 20 ++++----
 .../src/components/federation/LibraryForm.vue | 10 ++--
 .../federation/LibraryTrackTable.vue          | 30 ++++++------
 front/src/components/instance/Stats.vue       | 16 +++----
 front/src/components/library/Album.vue        | 10 ++--
 front/src/components/library/Artist.vue       |  8 ++--
 front/src/components/library/Artists.vue      | 10 ++--
 front/src/components/library/Home.vue         |  6 +--
 front/src/components/library/Library.vue      | 12 ++---
 front/src/components/library/Radios.vue       | 16 +++----
 front/src/components/library/Track.vue        | 32 ++++++-------
 .../library/import/ArtistImport.vue           |  4 +-
 .../components/library/import/BatchDetail.vue | 38 +++++++--------
 .../components/library/import/BatchList.vue   | 34 ++++++-------
 .../components/library/import/FileUpload.vue  | 26 +++++-----
 front/src/components/library/import/Main.vue  | 36 +++++++-------
 .../library/import/ReleaseImport.vue          |  2 +-
 .../components/library/import/TrackImport.vue |  8 ++--
 .../src/components/library/radios/Builder.vue | 26 +++++-----
 .../src/components/library/radios/Filter.vue  |  6 +--
 .../components/manage/library/FilesTable.vue  | 30 ++++++------
 .../manage/library/RequestsTable.vue          | 40 ++++++++--------
 .../manage/users/InvitationForm.vue           | 10 ++--
 .../manage/users/InvitationsTable.vue         | 24 +++++-----
 .../components/manage/users/UsersTable.vue    | 32 ++++++-------
 front/src/components/playlists/Editor.vue     | 14 +++---
 front/src/components/playlists/Form.vue       | 16 +++----
 .../components/playlists/PlaylistModal.vue    | 18 +++----
 .../playlists/TrackPlaylistIcon.vue           |  2 +-
 front/src/components/radios/Button.vue        |  4 +-
 front/src/components/radios/Card.vue          |  2 +-
 front/src/components/requests/Card.vue        |  2 +-
 front/src/components/requests/Form.vue        | 18 +++----
 front/src/views/admin/Settings.vue            |  2 +-
 front/src/views/admin/library/Base.vue        |  4 +-
 front/src/views/admin/library/FilesList.vue   |  2 +-
 .../src/views/admin/library/RequestsList.vue  |  2 +-
 front/src/views/admin/users/Base.vue          |  4 +-
 .../src/views/admin/users/InvitationsList.vue |  2 +-
 front/src/views/admin/users/UsersDetail.vue   | 16 +++----
 front/src/views/admin/users/UsersList.vue     |  2 +-
 front/src/views/auth/EmailConfirm.vue         | 16 +++----
 front/src/views/auth/PasswordReset.vue        | 12 ++---
 front/src/views/auth/PasswordResetConfirm.vue | 18 +++----
 front/src/views/federation/Base.vue           |  6 +--
 front/src/views/federation/LibraryDetail.vue  | 26 +++++-----
 .../views/federation/LibraryFollowersList.vue |  4 +-
 front/src/views/federation/LibraryList.vue    | 16 +++----
 .../src/views/federation/LibraryTrackList.vue |  2 +-
 front/src/views/instance/Timeline.vue         |  4 +-
 front/src/views/playlists/Detail.vue          | 12 ++---
 front/src/views/playlists/List.vue            | 16 +++----
 74 files changed, 507 insertions(+), 507 deletions(-)

diff --git a/front/src/App.vue b/front/src/App.vue
index 6efd3e58..a6da038b 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -2,12 +2,12 @@
   <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">{{ $gettext('Choose your instance') }}</h1>
+        <h1 class="ui header"><translate>Choose your instance</translate></h1>
         <form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)">
-          <p>{{ $gettext('You need to select an instance in order to continue') }}</p>
+          <p><translate>You need to select an instance in order to continue</translate></p>
           <div class="ui action input">
             <input type="text" v-model="instanceUrl">
-            <button type="submit" class="ui button">{{ $gettext('Submit') }}</button>
+            <button type="submit" class="ui button"><translate>Submit</translate></button>
           </div>
           <p>{{ $gettext('Suggested choices') }}</p>
           <div class="ui bulleted list">
@@ -30,17 +30,17 @@
               <h4 v-translate class="ui header">Links</h4>
               <div class="ui link list">
                 <router-link class="item" to="/about">
-                  {{ $gettext('About this instance') }}
+                  <translate>About this instance</translate>
                 </router-link>
-                <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://funkwhale.audio" class="item" target="_blank"><translate>Official website</translate></a>
+                <a href="https://docs.funkwhale.audio" class="item" target="_blank"><translate>Documentation</translate></a>
                 <a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank">
                   <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">{{ $gettext('Issue tracker') }}</a>
+                <a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank"><translate>Issue tracker</translate></a>
                 <a @click="switchInstance" class="item" >
-                  {{ $gettext('Use another instance') }}
+                  <translate>Use another instance</translate>
                   <template v-if="$store.state.instance.instanceUrl !== '/'">
                     <br>
                     ({{ $store.state.instance.instanceUrl }})
diff --git a/front/src/components/About.vue b/front/src/components/About.vue
index 5ebed810..a4597b29 100644
--- a/front/src/components/About.vue
+++ b/front/src/components/About.vue
@@ -6,20 +6,20 @@
             <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>
+            <template v-else="instance.name.value"><translate>About this instance</translate></template>
         </h1>
         <stats></stats>
       </div>
     </div>
     <div class="ui vertical stripe segment">
       <p v-if="!instance.short_description.value && !instance.long_description.value">
-        {{ $gettext('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
+        <translate>Unfortunately, owners of this instance did not yet take the time to complete this page.</translate>
       </p>
       <router-link
         class="ui button"
         v-if="$store.state.auth.availablePermissions['settings']"
         :to="{path: '/manage/settings', hash: 'instance'}">
-        <i class="pencil icon"></i>{{ $gettext('Edit instance info') }}
+        <i class="pencil icon"></i><translate>Edit instance info</translate>
       </router-link>
       <div
         v-if="instance.short_description.value"
diff --git a/front/src/components/Home.vue b/front/src/components/Home.vue
index dcea5c72..2f6196ea 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">
-          {{ $gettext('Welcome on Funkwhale') }}
+          <translate>Welcome on Funkwhale</translate>
         </h1>
-        <p>{{ $gettext('We think listening to music should be simple.') }}</p>
+        <p><translate>We think listening to music should be simple.</translate></p>
         <router-link class="ui icon button" to="/about">
           <i class="info icon"></i>
-          {{ $gettext('Learn more about this instance') }}
+          <translate>Learn more about this instance</translate>
         </router-link>
         <router-link class="ui icon teal button" to="/library">
-          {{ $gettext('Get me to the library') }}
+          <translate>Get me to the library</translate>
           <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">
-                {{ $gettext('Why funkwhale?') }}
+                <translate>Why funkwhale?</translate>
               </h2>
-              <p>{{ $gettext('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
+              <p><translate>That's simple: we loved Grooveshark and we want to build something even better.</translate></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">
-          {{ $gettext('Unlimited music') }}
+          <translate>Unlimited music</translate>
         </h2>
-        <p>{{ $gettext('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
+        <p><translate>Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.</translate></p>
         <div class="ui list">
           <div class="item">
             <i class="sound icon"></i>
             <div class="content">
-              {{ $gettext('Click once, listen for hours using built-in radios') }}
+              <translate>Click once, listen for hours using built-in radios</translate>
             </div>
           </div>
           <div class="item">
             <i class="heart icon"></i>
             <div class="content">
-              {{ $gettext('Keep a track of your favorite songs') }}
+              <translate>Keep a track of your favorite songs</translate>
             </div>
           </div>
           <div class="item">
             <i class="list icon"></i>
             <div class="content">
-              {{ $gettext('Playlists? We got them') }}
+              <translate>Playlists? We got them</translate>
             </div>
           </div>
         </div>
@@ -62,14 +62,14 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $gettext('Clean library') }}
+          <translate>Clean library</translate>
         </h2>
-        <p>{{ $gettext('Funkwhale takes care of handling your music') }}.</p>
+        <p><translate>Funkwhale takes care of handling your music</translate>.</p>
         <div class="ui list">
           <div class="item">
             <i class="download icon"></i>
             <div class="content">
-              {{ $gettext('Import music from various platforms, such as YouTube or SoundCloud') }}
+              <translate>Import music from various platforms, such as YouTube or SoundCloud</translate>
             </div>
           </div>
           <div class="item">
@@ -86,7 +86,7 @@
           <div class="item">
             <i class="plus icon"></i>
             <div class="content">
-              {{ $gettext('Covers, lyrics, our goal is to have them all ;)') }}
+              <translate>Covers, lyrics, our goal is to have them all ;)</translate>
             </div>
           </div>
         </div>
@@ -94,20 +94,20 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $gettext('Easy to use') }}
+          <translate>Easy to use</translate>
         </h2>
-        <p>{{ $gettext('Funkwhale is dead simple to use.') }}</p>
+        <p><translate>Funkwhale is dead simple to use.</translate></p>
         <div class="ui list">
           <div class="item">
             <i class="book icon"></i>
             <div class="content">
-              {{ $gettext('No add-ons, no plugins : you only need a web library') }}
+              <translate>No add-ons, no plugins : you only need a web library</translate>
             </div>
           </div>
           <div class="item">
             <i class="wizard icon"></i>
             <div class="content">
-              {{ $gettext('Access your music from a clean interface that focus on what really matters') }}
+              <translate>Access your music from a clean interface that focus on what really matters</translate>
             </div>
           </div>
         </div>
@@ -115,26 +115,26 @@
       <div class="ui middle aligned stackable text container">
         <div class="ui hidden divider"></div>
         <h2 class="ui header">
-          {{ $gettext('Your music, your way') }}
+          <translate>Your music, your way</translate>
         </h2>
-        <p>{{ $gettext('Funkwhale is free and gives you control on your music.') }}</p>
+        <p><translate>Funkwhale is free and gives you control on your music.</translate></p>
         <div class="ui list">
           <div class="item">
             <i class="smile icon"></i>
             <div class="content">
-              {{ $gettext('The plaform is free and open-source, you can install it and modify it without worries') }}
+              <translate>The plaform is free and open-source, you can install it and modify it without worries</translate>
             </div>
           </div>
           <div class="item">
             <i class="protect icon"></i>
             <div class="content">
-              {{ $gettext('We do not track you or bother you with ads') }}
+              <translate>We do not track you or bother you with ads</translate>
             </div>
           </div>
           <div class="item">
             <i class="users icon"></i>
             <div class="content">
-              {{ $gettext('You can invite friends and family to your instance so they can enjoy your music') }}
+              <translate>You can invite friends and family to your instance so they can enjoy your music</translate>
             </div>
           </div>
         </div>
diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue
index 150abd8c..5e6aaf92 100644
--- a/front/src/components/PageNotFound.vue
+++ b/front/src/components/PageNotFound.vue
@@ -5,14 +5,14 @@
         <h1 class="ui huge header">
           <i class="warning icon"></i>
           <div class="content">
-            <strike>{{ $gettext('Whale') }}</strike> {{ $gettext('Page not found!') }}
+            <strike><translate>Whale') }}</strike> {{ $gettext('Page not found!</translate>
           </div>
         </h1>
-        <p>{{ $gettext('We\'re sorry, the page you asked for does not exist:') }}</p>
+        <p><translate>We're sorry, the page you asked for does not exist:</translate></p>
         <a :href="path">{{ path }}</a>
         <div class="ui hidden divider"></div>
         <router-link class="ui icon button" to="/">
-          {{ $gettext('Go to home page') }}
+          <translate>Go to home page</translate>
           <i class="right arrow icon"></i>
         </router-link>
       </div>
diff --git a/front/src/components/Sidebar.vue b/front/src/components/Sidebar.vue
index 439a1e63..8cdf4254 100644
--- a/front/src/components/Sidebar.vue
+++ b/front/src/components/Sidebar.vue
@@ -16,11 +16,11 @@
 
   <div class="menu-area">
     <div class="ui compact fluid two item inverted menu">
-      <a class="active item" @click="selectedTab = 'library'" data-tab="library">Browse</a>
+      <a class="active item" @click="selectedTab = 'library'" data-tab="library"><translate>Browse</translate></a>
       <a class="item" @click="selectedTab = 'queue'" data-tab="queue">
-        {{ $gettext('Queue') }}&nbsp;
+        <translate>Queue</translate>&nbsp;
          <template v-if="queue.tracks.length === 0">
-           {{ $gettext('(empty)') }}
+           <translate>(empty)</translate>
          </template>
          <translate v-else :translate-params="{index: queue.currentIndex + 1, length: queue.tracks.length}">
           (%{ index } of %{ length })
@@ -32,7 +32,7 @@
     <div class="ui bottom attached active tab" data-tab="library">
       <div class="ui inverted vertical large fluid menu">
         <div class="item">
-          <div class="header">{{ $gettext('My account') }}</div>
+          <div class="header"><translate>My account</translate></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>
@@ -40,34 +40,34 @@
                 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>
+            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i><translate>Logout</translate></router-link>
+            <router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i><translate>Login</translate></router-link>
           </div>
         </div>
         <div class="item">
-          <div class="header">{{ $gettext('Music') }}</div>
+          <div class="header"><translate>Music</translate></div>
           <div class="menu">
-            <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>
+            <router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i><translate>Browse library</translate></router-link>
+            <router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i><translate>Favorites</translate></router-link>
             <a
               @click="$store.commit('playlists/chooseTrack', null)"
               v-if="$store.state.auth.authenticated"
               class="item">
-              <i class="list icon"></i>{{ $gettext('Playlists') }}
+              <i class="list icon"></i><translate>Playlists</translate>
             </a>
             <router-link
               v-if="$store.state.auth.authenticated"
-              class="item" :to="{path: '/activity'}"><i class="bell icon"></i>{{ $gettext('Activity') }}</router-link>
+              class="item" :to="{path: '/activity'}"><i class="bell icon"></i><translate>Activity</translate></router-link>
           </div>
         </div>
         <div class="item" v-if="showAdmin">
-          <div class="header">{{ $gettext('Administration') }}</div>
+          <div class="header"><translate>Administration</translate></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>{{ $gettext('Library') }}
+              <i class="book icon"></i><translate>Library</translate>
               <div
                 :class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
                 :title="$gettext('Pending import requests')">
@@ -78,13 +78,13 @@
               class="item"
               v-else-if="$store.state.auth.availablePermissions['upload']"
               to="/library/import/launch">
-              <i class="download icon"></i>{{ $gettext('Import music') }}
+              <i class="download icon"></i><translate>Import music</translate>
             </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['federation']"
               :to="{path: '/manage/federation/libraries'}">
-              <i class="sitemap icon"></i>{{ $gettext('Federation') }}
+              <i class="sitemap icon"></i><translate>Federation</translate>
               <div
                 :class="['ui', {'teal': $store.state.ui.notifications.federation > 0}, 'label']"
                 :title="$gettext('Pending follow requests')">
@@ -94,13 +94,13 @@
               class="item"
               v-if="$store.state.auth.availablePermissions['settings']"
               :to="{path: '/manage/settings'}">
-              <i class="settings icon"></i>{{ $gettext('Settings') }}
+              <i class="settings icon"></i><translate>Settings</translate>
             </router-link>
             <router-link
               class="item"
               v-if="$store.state.auth.availablePermissions['settings']"
               :to="{name: 'manage.users.users.list'}">
-              <i class="users icon"></i>{{ $gettext('Users') }}
+              <i class="users icon"></i><translate>Users</translate>
             </router-link>
           </div>
         </div>
@@ -110,7 +110,7 @@
       <i class="history icon"></i>
       <div class="content">
         <div class="header">
-          {{ $gettext('Do you want to restore your previous queue?') }}
+          <translate>Do you want to restore your previous queue?</translate>
         </div>
         <p>
           <translate
@@ -121,8 +121,8 @@
           </translate>
         </p>
         <div class="ui two buttons">
-          <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 @click="queue.restore()" class="ui basic inverted green button"><translate>Yes</translate></div>
+          <div @click="queue.removePrevious()" class="ui basic inverted red button"><translate>No</translate></div>
         </div>
       </div>
     </div>
@@ -153,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> {{ $gettext('You have a radio playing') }}
+            <i class="feed icon"></i> <translate>You have a radio playing</translate>
           </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>
+          <p><translate>New tracks will be appended here automatically.</translate></p>
+          <div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button"><translate>Stop radio</translate></div>
         </div>
       </div>
     </div>
diff --git a/front/src/components/admin/SettingsGroup.vue b/front/src/components/admin/SettingsGroup.vue
index 8ba0fb6f..c732213e 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">{{ $gettext('Error while saving settings') }}</div>
+      <div class="header"><translate>Error while saving settings</translate></div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
       </ul>
     </div>
     <div v-if="result" class="ui positive message">
-      {{ $gettext('Settings updated successfully.') }}
+      <translate>Settings updated successfully.</translate>
     </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']">
-        {{ $gettext('Save') }}
+        <translate>Save</translate>
     </button>
   </form>
 </template>
diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue
index 5c057245..497229d4 100644
--- a/front/src/components/audio/PlayButton.vue
+++ b/front/src/components/audio/PlayButton.vue
@@ -6,14 +6,14 @@
       :disabled="!playable"
       :class="['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}, 'button']">
       <i class="ui play icon"></i>
-      <template v-if="!discrete"><slot>{{ $gettext('Play') }}</slot></template>
+      <template v-if="!discrete"><slot><translate>Play</translate></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>{{ $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 class="item" :disabled="!playable" @click="add"><i class="plus icon"></i><translate>Add to queue</translate></div>
+        <div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i><translate>Play next</translate></div>
+        <div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i><translate>Play now</translate></div>
       </div>
     </div>
   </div>
diff --git a/front/src/components/audio/Search.vue b/front/src/components/audio/Search.vue
index ad5bdbeb..d04777ec 100644
--- a/front/src/components/audio/Search.vue
+++ b/front/src/components/audio/Search.vue
@@ -1,6 +1,6 @@
 <template>
   <div>
-    <h2>{{ $gettext('Search for some music') }}</h2>
+    <h2><translate>Search for some music</translate></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">{{ $gettext('Artists') }}</h3>
+      <h3 class="ui title"><translate>Artists</translate></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>{{ $gettext('Sorry, we did not found any artist matching your query') }}</p>
+      <p v-else><translate>Sorry, we did not found any artist matching your query</translate></p>
     </template>
     <template v-if="query.length > 0">
-      <h3 class="ui title">{{ $gettext('Albums') }}</h3>
+      <h3 class="ui title"><translate>Albums</translate></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>{{ $gettext('Sorry, we did not found any album matching your query') }}</p>
+      <p v-else><translate>Sorry, we did not found any album matching your query</translate></p>
     </template>
   </div>
 </template>
diff --git a/front/src/components/audio/album/Card.vue b/front/src/components/audio/album/Card.vue
index a12acdcc..71f2ec80 100644
--- a/front/src/components/audio/album/Card.vue
+++ b/front/src/components/audio/album/Card.vue
@@ -41,14 +41,14 @@
               <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">
-              {{ $gettext('Collapse') }}
+              <translate>Collapse</translate>
             </em>
           </div>
         </div>
       </div>
       <div class="extra content">
         <play-button class="mini basic orange right floated" :tracks="album.tracks">
-          {{ $gettext('Play all') }}
+          <translate>Play all</translate>
         </play-button>
         <span>
           <i class="music icon"></i>
diff --git a/front/src/components/audio/artist/Card.vue b/front/src/components/audio/artist/Card.vue
index 10745f14..bd7cb68c 100644
--- a/front/src/components/audio/artist/Card.vue
+++ b/front/src/components/audio/artist/Card.vue
@@ -31,7 +31,7 @@
               <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">
-              {{ $gettext('Collapse') }}
+              <translate>Collapse</translate>
             </em>
           </div>
         </div>
@@ -42,7 +42,7 @@
             <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">
-          {{ $gettext('Play all') }}
+          <translate>Play all</translate>
         </play-button>
       </div>
     </div>
diff --git a/front/src/components/audio/track/Table.vue b/front/src/components/audio/track/Table.vue
index 5ef9eb49..20322567 100644
--- a/front/src/components/audio/track/Table.vue
+++ b/front/src/components/audio/track/Table.vue
@@ -40,7 +40,7 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="$store.state.auth.aut
               </div>
             </div>
             <div class="actions">
-              <div class="ui black deny button">{{ $gettext('Cancel') }}</div>
+              <div class="ui black deny button"><translate>Cancel</translate></div>
             </div>
           </modal>
         </th>
diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue
index df5a0763..b89734b0 100644
--- a/front/src/components/auth/Login.vue
+++ b/front/src/components/auth/Login.vue
@@ -2,10 +2,10 @@
   <div class="main pusher" v-title="'Log In'">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $gettext('Log in to your Funkwhale account') }}</h2>
+        <h2><translate>Log in to your Funkwhale account</translate></h2>
         <form class="ui form" @submit.prevent="submit()">
           <div v-if="error" class="ui negative message">
-            <div class="header">{{ $gettext('We cannot log you in') }}</div>
+            <div class="header"><translate>We cannot log you in</translate></div>
             <ul class="list">
               <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>
@@ -13,9 +13,9 @@
           </div>
           <div class="field">
             <label>
-              {{ $gettext('Username or email') }} |
+              <translate>Username or email</translate> |
               <router-link :to="{path: '/signup'}">
-                {{ $gettext('Create an account') }}
+                <translate>Create an account</translate>
               </router-link>
             </label>
             <input
@@ -30,9 +30,9 @@
           </div>
           <div class="field">
             <label>
-              {{ $gettext('Password') }} |
+              <translate>Password</translate> |
               <router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}">
-                {{ $gettext('Reset your password') }}
+                <translate>Reset your password</translate>
               </router-link>
             </label>
             <password-input :index="2" required v-model="credentials.password" />
diff --git a/front/src/components/auth/Logout.vue b/front/src/components/auth/Logout.vue
index 2090a659..32e21373 100644
--- a/front/src/components/auth/Logout.vue
+++ b/front/src/components/auth/Logout.vue
@@ -3,10 +3,10 @@
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2>
-          {{ $gettext('Are you sure you want to log out?') }}
+          <translate>Are you sure you want to log out?</translate>
         </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>
+        <button class="ui button" @click="$store.dispatch('auth/logout')"><translate>Yes, log me out!</translate></button>
       </div>
     </div>
   </div>
diff --git a/front/src/components/auth/Profile.vue b/front/src/components/auth/Profile.vue
index cb6ed274..027f306e 100644
--- a/front/src/components/auth/Profile.vue
+++ b/front/src/components/auth/Profile.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="main pusher" v-title="username + '\'s Profile'">
+  <div class="main pusher" v-title="username + ''s Profile'">
     <div v-if="isLoading" class="ui vertical segment">
       <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
     </div>
@@ -13,15 +13,15 @@
           </div>
         </h2>
         <div class="ui basic green label">
-          {{ $gettext('This is you!') }}
+          <translate>This is you!</translate>
         </div>
         <div v-if="$store.state.auth.profile.is_staff" class="ui yellow label">
           <i class="star icon"></i>
-          {{ $gettext('Staff member') }}
+          <translate>Staff member</translate>
         </div>
         <router-link class="ui tiny basic button" :to="{path: '/settings'}">
           <i class="setting icon"> </i>
-          {{ $gettext('Settings...') }}
+          <translate>Settings...</translate>
         </router-link>
 
       </div>
diff --git a/front/src/components/auth/Settings.vue b/front/src/components/auth/Settings.vue
index 1dcc0544..7ff825ef 100644
--- a/front/src/components/auth/Settings.vue
+++ b/front/src/components/auth/Settings.vue
@@ -3,16 +3,16 @@
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
         <h2 class="ui header">
-          {{ $gettext('Account settings') }}
+          <translate>Account settings</translate>
         </h2>
         <form class="ui form" @submit.prevent="submitSettings()">
           <div v-if="settings.success" class="ui positive message">
             <div class="header">
-              {{ $gettext('Settings updated') }}
+              <translate>Settings updated</translate>
             </div>
           </div>
           <div v-if="settings.errors.length > 0" class="ui negative message">
-            <div class="header">{{ $gettext('We cannot save your settings') }}</div>
+            <div class="header"><translate>We cannot save your settings</translate></div>
             <ul class="list">
               <li v-for="error in settings.errors">{{ error }}</li>
             </ul>
@@ -25,26 +25,26 @@
             </select>
           </div>
           <button :class="['ui', {'loading': isLoading}, 'button']" type="submit">
-            {{ $gettext('Update settings') }}
+            <translate>Update settings</translate>
           </button>
         </form>
       </div>
       <div class="ui hidden divider"></div>
       <div class="ui small text container">
         <h2 class="ui header">
-          {{ $gettext('Change my password') }}
+          <translate>Change my password</translate>
         </h2>
         <div class="ui message">
-          {{ $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.') }}
+          <translate>Changing your password will also change your Subsonic API password if you have requested one.</translate>
+          <translate>You will have to update your password on your clients that use this password.</translate>
         </div>
         <form class="ui form" @submit.prevent="submitPassword()">
           <div v-if="passwordError" class="ui negative message">
             <div class="header">
-              {{ $gettext('Cannot change your password') }}
+              <translate>Cannot change your password</translate>
             </div>
             <ul class="list">
-              <li v-if="passwordError == 'invalid_credentials'">{{ $gettext('Please double-check your password is correct') }}</li>
+              <li v-if="passwordError == 'invalid_credentials'"><translate>Please double-check your password is correct</translate></li>
             </ul>
           </div>
           <div class="field">
@@ -60,16 +60,16 @@
             color="yellow"
             :class="['ui', {'loading': isLoading}, 'button']"
             :action="submitPassword">
-            {{ $gettext('Change password') }}
-            <p slot="modal-header">{{ $gettext('Change your password?') }}</p>
+            <translate>Change password</translate>
+            <p slot="modal-header"><translate>Change your password?</translate></p>
             <div slot="modal-content">
               <p>{{ $gettext("Changing your password will have the following consequences") }}</p>
               <ul>
-                <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>
+                <li><translate>You will be logged out from this session and have to log out with the new one</translate></li>
+                <li><translate>Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password</translate></li>
               </ul>
             </div>
-            <p slot="modal-confirm">{{ $gettext('Disable access') }}</p>
+            <p slot="modal-confirm"><translate>Disable access</translate></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 6bd32d75..2a5cb8fb 100644
--- a/front/src/components/auth/Signup.vue
+++ b/front/src/components/auth/Signup.vue
@@ -7,7 +7,7 @@
           :class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
           @submit.prevent="submit()">
           <p class="ui message" v-if="!$store.state.instance.settings.users.registration_enabled.value">
-            {{ $gettext('Registration are closed on this instance, you will need an invitation code to signup.') }}
+            <translate>Registration are closed on this instance, you will need an invitation code to signup.</translate>
           </p>
 
           <div v-if="errors.length > 0" class="ui negative message">
diff --git a/front/src/components/auth/SubsonicTokenForm.vue b/front/src/components/auth/SubsonicTokenForm.vue
index 3ec6e621..c5e6783d 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>{{ $gettext('Subsonic API password') }}</h2>
+    <h2><translate>Subsonic API password</translate></h2>
     <p class="ui message" v-if="!subsonicEnabled">
-      {{ $gettext('The Subsonic API is not available on this Funkwhale instance.') }}
+      <translate>The Subsonic API is not available on this Funkwhale instance.</translate>
     </p>
     <p>
-      {{ $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.') }}
+      <translate>Funkwhale is compatible with other music players that support the Subsonic API.</translate>
+      <translate>You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.</translate>
     </p>
     <p>
-      {{ $gettext('However, accessing Funkwhale from those clients require a separate password you can set below.') }}
+      <translate>However, accessing Funkwhale from those clients require a separate password you can set below.</translate>
     </p>
     <p><a href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients" target="_blank">
-      {{ $gettext('Discover how to use Funkwhale from other apps') }}
+      <translate>Discover how to use Funkwhale from other apps</translate>
     </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">{{ $gettext('Error') }}</div>
+      <div class="header"><translate>Error</translate></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">
-        {{ $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>
+        <translate>Request a new password</translate>
+        <p slot="modal-header"><translate>Request a new Subsonic API password?</translate></p>
+        <p slot="modal-content"><translate>This will log you out from existing devices that use the current password.</translate></p>
+        <p slot="modal-confirm"><translate>Request a new password</translate></p>
       </dangerous-button>
       <button
         v-else
         color="grey"
         :class="['ui', {'loading': isLoading}, 'button']"
-        @click="requestNewToken">{{ $gettext('Request a password') }}</button>
+        @click="requestNewToken"><translate>Request a password</translate></button>
         <dangerous-button
           v-if="token"
           color="yellow"
           :class="['ui', {'loading': isLoading}, 'button']"
           :action="disable">
-          {{ $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>
+          <translate>Disable Subsonic access</translate>
+          <p slot="modal-header"><translate>Disable Subsonic API access?</translate></p>
+          <p slot="modal-content"><translate>This will completely disable access to the Subsonic API using from account.</translate></p>
+          <p slot="modal-confirm"><translate>Disable access</translate></p>
         </dangerous-button>
     </template>
   </form>
diff --git a/front/src/components/common/ActionTable.vue b/front/src/components/common/ActionTable.vue
index b1f97101..5c06766a 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>{{ $gettext('Actions') }}</label>
+                <label><translate>Actions</translate></label>
                 <select class="ui dropdown" v-model="currentActionName">
                   <option v-for="action in actions" :value="action.name">
                     {{ action.label }}
@@ -19,13 +19,13 @@
                   @click="launchAction"
                   :disabled="checked.length === 0"
                   :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']">
-                  {{ $gettext('Go') }}</div>
+                  <translate>Go</translate></div>
                 <dangerous-button
                   v-else-if="!currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
                   confirm-color="green"
                   color=""
                   @confirm="launchAction">
-                  {{ $gettext('Go') }}
+                  <translate>Go</translate>
                   <p slot="modal-header">
                     <translate
                       :translate-n="objectsData.count"
@@ -37,7 +37,7 @@
                   <p slot="modal-content">
                     {{ $gettext('This may affect a lot of elements, please double check this is really what you want.')}}
                   </p>
-                  <p slot="modal-confirm">{{ $gettext('Launch') }}</p>
+                  <p slot="modal-confirm"><translate>Launch</translate></p>
                 </dangerous-button>
               </div>
               <div class="count field">
@@ -67,13 +67,13 @@
                     </translate>
                   </a>
                   <a @click="selectAll = false" v-else>
-                    {{ $gettext('Select only current page') }}
+                    <translate>Select only current page</translate>
                   </a>
                 </template>
               </div>
             </div>
             <div v-if="actionErrors.length > 0" class="ui negative message">
-              <div class="header">{{ $gettext('Error while applying action') }}</div>
+              <div class="header"><translate>Error while applying action</translate></div>
               <ul class="list">
                 <li v-for="error in actionErrors">{{ error }}</li>
               </ul>
diff --git a/front/src/components/common/DangerousButton.vue b/front/src/components/common/DangerousButton.vue
index a17aa857..e6139203 100644
--- a/front/src/components/common/DangerousButton.vue
+++ b/front/src/components/common/DangerousButton.vue
@@ -5,7 +5,7 @@
     <modal class="small" :show.sync="showModal">
       <div class="header">
         <slot name="modal-header">
-          {{ $gettext('Do you want to confirm this action?') }}
+          <translate>Do you want to confirm this action?</translate>
         </slot>
       </div>
       <div class="scrolling content">
@@ -15,11 +15,11 @@
       </div>
       <div class="actions">
         <div class="ui cancel button">
-          {{ $gettext('Cancel') }}
+          <translate>Cancel</translate>
         </div>
         <div :class="['ui', 'confirm', confirmButtonColor, 'button']" @click="confirm">
           <slot name="modal-confirm">
-            {{ $gettext('Confirm') }}
+            <translate>Confirm</translate>
           </slot>
         </div>
       </div>
diff --git a/front/src/components/discussion/Comment.vue b/front/src/components/discussion/Comment.vue
index f88b8423..ee9ce9b1 100644
--- a/front/src/components/discussion/Comment.vue
+++ b/front/src/components/discussion/Comment.vue
@@ -12,13 +12,13 @@
           @click="collapsed = false"
           v-if="truncated && collapsed"
           class="expand">
-          {{ $gettext('Expand') }}
+          <translate>Expand</translate>
         </span>
         <span
           @click="collapsed = true"
           v-if="truncated && !collapsed"
           class="collapse">
-          {{ $gettext('Collapse') }}
+          <translate>Collapse</translate>
         </span>
       </div>
     </div>
diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue
index aaa73dc7..a231453f 100644
--- a/front/src/components/favorites/List.vue
+++ b/front/src/components/favorites/List.vue
@@ -3,7 +3,7 @@
     <div class="ui vertical center aligned stripe segment">
       <div :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
         <div class="ui text loader">
-          {{ $gettext('Loading your favorites...') }}
+          <translate>Loading your favorites...</translate>
         </div>
       </div>
       <h2 v-if="results" class="ui center aligned icon header">
@@ -21,7 +21,7 @@
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <label>{{ $gettext('Ordering') }}</label>
+            <label><translate>Ordering</translate></label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -29,14 +29,14 @@
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering direction') }}</label>
+            <label><translate>Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+">{{ $gettext('Ascending') }}</option>
-              <option value="-">{{ $gettext('Descending') }}</option>
+              <option value="+"><translate>Ascending</translate></option>
+              <option value="-"><translate>Descending</translate></option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Results per page') }}</label>
+            <label><translate>Results per page</translate></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/federation/LibraryCard.vue b/front/src/components/federation/LibraryCard.vue
index 24b43221..27760687 100644
--- a/front/src/components/federation/LibraryCard.vue
+++ b/front/src/components/federation/LibraryCard.vue
@@ -13,13 +13,13 @@
     </div>
     <div class="content">
       <span class="right floated" v-if="following">
-        <i class="check icon"></i>{{ $gettext('Following') }}
+        <i class="check icon"></i><translate>Following</translate>
       </span>
       <span class="right floated" v-else-if="manuallyApprovesFollowers">
-        <i class="lock icon"></i>{{ $gettext('Followers only') }}
+        <i class="lock icon"></i><translate>Followers only</translate>
       </span>
       <span class="right floated" v-else>
-        <i class="open lock icon"></i>{{ $gettext('Open') }}
+        <i class="open lock icon"></i><translate>Open</translate>
       </span>
       <span v-if="totalItems">
         <i class="music icon"></i>
@@ -34,7 +34,7 @@
     <div class="extra content">
       <template v-if="awaitingApproval">
         <i class="clock icon"></i>
-        {{ $gettext('Follow request pending approval') }}
+        <translate>Follow request pending approval</translate>
       </template>
       <div
         v-if="!library"
@@ -48,7 +48,7 @@
         v-else
         class="ui basic button"
         :to="{name: 'federation.libraries.detail', params: {id: library.uuid }}">
-        {{ $gettext('Detail') }}
+        <translate>Detail</translate>
       </router-link>
     </div>
   </div>
diff --git a/front/src/components/federation/LibraryFollowTable.vue b/front/src/components/federation/LibraryFollowTable.vue
index 9f4a6baf..43085f61 100644
--- a/front/src/components/federation/LibraryFollowTable.vue
+++ b/front/src/components/federation/LibraryFollowTable.vue
@@ -9,7 +9,7 @@
           <div class="ui checkbox">
             <input v-model="pending" type="checkbox">
             <label>
-              {{ $gettext('Pending approval') }}
+              <translate>Pending approval</translate>
             </label>
           </div>
         </div>
@@ -36,23 +36,23 @@
           <td>
             <template v-if="follow.approved === true">
               <i class="check icon"></i>
-              {{ $gettext('Approved') }}
+              <translate>Approved</translate>
             </template>
             <template v-else-if="follow.approved === false">
               <i class="x icon"></i>
-              {{ $gettext('Refused') }}
+              <translate>Refused</translate>
             </template>
             <template v-else>
               <i class="clock icon"></i>
-              {{ $gettext('Pending') }}
+              <translate>Pending</translate>
             </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>
-              {{ $gettext('Deny') }}
+              <translate>Deny</translate>
               <p slot="modal-header">
-                {{ $gettext('Deny access?') }}
+                <translate>Deny access?</translate>
               </p>
               <p slot="modal-content">
                 <translate
@@ -61,14 +61,14 @@
                 </translate>
               </p>
               <p slot="modal-confirm">
-                {{ $gettext('Deny') }}
+                <translate>Deny</translate>
               </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>
-              {{ $gettext('Approve') }}
+              <translate>Approve</translate>
               <p slot="modal-header">
-                {{ $gettext('Approve access?') }}
+                <translate>Approve access?</translate>
               </p>
               <p slot="modal-content">
                 <translate
@@ -76,7 +76,7 @@
                   By confirming, %{ username } will be granted access to your library.
                 </translate>
               <p slot="modal-confirm">
-                {{ $gettext('Approve') }}
+                <translate>Approve</translate>
               </p>
             </dangerous-button>
           </td>
diff --git a/front/src/components/federation/LibraryForm.vue b/front/src/components/federation/LibraryForm.vue
index c790c0ef..37d19e2f 100644
--- a/front/src/components/federation/LibraryForm.vue
+++ b/front/src/components/federation/LibraryForm.vue
@@ -1,14 +1,14 @@
 <template>
   <form class="ui form" @submit.prevent="fetchInstanceInfo">
     <h3 class="ui header">
-      {{ $gettext('Federate with a new instance') }}
+      <translate>Federate with a new instance</translate>
     </h3>
     <p>
-      {{ $gettext('Use this form to scan an instance and setup federation.') }}
+      <translate>Use this form to scan an instance and setup federation.</translate>
     </p>
     <div v-if="errors.length > 0 || scanErrors.length > 0" class="ui negative message">
       <div class="header">
-        {{ $gettext('Error while scanning library') }}
+        <translate>Error while scanning library</translate>
       </div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
@@ -18,7 +18,7 @@
     <div class="ui two fields">
       <div class="ui field">
         <label>
-          {{ $gettext('Library name') }}
+          <translate>Library name</translate>
         </label>
         <input v-model="libraryUsername" type="text" placeholder="library@demo.funkwhale.audio" />
       </div>
@@ -29,7 +29,7 @@
           :disabled="isLoading"
           :class="['ui', 'icon', {loading: isLoading}, 'button']">
           <i class="search icon"></i>
-          {{ $gettext('Launch scan') }}
+          <translate>Launch scan</translate>
         </button>
       </div>
     </div>
diff --git a/front/src/components/federation/LibraryTrackTable.vue b/front/src/components/federation/LibraryTrackTable.vue
index 058c2137..48d84942 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>{{ $gettext('Search') }}</label>
+          <label><translate>Search</translate></label>
           <input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
         </div>
         <div class="ui field">
-          <label>{{ $gettext('Import status') }}</label>
+          <label><translate>Import status</translate></label>
           <select class="ui dropdown" v-model="importedFilter">
-            <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>
+            <option :value="null"><translate>Any</translate></option>
+            <option :value="'imported'"><translate>Imported</translate></option>
+            <option :value="'not_imported'"><translate>Not imported</translate></option>
+            <option :value="'import_pending'"><translate>Import pending</translate></option>
           </select>
         </div>
       </div>
@@ -29,12 +29,12 @@
         :action-url="'federation/library-tracks/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <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>
+          <th><translate>Status</translate></th>
+          <th><translate>Title</translate></th>
+          <th><translate>Artist</translate></th>
+          <th><translate>Album</translate></th>
+          <th><translate>Published date</translate></th>
+          <th v-if="showLibrary"><translate>Library</translate></th>
         </template>
         <template slot="action-success-footer" slot-scope="scope">
           <router-link
@@ -48,9 +48,9 @@
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
-            <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>
+            <span v-if="scope.obj.status === 'imported'" class="ui basic green label"><translate>In library</translate></span>
+            <span v-else-if="scope.obj.status === 'import_pending'" class="ui basic yellow label"><translate>Import pending</translate></span>
+            <span v-else class="ui basic label"><translate>Not imported</translate></span>
           </td>
           <td>
             <span :title="scope.obj.title">{{ scope.obj.title|truncate(30) }}</span>
diff --git a/front/src/components/instance/Stats.vue b/front/src/components/instance/Stats.vue
index fbb9e07f..4f145ba9 100644
--- a/front/src/components/instance/Stats.vue
+++ b/front/src/components/instance/Stats.vue
@@ -3,7 +3,7 @@
     <div v-if="stats" class="ui stackable two column grid">
       <div class="column">
         <h3 class="ui left aligned header">
-          {{ $gettext('User activity') }}
+          <translate>User activity</translate>
         </h3>
         <div v-if="stats" class="ui mini horizontal statistics">
           <div class="statistic">
@@ -11,19 +11,19 @@
               <i class="green user icon"></i>
               {{ stats.users }}
             </div>
-            <div class="label">{{ $gettext('users') }}</div>
+            <div class="label"><translate>users</translate></div>
           </div>
           <div class="statistic">
             <div class="value">
               <i class="orange sound icon"></i> {{ stats.listenings }}
             </div>
-            <div class="label">{{ $gettext('tracks listened') }}</div>
+            <div class="label"><translate>tracks listened</translate></div>
           </div>
           <div class="statistic">
             <div class="value">
               <i class="pink heart icon"></i> {{ stats.trackFavorites }}
             </div>
-            <div class="label">{{ $gettext('Tracks favorited') }}</div>
+            <div class="label"><translate>Tracks favorited</translate></div>
           </div>
         </div>
       </div>
@@ -34,25 +34,25 @@
             <div class="value">
               {{ parseInt(stats.musicDuration) }}
             </div>
-            <div class="label">{{ $gettext('Hours of music') }}</div>
+            <div class="label"><translate>Hours of music</translate></div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.artists }}
             </div>
-            <div class="label">{{ $gettext('Artists') }}</div>
+            <div class="label"><translate>Artists</translate></div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.albums }}
             </div>
-            <div class="label">{{ $gettext('Albums') }}</div>
+            <div class="label"><translate>Albums</translate></div>
           </div>
           <div class="statistic">
             <div class="value">
               {{ stats.tracks }}
             </div>
-            <div class="label">{{ $gettext('tracks') }}</div>
+            <div class="label"><translate>tracks</translate></div>
           </div>
         </div>
       </div>
diff --git a/front/src/components/library/Album.vue b/front/src/components/library/Album.vue
index 76c283a6..b4cae319 100644
--- a/front/src/components/library/Album.vue
+++ b/front/src/components/library/Album.vue
@@ -20,28 +20,28 @@
             </div>
             <div class="ui basic buttons">
               <router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
-                {{ $gettext('Artist page') }}
+                <translate>Artist page</translate>
               </router-link>
             </div>
           </h2>
           <div class="ui hidden divider"></div>
           <play-button class="orange" :tracks="album.tracks">
-            {{ $gettext('Play all') }}
+            <translate>Play all</translate>
           </play-button>
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            {{ $gettext('Search on Wikipedia') }}
+            <translate>Search on Wikipedia</translate>
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            {{ $gettext('View on MusicBrainz') }}
+            <translate>View on MusicBrainz</translate>
           </a>
         </div>
       </div>
       <div class="ui vertical stripe segment">
         <h2>
-          {{ $gettext('Tracks') }}
+          <translate>Tracks</translate>
         </h2>
         <track-table v-if="album" :display-position="true" :tracks="album.tracks"></track-table>
       </div>
diff --git a/front/src/components/library/Artist.vue b/front/src/components/library/Artist.vue
index 390f431f..ce147009 100644
--- a/front/src/components/library/Artist.vue
+++ b/front/src/components/library/Artist.vue
@@ -24,16 +24,16 @@
           <div class="ui hidden divider"></div>
           <radio-button type="artist" :object-id="artist.id"></radio-button>
           <play-button class="orange" :artist="artist.id">
-            {{ $gettext('Play all albums') }}
+            <translate>Play all albums</translate>
           </play-button>
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            {{ $gettext('Search on Wikipedia') }}
+            <translate>Search on Wikipedia</translate>
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            {{ $gettext('View on MusicBrainz') }}
+            <translate>View on MusicBrainz</translate>
           </a>
         </div>
       </div>
@@ -42,7 +42,7 @@
       </div>
       <div v-else-if="albums" class="ui vertical stripe segment">
         <h2>
-          {{ $gettext('Albums by this artist') }}
+          <translate>Albums by this artist</translate>
         </h2>
         <div class="ui stackable doubling three column grid">
           <div class="column" :key="album.id" v-for="album in albums">
diff --git a/front/src/components/library/Artists.vue b/front/src/components/library/Artists.vue
index 527a1105..9d02971c 100644
--- a/front/src/components/library/Artists.vue
+++ b/front/src/components/library/Artists.vue
@@ -2,18 +2,18 @@
   <div v-title="'Artists'">
     <div class="ui vertical stripe segment">
       <h2 class="ui header">
-        {{ $gettext('Browsing artists') }}
+        <translate>Browsing artists</translate>
       </h2>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
             <label>
-              {{ $gettext('Search') }}
+              <translate>Search</translate>
             </label>
             <input type="text" v-model="query" placeholder="Enter an artist name..."/>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering') }}</label>
+            <label><translate>Ordering</translate></label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -21,14 +21,14 @@
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering direction') }}</label>
+            <label><translate>Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
               <option value="+">Ascending</option>
               <option value="-">Descending</option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Results per page') }}</label>
+            <label><translate>Results per page</translate></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 8fbb7d29..f38c0d64 100644
--- a/front/src/components/library/Home.vue
+++ b/front/src/components/library/Home.vue
@@ -7,7 +7,7 @@
       <div class="ui stackable three column grid">
         <div class="column">
           <h2 class="ui header">
-            {{ $gettext('Latest artists') }}
+            <translate>Latest artists</translate>
           </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">
@@ -16,7 +16,7 @@
         </div>
         <div class="column">
           <h2 class="ui header">
-            {{ $gettext('Radios') }}
+            <translate>Radios</translate>
           </h2>
           <radio-card :type="'favorites'"></radio-card>
           <radio-card :type="'random'"></radio-card>
@@ -24,7 +24,7 @@
         </div>
         <div class="column">
           <h2 class="ui header">
-            {{ $gettext('Music requests') }}
+            <translate>Music requests</translate>
           </h2>
           <request-form v-if="$store.state.auth.authenticated"></request-form>
         </div>
diff --git a/front/src/components/library/Library.vue b/front/src/components/library/Library.vue
index 71e270c3..ee01539a 100644
--- a/front/src/components/library/Library.vue
+++ b/front/src/components/library/Library.vue
@@ -2,23 +2,23 @@
   <div class="main library pusher">
     <div class="ui secondary pointing menu">
       <router-link class="ui item" to="/library" exact>
-        {{ $gettext('Browse') }}
+        <translate>Browse</translate>
       </router-link>
       <router-link class="ui item" to="/library/artists" exact>
-        {{ $gettext('Artists') }}
+        <translate>Artists</translate>
       </router-link>
       <router-link class="ui item" to="/library/radios" exact>
-        {{ $gettext('Radios') }}
+        <translate>Radios</translate>
       </router-link>
       <router-link class="ui item" to="/library/playlists" exact>
-        {{ $gettext('Playlists') }}
+        <translate>Playlists</translate>
       </router-link>
       <div class="ui secondary right menu">
         <router-link v-if="showImports" class="ui item" to="/library/import/launch" exact>
-          {{ $gettext('Import') }}
+          <translate>Import</translate>
         </router-link>
         <router-link v-if="showImports" class="ui item" to="/library/import/batches">
-          {{ $gettext('Import batches') }}
+          <translate>Import batches</translate>
         </router-link>
       </div>
     </div>
diff --git a/front/src/components/library/Radios.vue b/front/src/components/library/Radios.vue
index 1413deae..1438e2f4 100644
--- a/front/src/components/library/Radios.vue
+++ b/front/src/components/library/Radios.vue
@@ -2,20 +2,20 @@
   <div v-title="'Radios'">
     <div class="ui vertical stripe segment">
       <h2 class="ui header">
-        {{ $gettext('Browsing radios') }}
+        <translate>Browsing radios</translate>
       </h2>
       <router-link class="ui green basic button" to="/library/radios/build" exact>
-        {{ $gettext('Create your own radio') }}
+        <translate>Create your own radio</translate>
       </router-link>
       <div class="ui hidden divider"></div>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <label>{{ $gettext('Search') }}</label>
+            <label><translate>Search</translate></label>
             <input type="text" v-model="query" placeholder="Enter a radio name..."/>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering') }}</label>
+            <label><translate>Ordering</translate></label>
             <select class="ui dropdown" v-model="ordering">
               <option v-for="option in orderingOptions" :value="option[0]">
                 {{ option[1] }}
@@ -23,18 +23,18 @@
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering direction') }}</label>
+            <label><translate>Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
               <option value="+">
-                {{ $gettext('Ascending') }}
+                <translate>Ascending</translate>
               </option>
               <option value="-">
-                {{ $gettext('Descending') }}
+                <translate>Descending</translate>
               </option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Results per page') }}</label>
+            <label><translate>Results per page</translate></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 04f44904..d4ed13ad 100644
--- a/front/src/components/library/Track.vue
+++ b/front/src/components/library/Track.vue
@@ -18,17 +18,17 @@
               <br>
               <div class="ui basic buttons">
                 <router-link class="ui button" :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
-                  {{ $gettext('Album page') }}
+                  <translate>Album page</translate>
                 </router-link>
                 <router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
-                  {{ $gettext('Artist page') }}
+                  <translate>Artist page</translate>
                 </router-link>
               </div>
             </div>
           </h2>
 
           <play-button class="orange" :track="track">
-            {{ $gettext('Play') }}
+            <translate>Play</translate>
           </play-button>
           <track-favorite-icon :track="track" :button="true"></track-favorite-icon>
           <track-playlist-icon
@@ -38,53 +38,53 @@
 
           <a :href="wikipediaUrl" target="_blank" class="ui button">
             <i class="wikipedia icon"></i>
-            {{ $gettext('Search on Wikipedia') }}
+            <translate>Search on Wikipedia</translate>
           </a>
           <a :href="musicbrainzUrl" target="_blank" class="ui button">
             <i class="external icon"></i>
-            {{ $gettext('View on MusicBrainz') }}
+            <translate>View on MusicBrainz</translate>
           </a>
           <a v-if="downloadUrl" :href="downloadUrl" target="_blank" class="ui button">
             <i class="download icon"></i>
-            {{ $gettext('Download') }}
+            <translate>Download</translate>
           </a>
         </div>
       </div>
       <div v-if="file" class="ui vertical stripe center aligned segment">
-        <h2 class="ui header">{{ $gettext('Track information') }}</h2>
+        <h2 class="ui header"><translate>Track information</translate></h2>
         <table class="ui very basic collapsing celled center aligned table">
           <tbody>
             <tr>
               <td>
-                {{ $gettext('Duration') }}
+                <translate>Duration</translate>
               </td>
               <td v-if="file.duration">
                 {{ time.parse(file.duration) }}
               </td>
               <td v-else>
-                {{ $gettext('N/A') }}
+                <translate>N/A</translate>
               </td>
             </tr>
             <tr>
               <td>
-                {{ $gettext('Size') }}
+                <translate>Size</translate>
               </td>
               <td v-if="file.size">
                 {{ file.size | humanSize }}
               </td>
               <td v-else>
-                {{ $gettext('N/A') }}
+                <translate>N/A</translate>
               </td>
             </tr>
             <tr>
               <td>
-                {{ $gettext('Bitrate') }}
+                <translate>Bitrate</translate>
               </td>
               <td v-if="file.bitrate">
                 {{ file.bitrate | humanSize }}/s
               </td>
               <td v-else>
-                {{ $gettext('N/A') }}
+                <translate>N/A</translate>
               </td>
             </tr>
           </tbody>
@@ -92,7 +92,7 @@
       </div>
       <div class="ui vertical stripe center aligned segment">
         <h2>
-          {{ $gettext('Lyrics') }}
+          <translate>Lyrics</translate>
         </h2>
         <div v-if="isLoadingLyrics" class="ui vertical segment">
           <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
@@ -100,10 +100,10 @@
         <div v-if="lyrics" v-html="lyrics.content_rendered">
         </div>
         <template v-if="!isLoadingLyrics & !lyrics">
-          <p>{{ $gettext('No lyrics available for this track.') }}</p>
+          <p><translate>No lyrics available for this track.</translate></p>
           <a class="ui button" target="_blank" :href="lyricsSearchUrl">
             <i class="search icon"></i>
-            {{ $gettext('Search on lyrics.wikia.com') }}
+            <translate>Search on lyrics.wikia.com</translate>
           </a>
         </template>
       </div>
diff --git a/front/src/components/library/import/ArtistImport.vue b/front/src/components/library/import/ArtistImport.vue
index 0db28eb8..2f37edc6 100644
--- a/front/src/components/library/import/ArtistImport.vue
+++ b/front/src/components/library/import/ArtistImport.vue
@@ -5,7 +5,7 @@
     </h3>
     <form class="ui form" @submit.prevent="">
       <h6 class="ui header">
-        {{ $gettext('Filter album types') }}
+        <translate>Filter album types</translate>
       </h6>
       <div class="inline fields">
         <div class="field" v-for="t in availableReleaseTypes">
@@ -15,7 +15,7 @@
           </div>
         </div>
         <div class="field">
-          <label>{{ $gettext('Query template') }}</label>
+          <label><translate>Query template</translate></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 2390ea70..b0d64a3d 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>{{ $gettext('Import batch') }}</strong>
+              <strong><translate>Import batch</translate></strong>
             </td>
             <td>
               #{{ batch.id }}
@@ -16,7 +16,7 @@
           </tr>
           <tr>
             <td>
-              <strong>{{ $gettext('Launch date') }}</strong>
+              <strong><translate>Launch date</translate></strong>
             </td>
             <td>
               <human-date :date="batch.creation_date"></human-date>
@@ -24,22 +24,22 @@
           </tr>
           <tr v-if="batch.user">
             <td>
-              <strong>{{ $gettext('Submitted by') }}</strong>
+              <strong><translate>Submitted by</translate></strong>
             </td>
             <td>
               <username :username="batch.user.username" />
             </td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $gettext('Pending') }}</strong></td>
+            <td><strong><translate>Pending</translate></strong></td>
             <td>{{ stats.pending }}</td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $gettext('Skipped') }}</strong></td>
+            <td><strong><translate>Skipped</translate></strong></td>
             <td>{{ stats.skipped }}</td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $gettext('Errored') }}</strong></td>
+            <td><strong><translate>Errored</translate></strong></td>
             <td>
               {{ stats.errored }}
               <button
@@ -52,7 +52,7 @@
             </td>
           </tr>
           <tr v-if="stats">
-            <td><strong>{{ $gettext('Finished') }}</strong></td>
+            <td><strong><translate>Finished</translate></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>{{ $gettext('Search') }}</label>
+            <label><translate>Search</translate></label>
             <input type="text" v-model="jobFilters.search" placeholder="Search by source..." />
           </div>
           <div class="ui field">
-            <label>{{ $gettext('Status') }}</label>
+            <label><translate>Status</translate></label>
             <select class="ui dropdown" v-model="jobFilters.status">
-              <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>
+              <option :value="null"><translate>Any</translate></option>
+              <option :value="'pending'"><translate>Pending</translate></option>
+              <option :value="'errored'"><translate>Errored</translate></option>
+              <option :value="'finished'"><translate>Success</translate></option>
+              <option :value="'skipped'"><translate>Skipped</translate></option>
             </select>
           </div>
         </div>
@@ -78,11 +78,11 @@
       <table v-if="jobResult" class="ui unstackable table">
         <thead>
           <tr>
-            <th>{{ $gettext('Job ID') }}</th>
-            <th>{{ $gettext('Recording MusicBrainz ID') }}</th>
-            <th>{{ $gettext('Source') }}</th>
-            <th>{{ $gettext('Status') }}</th>
-            <th>{{ $gettext('Track') }}</th>
+            <th><translate>Job ID</translate></th>
+            <th><translate>Recording MusicBrainz ID</translate></th>
+            <th><translate>Source</translate></th>
+            <th><translate>Status</translate></th>
+            <th><translate>Track</translate></th>
           </tr>
         </thead>
         <tbody>
diff --git a/front/src/components/library/import/BatchList.vue b/front/src/components/library/import/BatchList.vue
index bbc553e1..a7a7de91 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>{{ $gettext('Search') }}</label>
+            <label><translate>Search</translate></label>
             <input type="text" v-model="filters.search" placeholder="Search by submitter, source..." />
           </div>
           <div class="ui field">
-            <label>{{ $gettext('Status') }}</label>
+            <label><translate>Status</translate></label>
             <select class="ui dropdown" v-model="filters.status">
-              <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="null"><translate>Any</translate></option>
+              <option :value="'pending'"><translate>Pending</translate></option>
+              <option :value="'errored'"><translate>Errored</translate></option>
+              <option :value="'finished'"><translate>Success</translate></option>
             </select>
           </div>
           <div class="ui field">
-            <label>{{ $gettext('Import source') }}</label>
+            <label><translate>Import source</translate></label>
             <select class="ui dropdown" v-model="filters.source">
-              <option :value="null">{{ $gettext('Any') }}</option>
-              <option :value="'shell'">{{ $gettext('CLI') }}</option>
-              <option :value="'api'">{{ $gettext('API') }}</option>
-              <option :value="'federation'">{{ $gettext('Federation') }}</option>
+              <option :value="null"><translate>Any</translate></option>
+              <option :value="'shell'"><translate>CLI</translate></option>
+              <option :value="'api'"><translate>API</translate></option>
+              <option :value="'federation'"><translate>Federation</translate></option>
             </select>
           </div>
         </div>
@@ -32,12 +32,12 @@
       <table v-if="result && result.results.length > 0" class="ui unstackable table">
         <thead>
           <tr>
-            <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>
+            <th><translate>ID</translate></th>
+            <th><translate>Launch date</translate></th>
+            <th><translate>Jobs</translate></th>
+            <th><translate>Status</translate></th>
+            <th><translate>Source</translate></th>
+            <th><translate>Submitted by</translate></th>
           </tr>
         </thead>
         <tbody>
diff --git a/front/src/components/library/import/FileUpload.vue b/front/src/components/library/import/FileUpload.vue
index 635fd9bf..88ff8300 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">
-        {{ $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>
+        <translate>Ensure your music files are properly tagged before uploading them.</translate>
+        <a href="http://picard.musicbrainz.org/" target='_blank'><translate>We recommend using Picard for that purpose.</translate></a>
       </div>
       <file-upload-widget
         :class="['ui', 'icon', 'left', 'floated', 'button']"
@@ -20,30 +20,30 @@
         @input-file="inputFile"
         ref="upload">
         <i class="upload icon"></i>
-        {{ $gettext('Select files to upload...') }}
+        <translate>Select files to upload...</translate>
     </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>
-        {{ $gettext('Start Upload') }}
+        <translate>Start Upload</translate>
       </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>
-        {{ $gettext('Stop Upload') }}
+        <translate>Stop Upload</translate>
       </button>
     </div>
     <div class="ui hidden clearing divider"></div>
-    <template v-if="batch">{{ $gettext('Once all your files are uploaded, simply click the following button to check the import status.') }}</template>
+    <template v-if="batch"><translate>Once all your files are uploaded, simply click the following button to check the import status.</translate></template>
     <router-link class="ui basic button" v-if="batch" :to="{name: 'library.import.batches.detail', params: {id: batch.id }}">
-      {{ $gettext('Import detail page') }}
+      <translate>Import detail page</translate>
     </router-link>
     <table class="ui single line table">
       <thead>
         <tr>
-          <th>{{ $gettext('File name') }}</th>
-          <th>{{ $gettext('Size') }}</th>
-          <th>{{ $gettext('Status') }}</th>
+          <th><translate>File name</translate></th>
+          <th><translate>Size</translate></th>
+          <th><translate>Status</translate></th>
         </tr>
       </thead>
       <tbody>
@@ -54,10 +54,10 @@
             <span v-if="file.error" class="ui red label">
               {{ file.error }}
             </span>
-            <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>
+            <span v-else-if="file.success" class="ui green label"><translate>Success</translate></span>
+            <span v-else-if="file.active" class="ui yellow label"><translate>Uploading...</translate></span>
             <template v-else>
-              <span class="ui label">{{ $gettext('Pending') }}</span>
+              <span class="ui label"><translate>Pending</translate></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 9482df3a..b24bb8eb 100644
--- a/front/src/components/library/import/Main.vue
+++ b/front/src/components/library/import/Main.vue
@@ -4,30 +4,30 @@
       <div class="ui top three attached ordered steps">
         <a @click="currentStep = 0" :class="['step', {'active': currentStep === 0}, {'completed': currentStep > 0}]">
           <div class="content">
-            <div class="title">{{ $gettext('Import source') }}</div>
-            <div class="description">{{ $gettext('Uploaded files or external source') }}</div>
+            <div class="title"><translate>Import source</translate></div>
+            <div class="description"><translate>Uploaded files or external source</translate></div>
           </div>
         </a>
         <a @click="currentStep = 1" :class="['step', {'active': currentStep === 1}, {'completed': currentStep > 1}]">
           <div class="content">
-            <div class="title">{{ $gettext('Metadata') }}</div>
-            <div class="description">{{ $gettext('Grab corresponding metadata') }}</div>
+            <div class="title"><translate>Metadata</translate></div>
+            <div class="description"><translate>Grab corresponding metadata</translate></div>
           </div>
         </a>
         <a @click="currentStep = 2" :class="['step', {'active': currentStep === 2}, {'completed': currentStep > 2}]">
           <div class="content">
-            <div class="title">{{ $gettext('Music') }}</div>
-            <div class="description">{{ $gettext('Select relevant sources or files for import') }}</div>
+            <div class="title"><translate>Music</translate></div>
+            <div class="description"><translate>Select relevant sources or files for import</translate></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>
-          {{ $gettext('Previous step') }}
+          <translate>Previous step</translate>
         </button>
         <button @click="nextStep()" v-if="currentStep < 2" class="ui icon button">
-          {{ $gettext('Next step') }}
+          <translate>Next step</translate>
           <i class="right arrow icon"></i>
         </button>
         <button
@@ -57,13 +57,13 @@
       <div class="ui hidden divider"></div>
       <div class="ui attached segment">
         <template v-if="currentStep === 0">
-          <p>{{ $gettext('First, choose where you want to import the music from') }}</p>
+          <p><translate>First, choose where you want to import the music from</translate></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">
-                  {{ $gettext('External source. Supported backends') }}
+                  <translate>External source. Supported backends</translate>
                   <div v-for="backend in backends" class="ui basic label">
                     <i v-if="backend.icon" :class="[backend.icon, 'icon']"></i>
                     {{ backend.label }}
@@ -74,7 +74,7 @@
             <div class="field">
               <div class="ui radio checkbox">
                 <input type="radio" id="upload" value="upload" v-model="currentSource">
-                <label for="upload">{{ $gettext('File upload') }}</label>
+                <label for="upload"><translate>File upload</translate></label>
               </div>
             </div>
           </form>
@@ -83,7 +83,7 @@
           <div class="column">
             <form class="ui form" @submit.prevent="">
               <div class="field">
-                <label>{{ $gettext('Search an entity you want to import:') }}</label>
+                <label><translate>Search an entity you want to import:</translate></label>
                 <metadata-search
                   :mb-type="mbType"
                   :mb-id="mbId"
@@ -91,17 +91,17 @@
                   @type-changed="updateType"></metadata-search>
               </div>
             </form>
-            <div class="ui horizontal divider">{{ $gettext('Or') }}</div>
+            <div class="ui horizontal divider"><translate>Or</translate></div>
             <form class="ui form" @submit.prevent="">
               <div class="field">
-                <label>{{ $gettext('Input a MusicBrainz ID manually:') }}</label>
+                <label><translate>Input a MusicBrainz ID manually:</translate></label>
                 <input type="text" v-model="currentId" />
               </div>
             </form>
             <div class="ui hidden divider"></div>
             <template v-if="currentType && currentId">
               <h4 class="ui header">
-                {{ $gettext('You will import:') }}
+                <translate>You will import:</translate>
               </h4>
               <component
                 :mbId="currentId"
@@ -109,7 +109,7 @@
                 @metadata-changed="this.updateMetadata"
                 ></component>
             </template>
-            <p>{{ $gettext('You can also skip this step and enter metadata manually.') }}</p>
+            <p><translate>You can also skip this step and enter metadata manually.</translate></p>
           </div>
           <div class="column">
             <h5 class="ui header">What is metadata?</h5>
@@ -145,9 +145,9 @@
     </div>
     <div class="ui vertical stripe segment" v-if="currentRequest">
       <h3 class="ui header">
-        {{ $gettext('Music request') }}
+        <translate>Music request</translate>
       </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>
+      <p><translate>This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled.</translate></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 c351088f..0ec78903 100644
--- a/front/src/components/library/import/ReleaseImport.vue
+++ b/front/src/components/library/import/ReleaseImport.vue
@@ -12,7 +12,7 @@
       <div class="sub header">
         <div class="ui toggle checkbox">
           <input type="checkbox" v-model="enabled" />
-          <label>{{ $gettext('Import this release') }}</label>
+          <label><translate>Import this release</translate></label>
         </div>
       </div>
     </h3>
diff --git a/front/src/components/library/import/TrackImport.vue b/front/src/components/library/import/TrackImport.vue
index 1b7378b1..10a14634 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" />
-        <label>{{ $gettext('Import this track') }}</label>
+        <label><translate>Import this track</translate></label>
       </div>
     </div>
     <div class="three wide column" v-if="enabled">
       <form class="ui mini form" @submit.prevent="">
         <div class="field">
-          <label>{{ $gettext('Source') }}</label>
+          <label><translate>Source</translate></label>
           <select v-model="currentBackendId">
             <option v-for="backend in backends" :value="backend.id">
               {{ backend.label }}
@@ -40,9 +40,9 @@
     <div class="four wide column" v-if="enabled">
       <form class="ui mini form" @submit.prevent="">
         <div class="field">
-          <label>{{ $gettext('Search query') }}</label>
+          <label><translate>Search query</translate></label>
           <input type="text" v-model="query" />
-          <label>{{ $gettext('Imported URL') }}</label>
+          <label><translate>Imported URL</translate></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 6dadcc9b..2b8e71a3 100644
--- a/front/src/components/library/radios/Builder.vue
+++ b/front/src/components/library/radios/Builder.vue
@@ -3,38 +3,38 @@
     <div>
       <div>
         <h2 class="ui header">
-          {{ $gettext('Builder') }}
+          <translate>Builder</translate>
         </h2>
-        <p>{{ $gettext('You can use this interface to build your own custom radio, which will play tracks according to your criteria.') }}</p>
+        <p><translate>You can use this interface to build your own custom radio, which will play tracks according to your criteria.</translate></p>
           <div class="ui form">
           <div class="inline fields">
             <div class="field">
-              <label for="name">{{ $gettext('Radio name') }}</label>
+              <label for="name"><translate>Radio name</translate></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" />
-              <label for="public">{{ $gettext('Display publicly') }}</label>
+              <label for="public"><translate>Display publicly</translate></label>
             </div>
             <button :disabled="!canSave" @click="save" class="ui green button">
-              {{ $gettext('Save') }}
+              <translate>Save</translate>
             </button>
             <radio-button v-if="id" type="custom" :custom-radio-id="id"></radio-button>
           </div>
         </div>
         <div class="ui form">
           <p>
-            {{ $gettext('Add filters to customize your radio') }}
+            <translate>Add filters to customize your radio</translate>
           </p>
           <div class="inline field">
             <select class="ui dropdown" v-model="currentFilterType">
               <option value="">
-                {{ $gettext('Select a filter') }}
+                <translate>Select a filter</translate>
               </option>
               <option v-for="f in availableFilters" :value="f.type">{{ f.label }}</option>
             </select>
             <button :disabled="!currentFilterType" @click="add" class="ui button">
-              {{ $gettext('Add filter') }}
+              <translate>Add filter</translate>
             </button>
           </div>
           <p v-if="currentFilter">
@@ -44,11 +44,11 @@
         <table class="ui table">
           <thead>
             <tr>
-              <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>
+              <th class="two wide"><translate>Filter name</translate></th>
+              <th class="one wide"><translate>Exclude</translate></th>
+              <th class="six wide"><translate>Config</translate></th>
+              <th class="five wide"><translate>Candidates</translate></th>
+              <th class="two wide"><translate>Actions</translate></th>
             </tr>
           </thead>
           <tbody>
diff --git a/front/src/components/library/radios/Filter.vue b/front/src/components/library/radios/Filter.vue
index 9c31b59b..470d852f 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">
-          {{ $gettext('Track matching filter') }}
+          <translate>Track matching filter</translate>
         </div>
         <div class="content">
           <div class="description">
@@ -51,13 +51,13 @@
         </div>
         <div class="actions">
           <div class="ui black deny button">
-            {{ $gettext('Cancel') }}
+            <translate>Cancel</translate>
           </div>
         </div>
       </modal>
     </td>
     <td>
-      <button @click="$emit('delete', index)" class="ui basic red button">{{ $gettext('Remove') }}</button>
+      <button @click="$emit('delete', index)" class="ui basic red button"><translate>Remove</translate></button>
     </td>
   </tr>
 </template>
diff --git a/front/src/components/manage/library/FilesTable.vue b/front/src/components/manage/library/FilesTable.vue
index 604c8fc3..98b23f06 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>{{ $gettext('Search') }}</label>
+          <label><translate>Search</translate></label>
           <input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
         </div>
         <div class="field">
-          <label>{{ $gettext('Ordering') }}</label>
+          <label><translate>Ordering</translate></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">
-          <label>{{ $gettext('Ordering direction') }}</label>
+          <label><translate>Ordering direction</translate></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>{{ $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>
+          <th><translate>Title</translate></th>
+          <th><translate>Artist</translate></th>
+          <th><translate>Album</translate></th>
+          <th><translate>Import date</translate></th>
+          <th><translate>Type</translate></th>
+          <th><translate>Bitrate</translate></th>
+          <th><translate>Duration</translate></th>
+          <th><translate>Size</translate></th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
@@ -61,25 +61,25 @@
             {{ scope.obj.audio_mimetype }}
           </td>
           <td v-else>
-            {{ $gettext('N/A') }}
+            <translate>N/A</translate>
           </td>
           <td v-if="scope.obj.bitrate">
             {{ scope.obj.bitrate | humanSize }}/s
           </td>
           <td v-else>
-            {{ $gettext('N/A') }}
+            <translate>N/A</translate>
           </td>
           <td v-if="scope.obj.duration">
             {{ time.parse(scope.obj.duration) }}
           </td>
           <td v-else>
-            {{ $gettext('N/A') }}
+            <translate>N/A</translate>
           </td>
           <td v-if="scope.obj.size">
             {{ scope.obj.size | humanSize }}
           </td>
           <td v-else>
-            {{ $gettext('N/A') }}
+            <translate>N/A</translate>
           </td>
         </template>
       </action-table>
diff --git a/front/src/components/manage/library/RequestsTable.vue b/front/src/components/manage/library/RequestsTable.vue
index 7707afbf..4c2883ff 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>{{ $gettext('Search') }}</label>
+          <label><translate>Search</translate></label>
           <input type="text" v-model="search" placeholder="Search by artist, username, comment..." />
         </div>
         <div class="field">
-          <label>{{ $gettext('Ordering') }}</label>
+          <label><translate>Ordering</translate></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">
-          <label>{{ $gettext('Ordering direction') }}</label>
+          <label><translate>Ordering direction</translate></label>
           <select class="ui dropdown" v-model="orderingDirection">
             <option value="+">Ascending</option>
             <option value="-">Descending</option>
@@ -24,11 +24,11 @@
         <div class="field">
           <label>{{ $gettext("Status") }}</label>
           <select class="ui dropdown" v-model="status">
-            <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>
+            <option :value="null"><translate>All</translate></option>
+            <option :value="'pending'"><translate>Pending</translate></option>
+            <option :value="'accepted'"><translate>Accepted</translate></option>
+            <option :value="'imported'"><translate>Imported</translate></option>
+            <option :value="'closed'"><translate>Closed</translate></option>
           </select>
         </div>
       </div>
@@ -45,24 +45,24 @@
         :action-url="'manage/requests/import-requests/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <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>
+          <th><translate>User</translate></th>
+          <th><translate>Status</translate></th>
+          <th><translate>Artist</translate></th>
+          <th><translate>Albums</translate></th>
+          <th><translate>Comment</translate></th>
+          <th><translate>Creation date</translate></th>
+          <th><translate>Import date</translate></th>
+          <th><translate>Actions</translate></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'">{{ $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>
+            <span class="ui green basic label" v-if="scope.obj.status === 'imported'"><translate>Imported</translate></span>
+            <span class="ui pink basic label" v-else-if="scope.obj.status === 'accepted'"><translate>Accepted</translate></span>
+            <span class="ui yellow basic label" v-else-if="scope.obj.status === 'pending'"><translate>Pending</translate></span>
+            <span class="ui red basic label" v-else-if="scope.obj.status === 'closed'"><translate>Closed</translate></span>
           </td>
           <td>
             <span :title="scope.obj.artist_name">{{ scope.obj.artist_name|truncate(30) }}</span>
diff --git a/front/src/components/manage/users/InvitationForm.vue b/front/src/components/manage/users/InvitationForm.vue
index 28111d4e..92e419bd 100644
--- a/front/src/components/manage/users/InvitationForm.vue
+++ b/front/src/components/manage/users/InvitationForm.vue
@@ -2,7 +2,7 @@
   <div>
     <form class="ui form" @submit.prevent="submit">
       <div v-if="errors.length > 0" class="ui negative message">
-        <div class="header">{{ $gettext('Error while creating invitation') }}</div>
+        <div class="header"><translate>Error while creating invitation</translate></div>
         <ul class="list">
           <li v-for="error in errors">{{ error }}</li>
         </ul>
@@ -14,7 +14,7 @@
         </div>
         <div class="ui field">
           <button :class="['ui', {loading: isLoading}, 'button']" :disabled="isLoading" type="submit">
-            {{ $gettext('Get a new invitation') }}
+            <translate>Get a new invitation</translate>
           </button>
         </div>
       </div>
@@ -24,8 +24,8 @@
       <table class="ui ui basic table">
         <thead>
           <tr>
-            <th>{{ $gettext('Code') }}</th>
-            <th>{{ $gettext('Share link') }}</th>
+            <th><translate>Code</translate></th>
+            <th><translate>Share link</translate></th>
           </tr>
         </thead>
         <tbody>
@@ -35,7 +35,7 @@
           </tr>
         </tbody>
       </table>
-      <button class="ui basic button" @click="invitations = []">{{ $gettext('Clear') }}</button>
+      <button class="ui basic button" @click="invitations = []"><translate>Clear</translate></button>
     </div>
   </div>
 </template>
diff --git a/front/src/components/manage/users/InvitationsTable.vue b/front/src/components/manage/users/InvitationsTable.vue
index ce34ddbc..c9700f20 100644
--- a/front/src/components/manage/users/InvitationsTable.vue
+++ b/front/src/components/manage/users/InvitationsTable.vue
@@ -3,7 +3,7 @@
     <div class="ui inline form">
       <div class="fields">
         <div class="ui field">
-          <label>{{ $gettext('Search') }}</label>
+          <label><translate>Search</translate></label>
           <input type="text" v-model="search" placeholder="Search by username, email, code..." />
         </div>
         <div class="field">
@@ -17,9 +17,9 @@
         <div class="field">
           <label>{{ $gettext("Status") }}</label>
           <select class="ui dropdown" v-model="isOpen">
-            <option :value="null">{{ $gettext('All') }}</option>
-            <option :value="true">{{ $gettext('Open') }}</option>
-            <option :value="false">{{ $gettext('Expired/used') }}</option>
+            <option :value="null"><translate>All</translate></option>
+            <option :value="true"><translate>Open</translate></option>
+            <option :value="false"><translate>Expired/used</translate></option>
           </select>
         </div>
       </div>
@@ -36,20 +36,20 @@
         :action-url="'manage/users/invitations/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <th>{{ $gettext('Owner') }}</th>
-          <th>{{ $gettext('Status') }}</th>
-          <th>{{ $gettext('Creation date') }}</th>
-          <th>{{ $gettext('Expiration date') }}</th>
-          <th>{{ $gettext('Code') }}</th>
+          <th><translate>Owner</translate></th>
+          <th><translate>Status</translate></th>
+          <th><translate>Creation date</translate></th>
+          <th><translate>Expiration date</translate></th>
+          <th><translate>Code</translate></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">{{ $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>
+            <span v-if="scope.obj.users.length > 0" class="ui green basic label"><translate>Used</translate></span>
+            <span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label"><translate>Expired</translate></span>
+            <span v-else class="ui basic label"><translate>Not used</translate></span>
           </td>
           <td>
             <human-date :date="scope.obj.creation_date"></human-date>
diff --git a/front/src/components/manage/users/UsersTable.vue b/front/src/components/manage/users/UsersTable.vue
index af2e2567..d33261f1 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>{{ $gettext('Search') }}</label>
+          <label><translate>Search</translate></label>
           <input type="text" v-model="search" placeholder="Search by username, email, name..." />
         </div>
         <div class="field">
-          <label>{{ $gettext('Ordering') }}</label>
+          <label><translate>Ordering</translate></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">
-          <label>{{ $gettext('Ordering direction') }}</label>
+          <label><translate>Ordering direction</translate></label>
           <select class="ui dropdown" v-model="orderingDirection">
-            <option value="+">{{ $gettext('Ascending') }}</option>
-            <option value="-">{{ $gettext('Descending') }}</option>
+            <option value="+"><translate>Ascending</translate></option>
+            <option value="-"><translate>Descending</translate></option>
           </select>
         </div>
       </div>
@@ -35,13 +35,13 @@
         :action-url="'manage/library/track-files/action/'"
         :filters="actionFilters">
         <template slot="header-cells">
-          <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>
+          <th><translate>Username</translate></th>
+          <th><translate>Email</translate></th>
+          <th><translate>Account status</translate></th>
+          <th><translate>Sign-up</translate></th>
+          <th><translate>Last activity</translate></th>
+          <th><translate>Permissions</translate></th>
+          <th><translate>Status</translate></th>
         </template>
         <template slot="row-cells" slot-scope="scope">
           <td>
@@ -59,7 +59,7 @@
           </td>
           <td>
             <human-date v-if="scope.obj.last_activity" :date="scope.obj.last_activity"></human-date>
-            <template v-else>{{ $gettext('N/A') }}</template>
+            <template v-else><translate>N/A</translate></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">{{ $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>
+            <span v-if="scope.obj.is_superuser" class="ui pink label"><translate>Admin</translate></span>
+            <span v-else-if="scope.obj.is_staff" class="ui purple label"><translate>Staff member</translate></span>
+            <span v-else class="ui basic label"><translate>regular user</translate></span>
           </td>
         </template>
       </action-table>
diff --git a/front/src/components/playlists/Editor.vue b/front/src/components/playlists/Editor.vue
index 012fb2b4..d7ae04b2 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">
-      {{ $gettext('Playlist editor') }}
+      <translate>Playlist editor</translate>
     </h3>
     <div class="ui attached segment">
       <template v-if="status === 'loading'">
         <div class="ui active tiny inline loader"></div>
-        {{ $gettext('Syncing changes to server...') }}
+        <translate>Syncing changes to server...</translate>
       </template>
       <template v-else-if="status === 'errored'">
         <i class="red close icon"></i>
-        {{ $gettext('An error occured while saving your changes') }}
+        <translate>An error occured while saving your changes</translate>
         <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> {{ $gettext('Changes synced with server') }}
+        <i class="green check icon"></i> <translate>Changes synced with server</translate>
       </template>
     </div>
     <div class="ui bottom attached segment">
@@ -38,12 +38,12 @@
         </div>
 
       <dangerous-button :disabled="plts.length === 0" class="labeled right floated icon" color='yellow' :action="clearPlaylist">
-        <i class="eraser icon"></i> {{ $gettext('Clear playlist') }}
+        <i class="eraser icon"></i> <translate>Clear playlist</translate>
         <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>
+        <p slot="modal-content"><translate>This will remove all tracks from this playlist and cannot be undone.</translate></p>
+        <p slot="modal-confirm"><translate>Clear playlist</translate></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 2d26594c..cbde4f8e 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">{{ $gettext('Create a new playlist') }}</h4>
+    <h4 v-if="title" class="ui header"><translate>Create a new playlist</translate></h4>
     <div v-if="success" class="ui positive message">
       <div class="header">
         <template v-if="playlist">
-          {{ $gettext('Playlist updated') }}
+          <translate>Playlist updated</translate>
         </template>
         <template v-else>
-          {{ $gettext('Playlist created') }}
+          <translate>Playlist created</translate>
         </template>
       </div>
     </div>
     <div v-if="errors.length > 0" class="ui negative message">
-      <div class="header">{{ $gettext('We cannot create the playlist') }}</div>
+      <div class="header"><translate>We cannot create the playlist</translate></div>
       <ul class="list">
         <li v-for="error in errors">{{ error }}</li>
       </ul>
     </div>
     <div class="three fields">
       <div class="field">
-        <label>{{ $gettext('Playlist name') }}</label>
+        <label><translate>Playlist name</translate></label>
         <input v-model="name" required type="text" placeholder="My awesome playlist" />
       </div>
       <div class="field">
-        <label>{{ $gettext('Playlist visibility') }}</label>
+        <label><translate>Playlist visibility</translate></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">{{ $gettext('Update playlist') }}</template>
-          <template v-else>{{ $gettext('Create playlist') }}</template>
+          <template v-if="playlist"><translate>Update playlist</translate></template>
+          <template v-else><translate>Create playlist</translate></template>
         </button>
       </div>
     </div>
diff --git a/front/src/components/playlists/PlaylistModal.vue b/front/src/components/playlists/PlaylistModal.vue
index feca8396..cee32dab 100644
--- a/front/src/components/playlists/PlaylistModal.vue
+++ b/front/src/components/playlists/PlaylistModal.vue
@@ -1,12 +1,12 @@
 <template>
   <modal @update:show="update" :show="$store.state.playlists.showModal">
     <div class="header">
-      {{ $gettext('Manage playlists') }}
+      <translate>Manage playlists</translate>
     </div>
     <div class="scrolling content">
       <div class="description">
         <template v-if="track">
-          <h4 class="ui header">{{ $gettext('Current track') }}</h4>
+          <h4 class="ui header"><translate>Current track</translate></h4>
           <div
             v-translate="{artist: track.artist.name, title: track.title}"
             :template-params="{artist: track.artist.name, title: track.title}">
@@ -18,20 +18,20 @@
         <playlist-form></playlist-form>
         <div class="ui divider"></div>
         <div v-if="errors.length > 0" class="ui negative message">
-          <div class="header">{{ $gettext('We cannot add the track to a playlist') }}</div>
+          <div class="header"><translate>We cannot add the track to a playlist</translate></div>
           <ul class="list">
             <li v-for="error in errors">{{ error }}</li>
           </ul>
         </div>
         </div>
-        <h4 class="ui header">{{ $gettext('Available playlists') }}</h4>
+        <h4 class="ui header"><translate>Available playlists</translate></h4>
         <table class="ui unstackable very basic table">
           <thead>
             <tr>
               <th></th>
-              <th>{{ $gettext('Name') }}</th>
-              <th class="sorted descending">{{ $gettext('Last modification') }}</th>
-              <th>{{ $gettext('Tracks') }}</th>
+              <th><translate>Name</translate></th>
+              <th class="sorted descending"><translate>Last modification</translate></th>
+              <th><translate>Tracks</translate></th>
               <th></th>
             </tr>
           </thead>
@@ -52,7 +52,7 @@
                   class="ui green icon basic small right floated button"
                   :title="$gettext('Add to this playlist')"
                   @click="addToPlaylist(playlist.id)">
-                  <i class="plus icon"></i> {{ $gettext('Add track') }}
+                  <i class="plus icon"></i> <translate>Add track</translate>
                 </div>
               </td>
             </tr>
@@ -61,7 +61,7 @@
       </div>
     </div>
     <div class="actions">
-      <div class="ui cancel button">{{ $gettext('Cancel') }}</div>
+      <div class="ui cancel button"><translate>Cancel</translate></div>
     </div>
   </modal>
 </template>
diff --git a/front/src/components/playlists/TrackPlaylistIcon.vue b/front/src/components/playlists/TrackPlaylistIcon.vue
index c669acb9..4883ba41 100644
--- a/front/src/components/playlists/TrackPlaylistIcon.vue
+++ b/front/src/components/playlists/TrackPlaylistIcon.vue
@@ -4,7 +4,7 @@
     v-if="button"
     :class="['ui', 'button']">
     <i class="list icon"></i>
-    {{ $gettext('Add to playlist...') }}
+    <translate>Add to playlist...</translate>
   </button>
   <i
     v-else
diff --git a/front/src/components/radios/Button.vue b/front/src/components/radios/Button.vue
index 5a112e96..a33478c9 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">{{ $gettext('Stop') }}</template>
-    <template v-else>{{ $gettext('Start') }}</template>
+    <template v-if="running"><translate>Stop</translate></template>
+    <template v-else><translate>Start</translate></template>
     radio
   </button>
 </template>
diff --git a/front/src/components/radios/Card.vue b/front/src/components/radios/Card.vue
index db92f827..68b453fa 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 }}">
-          {{ $gettext('Edit...') }}
+          <translate>Edit...</translate>
         </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 8bddc94f..36bbc944 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">{{ $gettext('Create import') }}</button>
+        class="ui mini basic green right floated button"><translate>Create import</translate></button>
 
     </div>
   </div>
diff --git a/front/src/components/requests/Form.vue b/front/src/components/requests/Form.vue
index d2e5a439..0b96c20e 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>{{ $gettext('Something\'s missing in the library? Let us know what you would like to listen!') }}</p>
+      <p><translate>Something's missing in the library? Let us know what you would like to listen!</translate></p>
       <div class="required field">
-        <label>{{ $gettext('Artist name') }}</label>
+        <label><translate>Artist name</translate></label>
         <input v-model="currentArtistName" placeholder="The Beatles, Mickael Jackson…" required maxlength="200">
       </div>
       <div class="field">
-        <label>{{ $gettext('Albums') }}</label>
-        <p>{{ $gettext('Leave this field empty if you\'re requesting the whole discography.') }}</p>
+        <label><translate>Albums</translate></label>
+        <p><translate>Leave this field empty if you're requesting the whole discography.</translate></p>
         <input v-model="currentAlbums" placeholder="The White Album, Thriller…" maxlength="2000">
       </div>
       <div class="field">
-        <label>{{ $gettext('Comment') }}</label>
+        <label><translate>Comment</translate></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">{{ $gettext('Submit') }}</button>
+      <button class="ui submit button" type="submit"><translate>Submit</translate></button>
     </form>
     <div v-else class="ui success message">
       <div class="header">Request submitted!</div>
-      <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>
+      <p><translate>We've received your request, you'll get some groove soon ;)</translate></p>
+      <button @click="reset" class="ui button"><translate>Submit another request</translate></button>
     </div>
     <div v-if="requests.length > 0">
       <div class="ui divider"></div>
-      <h3 class="ui header">{{ $gettext('Pending requests') }}</h3>
+      <h3 class="ui header"><translate>Pending requests</translate></h3>
       <div class="ui list">
         <div v-for="request in requests" class="item">
           <div class="content">
diff --git a/front/src/views/admin/Settings.vue b/front/src/views/admin/Settings.vue
index 0579ee14..016ef6f7 100644
--- a/front/src/views/admin/Settings.vue
+++ b/front/src/views/admin/Settings.vue
@@ -13,7 +13,7 @@
           </div>
           <div class="four wide column">
             <div class="ui sticky vertical secondary menu">
-              <div class="header item">{{ $gettext('Sections') }}</div>
+              <div class="header item"><translate>Sections</translate></div>
               <a :class="['menu', {active: group.id === current}, 'item']"
                 @click.prevent="scrollTo(group.id)"
                 :href="'#' + group.id"
diff --git a/front/src/views/admin/library/Base.vue b/front/src/views/admin/library/Base.vue
index dd7e6e34..a592b4f3 100644
--- a/front/src/views/admin/library/Base.vue
+++ b/front/src/views/admin/library/Base.vue
@@ -3,11 +3,11 @@
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'manage.library.files'}">{{ $gettext('Files') }}</router-link>
+        :to="{name: 'manage.library.files'}"><translate>Files</translate></router-link>
       <router-link
         class="ui item"
         :to="{name: 'manage.library.requests'}">
-          {{ $gettext('Import requests') }}
+          <translate>Import requests</translate>
           <div
             :class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
             :title="$gettext('Pending import requests')">
diff --git a/front/src/views/admin/library/FilesList.vue b/front/src/views/admin/library/FilesList.vue
index a5add715..aa397f94 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">{{ $gettext('Library files') }}</h2>
+      <h2 class="ui header"><translate>Library files</translate></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 bf097fa7..781f38ca 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="$gettext('Import requests')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $gettext('Import requests') }}</h2>
+      <h2 class="ui header"><translate>Import requests</translate></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 ae175435..d0426497 100644
--- a/front/src/views/admin/users/Base.vue
+++ b/front/src/views/admin/users/Base.vue
@@ -3,10 +3,10 @@
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'manage.users.users.list'}">{{ $gettext('Users') }}</router-link>
+        :to="{name: 'manage.users.users.list'}"><translate>Users</translate></router-link>
       <router-link
         class="ui item"
-        :to="{name: 'manage.users.invitations.list'}">{{ $gettext('Invitations') }}</router-link>
+        :to="{name: 'manage.users.invitations.list'}"><translate>Invitations</translate></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 7a7446f0..b8744561 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="$gettext('Invitations')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $gettext('Invitations') }}</h2>
+      <h2 class="ui header"><translate>Invitations</translate></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 60a40805..a97eb8d5 100644
--- a/front/src/views/admin/users/UsersDetail.vue
+++ b/front/src/views/admin/users/UsersDetail.vue
@@ -19,7 +19,7 @@
             <tbody>
               <tr>
                 <td>
-                  {{ $gettext('Name') }}
+                  <translate>Name</translate>
                 </td>
                 <td>
                   {{ object.name }}
@@ -27,7 +27,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Email address') }}
+                  <translate>Email address</translate>
                 </td>
                 <td>
                   {{ object.email }}
@@ -35,7 +35,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Sign-up') }}
+                  <translate>Sign-up</translate>
                 </td>
                 <td>
                   <human-date :date="object.date_joined"></human-date>
@@ -43,16 +43,16 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Last activity') }}
+                  <translate>Last activity</translate>
                 </td>
                 <td>
                   <human-date v-if="object.last_activity" :date="object.last_activity"></human-date>
-                  <template v-else>{{ $gettext('N/A') }}</template>
+                  <template v-else><translate>N/A</translate></template>
                 </td>
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Account active') }}
+                  <translate>Account active</translate>
                   <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>
@@ -66,7 +66,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Permissions') }}
+                  <translate>Permissions</translate>
                 </td>
                 <td>
                   <select
@@ -82,7 +82,7 @@
           </table>
         </div>
         <div class="ui hidden divider"></div>
-        <button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
+        <button @click="fetchData" class="ui basic button"><translate>Refresh</translate></button>
       </div>
     </template>
   </div>
diff --git a/front/src/views/admin/users/UsersList.vue b/front/src/views/admin/users/UsersList.vue
index 5e9625ee..edb9e272 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="$gettext('Users')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $gettext('Users') }}</h2>
+      <h2 class="ui header"><translate>Users</translate></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 f01aa521..1fb99961 100644
--- a/front/src/views/auth/EmailConfirm.vue
+++ b/front/src/views/auth/EmailConfirm.vue
@@ -2,29 +2,29 @@
   <div class="main pusher" v-title="$gettext('Confirm your email')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $gettext('Confirm your email') }}</h2>
+        <h2><translate>Confirm your email</translate></h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $gettext('Error while confirming your email') }}</div>
+            <div class="header"><translate>Error while confirming your email</translate></div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
           <div class="field">
-            <label>{{ $gettext('Confirmation code') }}</label>
+            <label><translate>Confirmation code</translate></label>
             <input type="text" required v-model="key" />
           </div>
           <router-link :to="{path: '/login'}">
-            {{ $gettext('Back to login') }}
+            <translate>Back to login</translate>
           </router-link>
           <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-            {{ $gettext('Confirm your email') }}</button>
+            <translate>Confirm your email</translate></button>
         </form>
         <div v-else class="ui positive message">
-          <div class="header">{{ $gettext('Email confirmed') }}</div>
-          <p>{{ $gettext('Your email address was confirmed, you can now use the service without limitations.') }}</p>
+          <div class="header"><translate>Email confirmed</translate></div>
+          <p><translate>Your email address was confirmed, you can now use the service without limitations.</translate></p>
           <router-link :to="{name: 'login'}">
-            {{ $gettext('Proceed to login') }}
+            <translate>Proceed to login</translate>
           </router-link>
         </div>
       </div>
diff --git a/front/src/views/auth/PasswordReset.vue b/front/src/views/auth/PasswordReset.vue
index d56e8580..4775e024 100644
--- a/front/src/views/auth/PasswordReset.vue
+++ b/front/src/views/auth/PasswordReset.vue
@@ -2,17 +2,17 @@
   <div class="main pusher" v-title="$gettext('Reset your password')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $gettext('Reset your password') }}</h2>
+        <h2><translate>Reset your password</translate></h2>
         <form class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $gettext('Error while asking for a password reset') }}</div>
+            <div class="header"><translate>Error while asking for a password reset</translate></div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
-          <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>
+          <p><translate>Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.</translate></p>
           <div class="field">
-            <label>{{ $gettext('Account\'s email') }}</label>
+            <label><translate>Account's email</translate></label>
             <input
               required
               ref="email"
@@ -22,10 +22,10 @@
               v-model="email">
           </div>
           <router-link :to="{path: '/login'}">
-            {{ $gettext('Back to login') }}
+            <translate>Back to login</translate>
           </router-link>
           <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-            {{ $gettext('Ask for a password reset') }}</button>
+            <translate>Ask for a password reset</translate></button>
         </form>
       </div>
     </div>
diff --git a/front/src/views/auth/PasswordResetConfirm.vue b/front/src/views/auth/PasswordResetConfirm.vue
index 05225f09..42f2e178 100644
--- a/front/src/views/auth/PasswordResetConfirm.vue
+++ b/front/src/views/auth/PasswordResetConfirm.vue
@@ -2,34 +2,34 @@
   <div class="main pusher" v-title="$gettext('Change your password')">
     <div class="ui vertical stripe segment">
       <div class="ui small text container">
-        <h2>{{ $gettext('Change your password') }}</h2>
+        <h2><translate>Change your password</translate></h2>
         <form v-if="!success" class="ui form" @submit.prevent="submit()">
           <div v-if="errors.length > 0" class="ui negative message">
-            <div class="header">{{ $gettext('Error while changing your password') }}</div>
+            <div class="header"><translate>Error while changing your password</translate></div>
             <ul class="list">
               <li v-for="error in errors">{{ error }}</li>
             </ul>
           </div>
           <template v-if="token && uid">
             <div class="field">
-              <label>{{ $gettext('New password') }}</label>
+              <label><translate>New password</translate></label>
               <password-input v-model="newPassword" />
             </div>
             <router-link :to="{path: '/login'}">
-              {{ $gettext('Back to login') }}
+              <translate>Back to login</translate>
             </router-link>
             <button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
-              {{ $gettext('Update your password') }}</button>
+              <translate>Update your password</translate></button>
           </template>
           <template v-else>
-            <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>
+            <p><translate>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.</translate></p>
           </template>
         </form>
         <div v-else class="ui positive message">
-          <div class="header">{{ $gettext('Password updated successfully') }}</div>
-          <p>{{ $gettext('Your password has been updated successfully.') }}</p>
+          <div class="header"><translate>Password updated successfully</translate></div>
+          <p><translate>Your password has been updated successfully.</translate></p>
           <router-link :to="{name: 'login'}">
-            {{ $gettext('Proceed to login') }}
+            <translate>Proceed to login</translate>
           </router-link>
         </div>
       </div>
diff --git a/front/src/views/federation/Base.vue b/front/src/views/federation/Base.vue
index 5b126950..9ab5dc2b 100644
--- a/front/src/views/federation/Base.vue
+++ b/front/src/views/federation/Base.vue
@@ -3,15 +3,15 @@
     <div class="ui secondary pointing menu">
       <router-link
         class="ui item"
-        :to="{name: 'federation.libraries.list'}">{{ $gettext('Libraries') }}</router-link>
+        :to="{name: 'federation.libraries.list'}"><translate>Libraries</translate></router-link>
       <router-link
         class="ui item"
-        :to="{name: 'federation.tracks.list'}">{{ $gettext('Tracks') }}</router-link>
+        :to="{name: 'federation.tracks.list'}"><translate>Tracks</translate></router-link>
         <div class="ui secondary right menu">
           <router-link
             class="ui item"
             :to="{name: 'federation.followers.list'}">
-            {{ $gettext('Followers') }}
+            <translate>Followers</translate>
             <div class="ui teal label" :title="$gettext('Pending requests')">{{ requestsCount }}</div>
           </router-link>
         </div>
diff --git a/front/src/views/federation/LibraryDetail.vue b/front/src/views/federation/LibraryDetail.vue
index fb4e2799..22399b0b 100644
--- a/front/src/views/federation/LibraryDetail.vue
+++ b/front/src/views/federation/LibraryDetail.vue
@@ -19,18 +19,18 @@
             <tbody>
               <tr>
                 <td >
-                  {{ $gettext('Follow status') }}
+                  <translate>Follow status</translate>
                   <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> {{ $gettext('Pending approval') }}
+                    <i class="loading icon"></i> <translate>Pending approval</translate>
                   </template>
                   <template v-else-if="object.follow.approved === true">
-                    <i class="check icon"></i> {{ $gettext('Following') }}
+                    <i class="check icon"></i> <translate>Following</translate>
                   </template>
                   <template v-else-if="object.follow.approved === false">
-                    <i class="x icon"></i> {{ $gettext('Not following') }}
+                    <i class="x icon"></i> <translate>Not following</translate>
                   </template>
                 </td>
                 <td>
@@ -38,7 +38,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Federation') }}
+                  <translate>Federation</translate>
                   <span :data-tooltip="$gettext('Use this flag to enable/disable federation with this library')"><i class="question circle icon"></i></span>
                 </td>
                 <td>
@@ -54,7 +54,7 @@
               </tr>
               <tr>
                 <td>
-                  {{ $gettext('Auto importing') }}
+                  <translate>Auto importing</translate>
                   <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>
@@ -82,7 +82,7 @@
               </tr>
               -->
               <tr>
-                <td>{{ $gettext('Library size') }}</td>
+                <td><translate>Library size</translate></td>
                 <td>
                   <template v-if="object.tracks_count">
                     <translate
@@ -93,13 +93,13 @@
                     </translate>
                   </template>
                   <template v-else>
-                    {{ $gettext('Unkwnown') }}
+                    <translate>Unkwnown</translate>
                   </template>
                 </td>
                 <td></td>
               </tr>
               <tr>
-                <td>{{ $gettext('Last fetched') }}</td>
+                <td><translate>Last fetched</translate></td>
                 <td>
                   <human-date v-if="object.fetched_date" :date="object.fetched_date"></human-date>
                   <template v-else>Never</template>
@@ -107,10 +107,10 @@
                     @click="scan"
                     v-if="!scanTrigerred"
                     :class="['ui', 'basic', {loading: isScanLoading}, 'button']">
-                    <i class="sync icon"></i> {{ $gettext('Trigger scan') }}
+                    <i class="sync icon"></i> <translate>Trigger scan</translate>
                   </button>
                   <button v-else class="ui success button">
-                    <i class="check icon"></i> {{ $gettext('Scan triggered!') }}
+                    <i class="check icon"></i> <translate>Scan triggered!</translate>
                   </button>
 
                 </td>
@@ -120,10 +120,10 @@
           </table>
         </div>
         <div class="ui hidden divider"></div>
-        <button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
+        <button @click="fetchData" class="ui basic button"><translate>Refresh</translate></button>
       </div>
       <div class="ui vertical stripe segment">
-        <h2>{{ $gettext('Tracks available in this library') }}</h2>
+        <h2><translate>Tracks available in this library</translate></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 98210006..28e2a999 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">{{ $gettext('Browsing followers') }}</h2>
+      <h2 class="ui header"><translate>Browsing followers</translate></h2>
       <p>
-        {{ $gettext('Be careful when accepting follow requests, as it means the follower will have access to your entire library.') }}
+        <translate>Be careful when accepting follow requests, as it means the follower will have access to your entire library.</translate>
       </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 d5ec9789..ad54c008 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">{{ $gettext('Browsing libraries') }}</h2>
+      <h2 class="ui header"><translate>Browsing libraries</translate></h2>
       <router-link
         class="ui basic green button"
         :to="{name: 'federation.libraries.scan'}">
         <i class="plus icon"></i>
-        {{ $gettext('Add a new library') }}
+        <translate>Add a new library</translate>
       </router-link>
       <div class="ui hidden divider"></div>
       <div :class="['ui', {'loading': isLoading}, 'form']">
         <div class="fields">
           <div class="field">
-            <label>{{ $gettext('Search') }}</label>
+            <label><translate>Search</translate></label>
             <input class="search" type="text" v-model="query" placeholder="Enter an library domain name..."/>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering') }}</label>
+            <label><translate>Ordering</translate></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>{{ $gettext('Ordering direction') }}</label>
+            <label><translate>Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+">{{ $gettext('Ascending') }}</option>
-              <option value="-">{{ $gettext('Descending') }}</option>
+              <option value="+"><translate>Ascending</translate></option>
+              <option value="-"><translate>Descending</translate></option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Results per page') }}</label>
+            <label><translate>Results per page</translate></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 566be694..5bfce3f8 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">{{ $gettext('Browsing federated tracks') }}</h2>
+      <h2 class="ui header"><translate>Browsing federated tracks</translate></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 0f3f3e6d..5e9a6efa 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">{{ $gettext('Loading timeline...') }}</div>
+        <div class="ui text loader"><translate>Loading timeline...</translate></div>
       </div>
       <div v-else class="ui text container">
-        <h1 class="ui header">{{ $gettext('Recent activity on this instance') }}</h1>
+        <h1 class="ui header"><translate>Recent activity on this instance</translate></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 b76f2f2c..5074ce87 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -20,22 +20,22 @@
           </div>
         </h2>
         <div class="ui hidden divider"></div>
-        <play-button class="orange" :tracks="tracks">{{ $gettext('Play all') }}</play-button>
+        <play-button class="orange" :tracks="tracks"><translate>Play all</translate></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">{{ $gettext('End edition') }}</template>
-          <template v-else>{{ $gettext('Edit...') }}</template>
+          <template v-if="edit"><translate>End edition</translate></template>
+          <template v-else><translate>Edit...</translate></template>
         </button>
         <dangerous-button v-if="playlist.user.id === $store.state.auth.profile.id" class="labeled icon" :action="deletePlaylist">
-          <i class="trash icon"></i> {{ $gettext('Delete') }}
+          <i class="trash icon"></i> <translate>Delete</translate>
           <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>
+          <p slot="modal-content"><translate>This will completely delete this playlist and cannot be undone.</translate></p>
+          <p slot="modal-confirm"><translate>Delete playlist</translate></p>
         </dangerous-button>
       </div>
     </div>
diff --git a/front/src/views/playlists/List.vue b/front/src/views/playlists/List.vue
index ca59612d..6e39e73f 100644
--- a/front/src/views/playlists/List.vue
+++ b/front/src/views/playlists/List.vue
@@ -1,21 +1,21 @@
 <template>
   <div v-title="$gettext('Playlists')">
     <div class="ui vertical stripe segment">
-      <h2 class="ui header">{{ $gettext('Browsing playlists') }}</h2>
+      <h2 class="ui header"><translate>Browsing playlists</translate></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">{{ $gettext('Manage your playlists') }}</button>
+            class="ui basic green button"><translate>Manage your playlists</translate></button>
           <div class="ui hidden divider"></div>
         </template>
         <div class="fields">
           <div class="field">
-            <label>{{ $gettext('Search') }}</label>
+            <label><translate>Search</translate></label>
             <input type="text" v-model="query" :placeholder="$gettext('Enter an playlist name...')"/>
           </div>
           <div class="field">
-            <label>{{ $gettext('Ordering') }}</label>
+            <label><translate>Ordering</translate></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>{{ $gettext('Ordering direction') }}</label>
+            <label><translate>Ordering direction</translate></label>
             <select class="ui dropdown" v-model="orderingDirection">
-              <option value="+">{{ $gettext('Ascending') }}</option>
-              <option value="-">{{ $gettext('Descending') }}</option>
+              <option value="+"><translate>Ascending</translate></option>
+              <option value="-"><translate>Descending</translate></option>
             </select>
           </div>
           <div class="field">
-            <label>{{ $gettext('Results per page') }}</label>
+            <label><translate>Results per page</translate></label>
             <select class="ui dropdown" v-model="paginateBy">
               <option :value="parseInt(12)">12</option>
               <option :value="parseInt(25)">25</option>
-- 
GitLab