From 60f38a3117dd15da7456b875f16c81b7f2a6ef47 Mon Sep 17 00:00:00 2001
From: jon r <jon@allmende.io>
Date: Thu, 3 Apr 2025 12:49:38 +0200
Subject: [PATCH] fix(front): translations

---
 front/src/LegacyLayout.vue                    | 105 ------------------
 front/src/components/SetInstanceModal.vue     |   4 +
 front/src/components/admin/SettingsGroup.vue  |   3 +-
 front/src/components/channels/AlbumSelect.vue |   2 +-
 front/src/components/library/EditForm.vue     |   6 +-
 front/src/components/library/FileUpload.vue   |   4 +-
 front/src/locales/en_US.json                  |  21 +++-
 front/src/ui/components/Sidebar.vue           |  13 ---
 front/src/ui/components/UploadGroupList.vue   |   4 +
 front/src/ui/components/UploadList.vue        |   4 +
 front/src/ui/components/UploadModal.vue       |   4 +-
 front/src/ui/modals/Search.vue                |   5 +-
 front/src/ui/modals/Upload.vue                |   6 +-
 front/src/ui/pages/upload.vue                 |   4 +
 front/src/ui/pages/upload/all.vue             |   4 +
 front/src/ui/pages/upload/index.vue           |   4 +
 front/src/views/Search.vue                    |   3 +-
 front/src/views/content/libraries/Quota.vue   |   2 +-
 front/src/views/content/upload/Home.vue       |   4 +
 front/src/views/playlists/Detail.vue          |   5 +-
 20 files changed, 69 insertions(+), 138 deletions(-)
 delete mode 100644 front/src/LegacyLayout.vue

diff --git a/front/src/LegacyLayout.vue b/front/src/LegacyLayout.vue
deleted file mode 100644
index 4bf2925cc..000000000
--- a/front/src/LegacyLayout.vue
+++ /dev/null
@@ -1,105 +0,0 @@
-<script setup lang="ts">
-import { useIntervalFn, useStyleTag, useToggle, useWindowSize } from '@vueuse/core'
-import { computed, defineAsyncComponent, nextTick, onMounted } from 'vue'
-
-import { useQueue } from '~/composables/audio/queue'
-import { useStore } from '~/store'
-import { useI18n } from 'vue-i18n'
-
-import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
-
-const { t } = useI18n()
-
-const ChannelUploadModal = defineAsyncComponent(() => import('~/components/channels/UploadModal.vue'))
-const PlaylistModal = defineAsyncComponent(() => import('~/components/playlists/PlaylistModal.vue'))
-const FilterModal = defineAsyncComponent(() => import('~/components/moderation/FilterModal.vue'))
-const ReportModal = defineAsyncComponent(() => import('~/components/moderation/ReportModal.vue'))
-const ServiceMessages = defineAsyncComponent(() => import('~/components/ServiceMessages.vue'))
-const ShortcutsModal = defineAsyncComponent(() => import('~/components/ShortcutsModal.vue'))
-const AudioPlayer = defineAsyncComponent(() => import('~/components/audio/Player.vue'))
-const Sidebar = defineAsyncComponent(() => import('~/components/Sidebar.vue'))
-const Queue = defineAsyncComponent(() => import('~/components/Queue.vue'))
-
-const store = useStore()
-
-// Tracks
-const { tracks } = useQueue()
-
-// Fake content
-onMounted(async () => {
-  await nextTick()
-  document.getElementById('fake-content')?.classList.add('loaded')
-})
-
-// Styles
-const customStylesheets = computed(() => {
-  return store.state.instance.frontSettings.additionalStylesheets ?? []
-})
-
-useStyleTag(computed(() => store.state.instance.settings.ui.custom_css.value))
-
-// Time ago
-useIntervalFn(() => {
-  // used to redraw ago dates every minute
-  store.commit('ui/computeLastDate')
-}, 1000 * 60)
-
-// Shortcuts
-const [showShortcutsModal, toggleShortcutsModal] = useToggle(false)
-onKeyboardShortcut('h', () => toggleShortcutsModal())
-
-const { width } = useWindowSize()
-</script>
-
-<template>
-  <div
-    :key="store.state.instance.instanceUrl"
-    :class="{
-      'has-bottom-player': tracks.length > 0,
-      'queue-focused': store.state.ui.queueFocused
-    }"
-  >
-    <!-- here, we display custom stylesheets, if any -->
-    <link
-      v-for="url in customStylesheets"
-      :key="url"
-      rel="stylesheet"
-      property="stylesheet"
-      :href="url"
-    >
-
-    <sidebar
-      :width="width"
-      @show:shortcuts-modal="toggleShortcutsModal"
-    />
-    <service-messages />
-    <transition name="queue">
-      <queue v-show="store.state.ui.queueFocused" />
-    </transition>
-
-    <router-view v-slot="{ Component }">
-      <template v-if="Component">
-        <keep-alive :max="1">
-          <Suspense>
-            <component :is="Component" />
-            <template #fallback>
-              <!-- TODO (wvffle): Add loader -->
-              {{ t('App.loading') }}
-            </template>
-          </Suspense>
-        </keep-alive>
-      </template>
-      <template v-else>
-        <!-- Display a proper 404 page or error message -->
-        <h1>404 - Page Not Found</h1>
-      </template>
-    </router-view>
-
-    <audio-player />
-    <playlist-modal v-if="store.state.auth.authenticated" />
-    <channel-upload-modal v-if="store.state.auth.authenticated" />
-    <filter-modal v-if="store.state.auth.authenticated" />
-    <report-modal />
-    <shortcuts-modal v-model:show="showShortcutsModal" />
-  </div>
-</template>
diff --git a/front/src/components/SetInstanceModal.vue b/front/src/components/SetInstanceModal.vue
index a35e0f51c..765d40173 100644
--- a/front/src/components/SetInstanceModal.vue
+++ b/front/src/components/SetInstanceModal.vue
@@ -7,6 +7,8 @@ import { ref, computed, watch, nextTick } from 'vue'
 import { useStore } from '~/store'
 import { useI18n } from 'vue-i18n'
 
+// TODO: Delete this file?
+
 const { t } = useI18n()
 
 interface Props {
@@ -65,6 +67,7 @@ const checkAndSwitch = async (url: string) => {
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <Modal
     v-model="show"
     :title="t('views.ChooseInstance.header.chooseInstance')"
@@ -181,4 +184,5 @@ const checkAndSwitch = async (url: string) => {
       </button>
     </div>
   </Modal>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
 </template>
diff --git a/front/src/components/admin/SettingsGroup.vue b/front/src/components/admin/SettingsGroup.vue
index 48110fab5..73d79f229 100644
--- a/front/src/components/admin/SettingsGroup.vue
+++ b/front/src/components/admin/SettingsGroup.vue
@@ -250,8 +250,7 @@ const save = async () => {
         red
       >
         <h4 class="header">
-          {{ group.label }}:
-          {{ t('components.admin.SettingsGroup.header.error') }}
+          {{ t('components.admin.SettingsGroup.header.error', {label: group.label}) }}
         </h4>
         <ul class="list">
           <li
diff --git a/front/src/components/channels/AlbumSelect.vue b/front/src/components/channels/AlbumSelect.vue
index 094061eb1..8a2342dee 100644
--- a/front/src/components/channels/AlbumSelect.vue
+++ b/front/src/components/channels/AlbumSelect.vue
@@ -71,7 +71,7 @@ watch(albums, (value) => {
       icon="bi-plus"
       :to="useModal('album').to"
     >
-      Add Album
+      {{ t('components.channels.AlbumSelect.add') }}
       <AlbumModal
         v-model="model.channel"
         @created="fetchAlbums"
diff --git a/front/src/components/library/EditForm.vue b/front/src/components/library/EditForm.vue
index 15b51a2c3..401f21af1 100644
--- a/front/src/components/library/EditForm.vue
+++ b/front/src/components/library/EditForm.vue
@@ -6,6 +6,7 @@ import { computed, reactive, ref } from 'vue'
 import { isEqual, clone } from 'lodash-es'
 import { useI18n } from 'vue-i18n'
 import { useStore } from '~/store'
+import { useRoute } from 'vue-router'
 
 import axios from 'axios'
 
@@ -36,6 +37,7 @@ const props = withDefaults(defineProps<Props>(), {
 const { t } = useI18n()
 const configs = useEditConfigs()
 const store = useStore()
+const route = useRoute()
 
 const config = computed(() => configs[props.objectType])
 const currentState = computed(() => config.value.fields.reduce((state: ReviewState, field) => {
@@ -157,13 +159,15 @@ const resetField = (fieldId: string) => {
       {{ t('components.library.EditForm.button.new') }}
     </Button>
 
+    <!-- TODO: Implement link back to all types of object -->
     <Link
+      v-if="route.path.includes('album')"
       solid
       secondary
       raised
       :to="{ name: 'library.albums.detail', params: { id: object.id } }"
     >
-      Back to Album
+      {{ t('components.library.EditForm.button.backToAlbum') }}
     </Link>
   </Alert>
   <Layout
diff --git a/front/src/components/library/FileUpload.vue b/front/src/components/library/FileUpload.vue
index 259e9aee4..278b77f4e 100644
--- a/front/src/components/library/FileUpload.vue
+++ b/front/src/components/library/FileUpload.vue
@@ -426,7 +426,7 @@ const isServerDisclosureOpen = ref(false)
           'yellow': files.length > uploadedFilesCount + erroredFilesCount
         }"
       >
-        {{ uploadedFilesCount + erroredFilesCount }} / {{ files.length }}
+        {{ t('components.library.FileUpload.table.upload.progressNum', {current: uploadedFilesCount + erroredFilesCount, total: files.length}) }}
       </Pill>
     </Layout>
     <Layout
@@ -435,7 +435,7 @@ const isServerDisclosureOpen = ref(false)
     >
       <label>{{ t('components.library.FileUpload.link.processing') }}</label>
       <Pill>
-        {{ processedFilesCount }} / {{ processableFiles }}
+        {{ t('components.library.FileUpload.table.upload.progressNum', {current: processedFilesCount, total: processableFiles}) }}
       </Pill>
     </Layout>
   </Layout>
diff --git a/front/src/locales/en_US.json b/front/src/locales/en_US.json
index f06a2852c..91afccea1 100644
--- a/front/src/locales/en_US.json
+++ b/front/src/locales/en_US.json
@@ -330,7 +330,7 @@
           "save": "Save"
         },
         "header": {
-          "error": "Error while saving settings.",
+          "error": "{label}: Error while saving settings.",
           "image": "Current image"
         },
         "message": {
@@ -1027,6 +1027,7 @@
         }
       },
       "AlbumSelect": {
+        "add": "Add Album",
         "label": {
           "album": "Album",
           "series": "Series"
@@ -1642,6 +1643,7 @@
       },
       "EditForm": {
         "button": {
+          "backToAlbum": "Back to Album",
           "cancel": "Cancel",
           "clear": "Clear",
           "new": "Submit another edit",
@@ -1716,6 +1718,7 @@
               "size": "Size",
               "status": "Status"
             },
+            "progressNum": "{current} / {total}",
             "progress": "{percent}%",
             "status": {
               "pending": "Pending",
@@ -3253,6 +3256,16 @@
       "newAppVersion": "A new version of the app is available."
     }
   },
+  "modals": {
+    "search": {
+      "tryAgain": "If the following link does not work, wait a few seconds and try again"
+    },
+    "upload": {
+      "library": "Host music you listen to",
+      "musicChannel": "Publish music you make",
+      "podcastChannel": "Publish podcasts you make"
+    }
+  },
   "views": {
     "ChooseInstance": {
       "button": {
@@ -4371,7 +4384,7 @@
             "currentUsage": "Current usage"
           },
           "label": {
-            "currentUsage": "{amount} used on {max} allowed",
+            "currentUsage": "{currentAmount} used on {max} allowed",
             "errored": "Errored files",
             "pending": "Pending files",
             "percentUsed": "{progress}%",
@@ -4583,7 +4596,9 @@
           "tracks": "Tracks"
         },
         "meta": {
-          "tracks": "Playlist containing {n} track, by {username} | Playlist containing {n} tracks, by {username}"
+          "attribution": "by",
+          "tracks": "Playlist containing {n} track, by {username} | Playlist containing {n} tracks, by {username}",
+          "updated": "updated"
         },
         "modal": {
           "delete": {
diff --git a/front/src/ui/components/Sidebar.vue b/front/src/ui/components/Sidebar.vue
index 6a11b990c..640164d0e 100644
--- a/front/src/ui/components/Sidebar.vue
+++ b/front/src/ui/components/Sidebar.vue
@@ -355,19 +355,6 @@ const moderationNotifications = computed(() =>
           {{ t('components.Sidebar.link.about') }}
         </Link>
         <Spacer shrink />
-        <Link
-          thin-font
-          to="/privacy"
-        >
-          Privacy
-        </Link>
-        <Spacer shrink />
-        <Link
-          thin-font
-          to="/legal"
-        >
-          Legal
-        </Link>
       </Layout>
     </Layout>
   </Layout>
diff --git a/front/src/ui/components/UploadGroupList.vue b/front/src/ui/components/UploadGroupList.vue
index 3782389b5..3dcdef5ec 100644
--- a/front/src/ui/components/UploadGroupList.vue
+++ b/front/src/ui/components/UploadGroupList.vue
@@ -6,6 +6,8 @@ import UploadList from '~/ui/components/UploadList.vue'
 import { UseTimeAgo } from '@vueuse/components'
 import { Icon } from '@iconify/vue'
 
+// TODO: Delete this file, please.
+
 defineProps<{ groups: UploadGroup[], isUploading?: boolean }>()
 
 const openUploadGroup = ref<UploadGroup>()
@@ -45,6 +47,7 @@ const getDescription = (group: UploadGroup) => {
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <div>
     <div
       v-for="group of groups"
@@ -161,6 +164,7 @@ const getDescription = (group: UploadGroup) => {
       </VerticalCollapse>
     </div>
   </div>
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped lang="scss">
diff --git a/front/src/ui/components/UploadList.vue b/front/src/ui/components/UploadList.vue
index 26380cda5..a13db2021 100644
--- a/front/src/ui/components/UploadList.vue
+++ b/front/src/ui/components/UploadList.vue
@@ -10,9 +10,12 @@ defineProps<{
   wide?: boolean
 }>()
 
+// TODO: Delete this file, please.
+
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <div class="file-list">
     <div
       v-for="track in uploads"
@@ -106,6 +109,7 @@ defineProps<{
       />
     </div>
   </div>
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped lang="scss">
diff --git a/front/src/ui/components/UploadModal.vue b/front/src/ui/components/UploadModal.vue
index b25ba5e3e..4335d049e 100644
--- a/front/src/ui/components/UploadModal.vue
+++ b/front/src/ui/components/UploadModal.vue
@@ -63,7 +63,7 @@ const continueInBackground = () => {
   return router.push('/upload/running')
 }
 
-// TODO (whole file): Translations
+// TODO (whole file): Delete this file, please.
 
 // Sorting
 const sortItems = reactive([
@@ -94,6 +94,7 @@ const isOpen = computed({
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <Modal
     v-model="isOpen"
     title="Upload..."
@@ -162,6 +163,7 @@ const isOpen = computed({
       </Button>
     </template>
   </Modal>
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped lang="scss">
diff --git a/front/src/ui/modals/Search.vue b/front/src/ui/modals/Search.vue
index 8920a3286..b684ce3a1 100644
--- a/front/src/ui/modals/Search.vue
+++ b/front/src/ui/modals/Search.vue
@@ -492,7 +492,7 @@ watch(queryDebounced, search, { immediate: true })
         <!-- If response has "url": "webfinger://node1@node1.funkwhale.test" -> Link to go directly to the federation page -->
 
         <span v-if="category.type === 'rss' && count(category) > 0">
-          <Alert>If the following link does not work, wait a few seconds and try again</Alert>
+          <Alert>{{ t('modals.search.tryAgain') }}</Alert>
           <Link
             v-for="channel in resultsPerCategory(category)"
             :key="channel.artist.fid"
@@ -504,7 +504,8 @@ watch(queryDebounced, search, { immediate: true })
         </span>
 
         <span v-else-if="category.type === 'federation'">
-          TODO: {{ resultsPerCategory(category) }}
+          <!-- TODO: Federation search: backend adapter + display, fix results_per_category query -->
+          <!-- {{ resultsPerCategory(category) }} -->
         </span>
 
         <EmptyState
diff --git a/front/src/ui/modals/Upload.vue b/front/src/ui/modals/Upload.vue
index efdb1f5fe..14658c6fe 100644
--- a/front/src/ui/modals/Upload.vue
+++ b/front/src/ui/modals/Upload.vue
@@ -110,7 +110,7 @@ const channelUpload = ref()
             :class="$style.icon"
           />
         </template>
-        {{ "Host music you listen to" /* TODO: Translate */ }}
+        {{ t('modals.upload.library') }}
       </Card>
       <Card
         small
@@ -125,7 +125,7 @@ const channelUpload = ref()
             :class="$style.icon"
           />
         </template>
-        {{ "Publish music you make" /* TODO: Translate */ }}
+        {{ t('modals.upload.musicChannel') }}
       </Card>
       <Card
         small
@@ -140,7 +140,7 @@ const channelUpload = ref()
             :class="$style.icon"
           />
         </template>
-        {{ "Publish podcasts you make" /* TODO: Translate */ }}
+        {{ t('modals.upload.podcastChannel') }}
       </Card>
     </Layout>
 
diff --git a/front/src/ui/pages/upload.vue b/front/src/ui/pages/upload.vue
index 57876765d..e3d08fe3e 100644
--- a/front/src/ui/pages/upload.vue
+++ b/front/src/ui/pages/upload.vue
@@ -4,6 +4,8 @@ import { useUploadsStore } from '~/ui/stores/upload'
 import { bytesToHumanSize } from '~/ui/composables/bytes'
 import UploadModal from '~/ui/components/UploadModal.vue'
 
+// TODO: Delete this file?
+
 const filesystemStats = reactive({
   total: 10737418240,
   used: 3e9
@@ -40,6 +42,7 @@ const tabs = computed(() => [
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <div class="flex items-center">
     <h1 class="mr-auto">
       Upload
@@ -82,6 +85,7 @@ const tabs = computed(() => [
   <RouterView />
 
   <UploadModal />
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped lang="scss">
diff --git a/front/src/ui/pages/upload/all.vue b/front/src/ui/pages/upload/all.vue
index ac47a4802..7cae73c72 100644
--- a/front/src/ui/pages/upload/all.vue
+++ b/front/src/ui/pages/upload/all.vue
@@ -5,6 +5,8 @@ import { bytesToHumanSize } from '~/ui/composables/bytes'
 import { useUploadsStore, type UploadGroupEntry } from '~/ui/stores/upload'
 import CoverArt from '~/ui/components/CoverArt.vue'
 
+// TODO: Delete this file?
+
 interface Recording {
   guid: string
   title: string
@@ -49,6 +51,7 @@ const columns = [
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <div
     v-if="allTracks.length === 0"
     class="flex flex-col items-center py-32"
@@ -80,6 +83,7 @@ const columns = [
       {{ intl.format(value) }}
     </template>
   </FwTable>
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped>
diff --git a/front/src/ui/pages/upload/index.vue b/front/src/ui/pages/upload/index.vue
index d9b371116..4361fd18d 100644
--- a/front/src/ui/pages/upload/index.vue
+++ b/front/src/ui/pages/upload/index.vue
@@ -5,6 +5,8 @@ import { ref } from 'vue'
 import axios from 'axios'
 import { useAsyncState } from '@vueuse/core'
 
+// TODO: Delete this file?
+
 interface Tab {
   label: string
   icon: string
@@ -49,6 +51,7 @@ const { state: items } = useAsyncState(
 </script>
 
 <template>
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <div class="upload">
     <p> Select a destination for your audio files: </p>
 
@@ -98,6 +101,7 @@ const { state: items } = useAsyncState(
       Open library
     </FwButton>
   </div>
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
 
 <style scoped lang="scss">
diff --git a/front/src/views/Search.vue b/front/src/views/Search.vue
index 394d6b375..b9ce02b42 100644
--- a/front/src/views/Search.vue
+++ b/front/src/views/Search.vue
@@ -22,6 +22,8 @@ import Button from '~/components/ui/Button.vue'
 import useErrorHandler from '~/composables/useErrorHandler'
 import useLogger from '~/composables/useLogger'
 
+// TODO: Depreciate & refactor essential functionality to new ui/modals/Search.vue
+
 type QueryType = 'artists' | 'albums' | 'tracks' | 'playlists' | 'tags' | 'radios' | 'podcasts' | 'series' | 'rss'
 
 const type = useRouteQuery<QueryType>('type', 'artists')
@@ -226,7 +228,6 @@ const radioConfig = computed(() => {
     class="main"
   >
     <section class="ui vertical stripe segment">
-      /front/src/components/audio/Search.vue
       <div
         v-if="id"
         class="ui small text container"
diff --git a/front/src/views/content/libraries/Quota.vue b/front/src/views/content/libraries/Quota.vue
index d1bbcdc32..91338cf16 100644
--- a/front/src/views/content/libraries/Quota.vue
+++ b/front/src/views/content/libraries/Quota.vue
@@ -84,7 +84,7 @@ const purgeErroredFiles = () => purge('errored')
         v-if="quotaStatus"
         class="label"
       >
-        {{ t('views.content.libraries.Quota.label.currentUsage', {max: humanSize(quotaStatus.max * 1000 * 1000), current: humanSize(quotaStatus.current * 1000 * 1000)}) }}
+        {{ t('views.content.libraries.Quota.label.currentUsage', {max: humanSize(quotaStatus.max * 1000 * 1000), currentAmount: humanSize(quotaStatus.current * 1000 * 1000)}) }}
       </div>
     </div>
     <div class="ui hidden divider" />
diff --git a/front/src/views/content/upload/Home.vue b/front/src/views/content/upload/Home.vue
index 3e406f327..767008bd5 100644
--- a/front/src/views/content/upload/Home.vue
+++ b/front/src/views/content/upload/Home.vue
@@ -21,6 +21,8 @@ import Upload from '~/ui/pages/upload.vue'
 
 import useErrorHandler from '~/composables/useErrorHandler'
 
+// TODO: Delete this file.
+
 const { t } = useI18n()
 
 const router = useRouter()
@@ -103,6 +105,7 @@ const openModal = (object_: Library | Channel) => {
 
 <template>
   <!-- TODO: Remove this module -->
+  <!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
   <section
     v-title="labels.title"
     class="ui vertical aligned stripe segment"
@@ -160,4 +163,5 @@ const openModal = (object_: Library | Channel) => {
     <upload />
   </fw-modal>
   <!-- <channel-upload-modal v-if="store.state.auth.authenticated" /> -->
+  <!-- eslint-enable @intlify/vue-i18n/no-raw-text -->
 </template>
diff --git a/front/src/views/playlists/Detail.vue b/front/src/views/playlists/Detail.vue
index 54cf4aae3..f3fc007fb 100644
--- a/front/src/views/playlists/Detail.vue
+++ b/front/src/views/playlists/Detail.vue
@@ -162,15 +162,14 @@ const deletePlaylist = async () => {
           flex
           gap-8
         >
-          <!-- TODO: Translations -->
-          by
+          {{ t('views.playlists.Detail.meta.attribution') }}
           <ActorLink
             :actor="playlist.actor"
             :avatar="false"
             :discrete="true"
           />
           <i class="bi bi-dot" />
-          updated
+          {{ t('views.playlists.Detail.meta.updated') }}
           <HumanDate
             :date="playlist.modification_date"
           />