Replace gettext with i18n

This commit is contained in:
wvffle 2022-09-08 14:32:45 +00:00 committed by Kasper Seweryn
parent 2694417bdf
commit ebea32faf9
197 changed files with 2623 additions and 2932 deletions

View File

@ -76,7 +76,6 @@
"@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.2.4",
"@vue/tsconfig": "0.1.3",
"easygettext": "2.17.0",
"eslint": "8.28.0",
"eslint-config-standard": "17.0.0",
"eslint-plugin-html": "7.1.0",

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { useStore } from '~/store'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { get } from 'lodash-es'
import { humanSize } from '~/utils/filters'
import { computed } from 'vue'
@ -11,9 +11,9 @@ import LogoText from '~/components/LogoText.vue'
const store = useStore()
const nodeinfo = computed(() => store.state.instance.nodeinfo)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/About/Title', 'About')
title: t('About')
}))
const podName = computed(() => get(nodeinfo.value, 'metadata.nodeName') ?? 'Funkwhale')
@ -67,12 +67,12 @@ const headerStyle = computed(() => {
<div class="column" />
</div>
<h2 class="header">
<translate translate-context="Content/About/Heading">
<translate >
A social platform to enjoy and share music
</translate>
</h2>
<p>
<translate translate-context="Content/About/Paragraph">
<translate >
Funkwhale is a community-driven project that lets you listen and share music and audio within a decentralized, open network.
</translate>
</p>
@ -88,19 +88,19 @@ const headerStyle = computed(() => {
class="signup-form content"
>
<h3 class="header">
<translate translate-context="*/Signup/Title">
<translate >
Sign up
</translate>
</h3>
<template v-if="openRegistrations">
<p>
<translate translate-context="Content/About/Paragraph">
<translate >
Sign up now to keep a track of your favorites, create playlists, discover new content and much more!
</translate>
</p>
<p v-if="defaultUploadQuota">
<translate
translate-context="Content/About/Paragraph"
:translate-params="{quota: defaultUploadQuota}"
>
Users on this pod also get %{ quota } of free storage to upload their own content!
@ -112,7 +112,7 @@ const headerStyle = computed(() => {
/>
</template>
<div v-else>
<p translate-context="Content/About/Paragraph">
<p >
Registrations are closed on this pod. You can signup on another pod using the link below.
</p>
@ -121,7 +121,7 @@ const headerStyle = computed(() => {
rel="noopener"
href="https://funkwhale.audio/#get-started"
>
<translate translate-context="Content/About/Link">Find another pod</translate>
<translate >Find another pod</translate>
&nbsp;<i class="external alternate icon" />
</a>
</div>
@ -131,12 +131,12 @@ const headerStyle = computed(() => {
class="signup-form content"
>
<h3 class="header">
<translate translate-context="*/Signup/Title">
<translate >
Sign up
</translate>
<div class="ui positive message">
<div class="header">
<translate translate-context="Content/About/Message">
<translate >
You're already signed in!
</translate>
</div>
@ -164,7 +164,7 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
About this pod
</translate>
</h3>
@ -175,7 +175,7 @@ const headerStyle = computed(() => {
{{ shortDescription }}
</div>
<p v-else>
<translate translate-context="Content/About/Paragraph">
<translate >
No description available.
</translate>
</p>
@ -188,7 +188,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.users"
translate-plural="active users"
>active user</translate>
@ -213,7 +213,7 @@ const headerStyle = computed(() => {
to="/about/pod"
class="ui fluid basic secondary button"
>
<translate translate-context="Content/About/Paragraph">
<translate >
Learn More
</translate>
</router-link>
@ -234,12 +234,12 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
Browse public content
</translate>
</h3>
<p>
<translate translate-context="Content/About/Paragraph">
<translate >
Listen to public albums and playlists shared on this pod.
</translate>
</p>
@ -255,11 +255,11 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
<translate translate-context="Content/About/Header">Find another pod</translate>
<translate >Find another pod</translate>
&nbsp;<i class="external alternate icon" />
</h3>
<p>
<translate translate-context="Content/About/Paragraph">Listen to public albums and playlists shared on this pod.</translate>
<translate >Listen to public albums and playlists shared on this pod.</translate>
</p>
</div>
</a>
@ -273,11 +273,11 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
<translate translate-context="Content/About/Header">Find an app</translate>
<translate >Find an app</translate>
&nbsp;<i class="external alternate icon" />
</h3>
<p>
<translate translate-context="Content/About/Paragraph">Use Funkwhale on other devices with our apps.</translate>
<translate >Use Funkwhale on other devices with our apps.</translate>
</p>
</div>
</a>
@ -287,7 +287,7 @@ const headerStyle = computed(() => {
to="/about/pod"
class="ui right floated basic secondary button"
>
<translate translate-context="Content/About/Paragraph">
<translate >
About this pod
</translate>
<i class="icon arrow right" />

View File

@ -1,6 +1,5 @@
<script setup lang="ts">
import { humanSize } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { useStore } from '~/store'
import { get } from 'lodash-es'
import { computed } from 'vue'
@ -9,6 +8,7 @@ import axios from 'axios'
import useMarkdown from '~/composables/useMarkdown'
import type { NodeInfo } from '~/store/instance'
import { useI18n } from 'vue-i18n'
const store = useStore()
const nodeinfo = computed(() => store.state.instance.nodeinfo)
@ -19,9 +19,9 @@ const fetchData = async () => {
}
fetchData()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/About/Title', 'About')
title: t('About')
}))
const podName = computed(() => get(nodeinfo.value, 'metadata.nodeName') || 'Funkwhale')
@ -99,7 +99,7 @@ const headerStyle = computed(() => {
to="/about/pod"
class="item"
>
<translate translate-context="Content/About/Header">
<translate >
About this pod
</translate>
</router-link>
@ -107,7 +107,7 @@ const headerStyle = computed(() => {
to="/about/pod#rules"
class="item"
>
<translate translate-context="Content/About/Header">
<translate >
Rules
</translate>
</router-link>
@ -115,7 +115,7 @@ const headerStyle = computed(() => {
to="/about/pod#terms"
class="item"
>
<translate translate-context="Content/About/Header">
<translate >
Terms and privacy policy
</translate>
</router-link>
@ -123,7 +123,7 @@ const headerStyle = computed(() => {
to="/about/pod#features"
class="item"
>
<translate translate-context="Content/About/Header">
<translate >
Features
</translate>
</router-link>
@ -132,7 +132,7 @@ const headerStyle = computed(() => {
to="/about/pod#statistics"
class="item"
>
<translate translate-context="Content/About/Header">
<translate >
Statistics
</translate>
</router-link>
@ -144,7 +144,7 @@ const headerStyle = computed(() => {
id="description about-this-pod"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
About this pod
</translate>
</h2>
@ -153,7 +153,7 @@ const headerStyle = computed(() => {
:html="longDescription"
/>
<p v-else>
<translate translate-context="Content/About/Paragraph">
<translate >
No description available.
</translate>
</p>
@ -162,7 +162,7 @@ const headerStyle = computed(() => {
id="rules"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
Rules
</translate>
</h3>
@ -171,7 +171,7 @@ const headerStyle = computed(() => {
:html="rules"
/>
<p v-else>
<translate translate-context="Content/About/Paragraph">
<translate >
No rules available.
</translate>
</p>
@ -180,7 +180,7 @@ const headerStyle = computed(() => {
id="terms"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
Terms and privacy policy
</translate>
</h3>
@ -189,7 +189,7 @@ const headerStyle = computed(() => {
:html="terms"
/>
<p v-else>
<translate translate-context="Content/About/Paragraph">
<translate >
No terms available.
</translate>
</p>
@ -198,7 +198,7 @@ const headerStyle = computed(() => {
id="features"
class="header"
>
<translate translate-context="Content/About/Header/Name">
<translate >
Features
</translate>
</h3>
@ -208,7 +208,7 @@ const headerStyle = computed(() => {
<tbody>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Funkwhale version
</translate>
</td>
@ -225,13 +225,13 @@ const headerStyle = computed(() => {
class="right aligned"
>
<span class="features-status ui text">
<translate translate-context="*/*/*">N/A</translate>
<translate >N/A</translate>
</span>
</td>
</tr>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Federation
</translate>
</td>
@ -241,7 +241,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
<translate translate-context="*/*/*/State of feature">Enabled</translate>
<translate >Enabled</translate>
</span>
</td>
<td
@ -250,13 +250,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
<translate translate-context="*/*/*/State of feature">Disabled</translate>
<translate >Disabled</translate>
</span>
</td>
</tr>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Allow-list
</translate>
</td>
@ -266,7 +266,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
<translate translate-context="*/*/*/State of feature">Enabled</translate>
<translate >Enabled</translate>
</span>
</td>
<td
@ -275,7 +275,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
<translate translate-context="*/*/*/State of feature">Disabled</translate>
<translate >Disabled</translate>
</span>
</td>
</tr>
@ -287,7 +287,7 @@ const headerStyle = computed(() => {
<tbody>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Anonymous access
</translate>
</td>
@ -297,7 +297,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
<translate translate-context="*/*/*/State of feature">Enabled</translate>
<translate >Enabled</translate>
</span>
</td>
<td
@ -306,13 +306,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
<translate translate-context="*/*/*/State of feature">Disabled</translate>
<translate >Disabled</translate>
</span>
</td>
</tr>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Registrations
</translate>
</td>
@ -322,7 +322,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
<translate translate-context="*/*/*/State of registrations">Open</translate>
<translate >Open</translate>
</span>
</td>
<td
@ -331,13 +331,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
<translate translate-context="*/*/*/State of registrations">Closed</translate>
<translate >Closed</translate>
</span>
</td>
</tr>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Upload quota
</translate>
</td>
@ -354,7 +354,7 @@ const headerStyle = computed(() => {
class="right aligned"
>
<span class="features-status ui text">
<translate translate-context="*/*/*">N/A</translate>
<translate >N/A</translate>
</span>
</td>
</tr>
@ -368,7 +368,7 @@ const headerStyle = computed(() => {
id="statistics"
class="header"
>
<translate translate-context="Content/About/Header">
<translate >
Statistics
</translate>
</h3>
@ -395,7 +395,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.artists.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.artists"
translate-plural="artists"
>artist</translate>
@ -409,7 +409,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.albums.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.albums"
translate-plural="albums"
>album</translate>
@ -423,7 +423,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.tracks.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.tracks"
translate-plural="tracks"
>track</translate>
@ -437,7 +437,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.users"
translate-plural="active users"
>active user</translate>
@ -451,7 +451,7 @@ const headerStyle = computed(() => {
<span class="ui big text"><strong>{{ stats.listenings.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
<translate
translate-context="Content/About/*"
:translate-n="stats.listenings"
translate-plural="listenings"
>listening</translate>
@ -465,7 +465,7 @@ const headerStyle = computed(() => {
id="contact"
class="ui header"
>
<translate translate-context="Content/About/Header">
<translate >
Contact
</translate>
</h3>
@ -474,7 +474,7 @@ const headerStyle = computed(() => {
:href="`mailto:${contactEmail}`"
>
<translate
translate-context="Content/About/Email"
:translate-params="{ email: contactEmail }"
>Send us an email: {{ contactEmail }}</translate>
</a>
@ -487,7 +487,7 @@ const headerStyle = computed(() => {
class="ui left floated basic secondary button"
>
<i class="icon arrow left" />
<translate translate-context="Content/About/Paragraph">
<translate >
Introduction
</translate>
</router-link>

View File

@ -9,12 +9,12 @@ import { humanSize } from '~/utils/filters'
import { useStore } from '~/store'
import { computed } from 'vue'
import { whenever } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/Home/Title', 'Home')
title: t('Home')
}))
const store = useStore()
@ -70,13 +70,12 @@ whenever(() => store.state.auth.authenticated, () => {
>
<div class="segment-content">
<h1 class="ui center aligned large header">
<span
v-translate="{podName: podName}"
translate-context="Content/Home/Header"
<translate
:translate-params="{podName: podName}"
tag="span"
>
Welcome to %{ podName }!
</span>
</translate>
<div
v-if="shortDescription"
class="sub header"
@ -90,7 +89,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="ten wide column">
<h2 class="header">
<translate translate-context="Content/Home/Header">
<translate >
About this Funkwhale pod
</translate>
</h2>
@ -101,7 +100,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="eight wide column">
<p v-if="!longDescription">
<translate translate-context="Content/Home/Paragraph">
<translate >
No description available.
</translate>
</p>
@ -126,7 +125,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link"
:to="{name: 'about'}"
>
<translate translate-context="Content/Home/Link">
<translate >
Learn more
</translate>
</router-link>
@ -143,7 +142,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link"
:to="{name: 'about', hash: '#rules'}"
>
<translate translate-context="Content/Home/Link">
<translate >
Server rules
</translate>
</router-link>
@ -155,13 +154,13 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="eight wide column">
<template v-if="stats">
<h3 class="sub header">
<translate translate-context="Content/Home/Header">
<translate >
Statistics
</translate>
</h3>
<p>
<i class="user icon" /><translate
translate-context="Content/Home/Stat"
:translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }"
:translate-n="stats.users"
translate-plural="%{ count } active users"
@ -182,7 +181,7 @@ whenever(() => store.state.auth.authenticated, () => {
</template>
<template v-if="contactEmail">
<h3 class="sub header">
<translate translate-context="Content/Home/Header/Name">
<translate >
Contact
</translate>
</h3>
@ -207,34 +206,28 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="four wide column">
<h3 class="header">
<translate translate-context="Footer/*/Title/Short">
<translate >
About Funkwhale
</translate>
</h3>
<p
v-translate
translate-context="Content/Home/Paragraph"
>
<translate tag="p">
This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network.
</p>
<p
v-translate
translate-context="Content/Home/Paragraph"
>
</translate>
<translate tag="p">
Funkwhale is free and developed by a friendly community of volunteers.
</p>
</translate>
<a
target="_blank"
rel="noopener"
href="https://funkwhale.audio"
>
<i class="external alternate icon" />
<translate translate-context="Content/Home/Link">Visit funkwhale.audio</translate>
<translate >Visit funkwhale.audio</translate>
</a>
</div>
<div class="four wide column">
<h3 class="header">
<translate translate-context="Head/Login/Title">
<translate >
Log In
</translate>
</h3>
@ -246,19 +239,19 @@ whenever(() => store.state.auth.authenticated, () => {
</div>
<div class="four wide column">
<h3 class="header">
<translate translate-context="*/Signup/Title">
<translate >
Sign up
</translate>
</h3>
<template v-if="openRegistrations">
<p>
<translate translate-context="Content/Home/Paragraph">
<translate >
Sign up now to keep track of your favorites, create playlists, discover new content and much more!
</translate>
</p>
<p v-if="defaultUploadQuota">
<translate
translate-context="Content/Home/Paragraph"
:translate-params="{quota: humanSize(defaultUploadQuota * 1000 * 1000)}"
>
Users on this pod also get %{ quota } of free storage to upload their own content!
@ -270,7 +263,7 @@ whenever(() => store.state.auth.authenticated, () => {
/>
</template>
<div v-else>
<p translate-context="Content/Home/Paragraph">
<p >
Registrations are closed on this pod. You can signup on another pod using the link below.
</p>
<a
@ -279,14 +272,14 @@ whenever(() => store.state.auth.authenticated, () => {
href="https://funkwhale.audio/#get-started"
>
<i class="external alternate icon" />
<translate translate-context="Content/Home/Link">Find another pod</translate>
<translate >Find another pod</translate>
</a>
</div>
</div>
<div class="four wide column">
<h3 class="header">
<translate translate-context="Content/Home/Header">
<translate >
Useful links
</translate>
</h3>
@ -299,12 +292,12 @@ whenever(() => store.state.auth.authenticated, () => {
class="header"
to="/library"
>
<translate translate-context="Content/Home/Link">
<translate >
Browse public content
</translate>
</router-link>
<div class="description">
<translate translate-context="Content/Home/Link">
<translate >
Listen to public albums and playlists shared on this pod
</translate>
</div>
@ -319,10 +312,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank"
rel="noopener"
>
<translate translate-context="Content/Home/Link">Mobile apps</translate>
<translate >Mobile apps</translate>
</a>
<div class="description">
<translate translate-context="Content/Home/Link">
<translate >
Use Funkwhale on other devices with our apps
</translate>
</div>
@ -337,10 +330,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank"
rel="noopener"
>
<translate translate-context="Content/Home/Link">User guides</translate>
<translate >User guides</translate>
</a>
<div class="description">
<translate translate-context="Content/Home/Link">
<translate >
Discover everything you need to know about Funkwhale and its features
</translate>
</div>
@ -359,12 +352,12 @@ whenever(() => store.state.auth.authenticated, () => {
:limit="10"
>
<template #title>
<translate translate-context="Content/Home/Title">
<translate >
Recently added albums
</translate>
</template>
<router-link to="/library">
<translate translate-context="Content/Home/Link">
<translate >
View more
</translate>
<div class="ui hidden divider" />
@ -372,7 +365,7 @@ whenever(() => store.state.auth.authenticated, () => {
</album-widget>
<div class="ui hidden section divider" />
<h3 class="ui header">
<translate translate-context="*/*/*">
<translate >
New channels
</translate>
</h3>

View File

@ -1,12 +1,12 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
const path = window.location.href
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/*/Title', 'Page Not Found')
title: t('Page Not Found')
}))
</script>
@ -20,13 +20,13 @@ const labels = computed(() => ({
<h1 class="ui huge header">
<i class="warning icon" />
<div class="content">
<translate translate-context="Content/*/Title">
<translate >
Page not found!
</translate>
</div>
</h1>
<p>
<translate translate-context="Content/*/Paragraph">
<translate >
Sorry, the page you asked for does not exist:
</translate>
</p>
@ -36,7 +36,7 @@ const labels = computed(() => ({
class="ui icon labeled right button"
to="/"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Go to home page
</translate>
<i class="right arrow icon" />

View File

@ -4,7 +4,6 @@ import type { QueueItemSource } from '~/types'
import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core'
import { nextTick, ref, computed, watchEffect, onMounted } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useGettext } from 'vue3-gettext'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'
@ -18,6 +17,11 @@ import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import PlayerControls from '~/components/audio/PlayerControls.vue'
import MilkDrop from '~/components/audio/visualizer/MilkDrop.vue'
import { whenever, watchDebounced, useCurrentElement, useScrollLock } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import useQueue from '~/composables/audio/useQueue'
import usePlayer from '~/composables/audio/usePlayer'
import VirtualList from '~/components/vui/list/VirtualList.vue'
import QueueItem from '~/components/QueueItem.vue'
@ -49,24 +53,24 @@ const { currentSound } = useTracks()
const queueModal = ref()
const { activate, deactivate } = useFocusTrap(queueModal, { allowOutsideClick: true, preventScroll: true })
const { $pgettext } = useGettext()
const { t } = useI18n()
const scrollLock = useScrollLock(document.body)
const store = useStore()
const labels = computed(() => ({
queue: $pgettext('*/*/*', 'Queue'),
populating: $pgettext('*/*/*', 'Fetching radio track'),
duration: $pgettext('*/*/*', 'Duration'),
addArtistContentFilter: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Hide content from this artist…'),
restart: $pgettext('*/*/*', 'Restart track'),
previous: $pgettext('*/*/*', 'Previous track'),
next: $pgettext('*/*/*', 'Next track'),
pause: $pgettext('*/*/*', 'Pause'),
play: $pgettext('*/*/*', 'Play'),
fullscreen: $pgettext('*/*/*', 'Fullscreen'),
exitFullscreen: $pgettext('*/*/*', 'Exit fullscreen'),
showCoverArt: $pgettext('*/*/*', 'Show cover art'),
showVisualizer: $pgettext('*/*/*', 'Show visualizer')
queue: t('Queue'),
populating: t('Fetching radio track'),
duration: t('Duration'),
addArtistContentFilter: t('Hide content from this artist…'),
restart: t('Restart track'),
previous: t('Previous track'),
next: t('Next track'),
pause: t('Pause'),
play: t('Play'),
fullscreen: t('Fullscreen'),
exitFullscreen: t('Exit fullscreen'),
showCoverArt: t('Show cover art'),
showVisualizer: t('Show visualizer')
}))
watchEffect(async () => {
@ -125,10 +129,11 @@ const queueItems = computed(() => queue.value.map((track, index) => ({
...track,
key: `${index}-${track.id}`,
labels: {
remove: $pgettext('*/*/*', 'Remove'),
selectTrack: $pgettext('*/*/*', 'Select track'),
favorite: $pgettext('*/*/*', 'Favorite track')
}
remove: t('Remove'),
selectTrack: t('Select track'),
favorite: t('Favorite track')
},
duration: time.durationFormatted(track.uploads[0]?.duration ?? 0) ?? ''
}) as QueueItemSource))
const reorderTracks = async (from: number, to: number) => {
@ -296,18 +301,18 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui small warning message"
>
<h3 class="header">
<translate translate-context="Sidebar/Player/Error message.Title">
<translate >
The track cannot be loaded
</translate>
</h3>
<p v-if="hasNext && isPlaying">
<translate translate-context="Sidebar/Player/Error message.Paragraph">
<translate >
The next track will play automatically in a few seconds
</translate>
<i class="loading spinner icon" />
</p>
<p>
<translate translate-context="Sidebar/Player/Error message.Paragraph">
<translate >
You may have a connectivity issue.
</translate>
</p>
@ -408,7 +413,7 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui right floated basic button"
@click="$store.commit('ui/queueFocused', null)"
>
<translate translate-context="*/Queue/*/Verb">
<translate >
Close
</translate>
</button>
@ -416,7 +421,7 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui right floated basic button danger"
@click="clear"
>
<translate translate-context="*/Queue/*/Verb">
<translate >
Clear
</translate>
</button>
@ -424,7 +429,6 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
<div class="sub header">
<div>
<translate
translate-context="Sidebar/Queue/Text"
:translate-params="{index: currentIndex + 1, length: queue.length}"
>
Track %{ index } of %{ length }
@ -473,12 +477,12 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
>
<div class="content">
<h3 class="header">
<i class="feed icon" /> <translate translate-context="Sidebar/Player/Title">
<i class="feed icon" /> <translate >
You have a radio playing
</translate>
</h3>
<p>
<translate translate-context="Sidebar/Player/Paragraph">
<translate >
New tracks will be appended here automatically.
</translate>
</p>
@ -486,7 +490,7 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui basic primary button"
@click="$store.dispatch('radios/stop')"
>
<translate translate-context="*/Player/Button.Label/Short, Verb">
<translate >
Stop radio
</translate>
</button>

View File

@ -2,7 +2,7 @@
import type { BackendError } from '~/types'
import { ref, computed, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'
@ -37,17 +37,17 @@ const type = ref(props.initialType)
const id = ref(props.initialId)
const errors = ref([] as string[])
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: type.value === 'rss'
? $pgettext('Head/Fetch/Title', 'Subscribe to a podcast RSS feed')
: $pgettext('Head/Fetch/Title', 'Subscribe to a podcast hosted on the Fediverse'),
? t('Subscribe to a podcast RSS feed')
: t('Subscribe to a podcast hosted on the Fediverse'),
fieldLabel: type.value === 'rss'
? $pgettext('*/*/*', 'RSS feed location')
: $pgettext('*/*/*', 'Fediverse object'),
? t('RSS feed location')
: t('Fediverse object'),
fieldPlaceholder: type.value === 'rss'
? $pgettext('Head/Fetch/Field.Placeholder', 'https://website.example.com/rss.xml')
: $pgettext('Head/Fetch/Field.Placeholder', '@username@example.com')
? t('https://website.example.com/rss.xml')
: t('@username@example.com')
}))
const obj = ref()
@ -117,7 +117,7 @@ const createFetch = async () => {
obj.value = response.data
if (response.data.status === 'errored' || response.data.status === 'skipped') {
errors.value.push($pgettext('Content/*/Error message.Title', 'This object cannot be retrieved'))
errors.value.push(t('This object cannot be retrieved'))
}
} catch (error) {
errors.value = (error as BackendError).backendErrors
@ -172,7 +172,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'rss'"
>
<i class="feed icon" />
<translate translate-context="Content/Search/Input.Label/Noun">
<translate >
RSS
</translate>
</button>
@ -182,7 +182,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'artists'"
>
<i class="globe icon" />
<translate translate-context="Content/Search/Input.Label/Noun">
<translate >
Fediverse
</translate>
</button>
@ -199,7 +199,7 @@ watch(() => props.initialId, () => {
class="ui negative message"
>
<h3 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while fetching object
</translate>
</h3>
@ -217,12 +217,12 @@ watch(() => props.initialId, () => {
{{ labels.fieldLabel }}
</label>
<p v-if="type === 'rss'">
<translate translate-context="Content/Fetch/Paragraph">
<translate >
Use this form to subscribe to an RSS feed from its URL.
</translate>
</p>
<p v-else-if="type === 'artists'">
<translate translate-context="Content/Fetch/Paragraph">
<translate >
Use this form to subscribe to a channel hosted somewhere else on the Fediverse.
</translate>
</p>
@ -241,7 +241,7 @@ watch(() => props.initialId, () => {
:class="['ui', 'primary', {loading: isLoading}, 'button']"
:disabled="isLoading || !id || id.length === 0"
>
<translate translate-context="Content/Search/Input.Label/Noun">
<translate >
Search
</translate>
</button>
@ -252,7 +252,7 @@ watch(() => props.initialId, () => {
class="ui warning message"
>
<p>
<translate translate-context="Content/*/Error message.Title">
<translate >
This kind of object isn't supported yet
</translate>
</p>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useVModel } from '@vueuse/core'
import { useStore } from '~/store'
import { uniq } from 'lodash-es'
@ -38,7 +38,7 @@ const suggestedInstances = computed(() => {
watch(() => store.state.instance.instanceUrl, () => store.dispatch('instance/fetchSettings'))
const { $pgettext } = useGettext()
const { t } = useI18n()
const isError = ref(false)
const isLoading = ref(false)
const checkAndSwitch = async (url: string) => {
@ -51,7 +51,7 @@ const checkAndSwitch = async (url: string) => {
show.value = false
store.commit('ui/addMessage', {
content: $pgettext('*/Instance/Message', 'You are now using the Funkwhale instance at %{ url }', { url: instanceUrl }),
content: t('You are now using the Funkwhale instance at %{ url }', { url: instanceUrl }),
date: new Date()
})
@ -71,7 +71,7 @@ const checkAndSwitch = async (url: string) => {
@update:show="isError = false"
>
<h3 class="header">
<translate translate-context="Popup/Instance/Title">
<translate >
Choose your instance
</translate>
</h3>
@ -82,18 +82,18 @@ const checkAndSwitch = async (url: string) => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Popup/Instance/Error message.Title">
<translate >
It is not possible to connect to the given URL
</translate>
</h4>
<ul class="list">
<li>
<translate translate-context="Popup/Instance/Error message.List item">
<translate >
The server might be down
</translate>
</li>
<li>
<translate translate-context="Popup/Instance/Error message.List item">
<translate >
The given address is not a Funkwhale server
</translate>
</li>
@ -107,7 +107,7 @@ const checkAndSwitch = async (url: string) => {
v-if="$store.state.instance.instanceUrl"
v-translate="{url: $store.state.instance.instanceUrl, hostname: $store.getters['instance/domain'] }"
class="description"
translate-context="Popup/Login/Paragraph"
>
You are currently connected to <a
href="%{ url }"
@ -115,12 +115,12 @@ const checkAndSwitch = async (url: string) => {
>%{ hostname }&nbsp;<i class="external icon" /></a>. If you continue, you will be disconnected from your current instance and all your local data will be deleted.
</p>
<p v-else>
<translate translate-context="Popup/Instance/Paragraph">
<translate >
To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices.
</translate>
</p>
<div class="field">
<label for="instance-picker"><translate translate-context="Popup/Instance/Input.Label/Noun">Instance URL</translate></label>
<label for="instance-picker"><translate >Instance URL</translate></label>
<div class="ui action input">
<input
id="instance-picker"
@ -132,7 +132,7 @@ const checkAndSwitch = async (url: string) => {
type="submit"
:class="['ui', 'icon', {loading: isLoading}, 'button']"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Submit
</translate>
</button>
@ -146,7 +146,7 @@ const checkAndSwitch = async (url: string) => {
>
<div class="field">
<h4>
<translate translate-context="Popup/Instance/List.Label">
<translate >
Suggested choices
</translate>
</h4>
@ -163,7 +163,7 @@ const checkAndSwitch = async (url: string) => {
</div>
<div class="actions">
<button class="ui basic cancel button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>

View File

@ -2,7 +2,7 @@
import SemanticModal from '~/components/semantic/Modal.vue'
import { useVModel } from '@vueuse/core'
import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface Events {
(e: 'update:show', show: boolean): void
@ -17,22 +17,22 @@ const props = defineProps<Props>()
const showRef = useVModel(props, 'show', emit)
const { $pgettext } = useGettext()
const { t } = useI18n()
const general = computed(() => [
{
title: $pgettext('Popup/Keyboard shortcuts/Title', 'General shortcuts'),
title: t('General shortcuts'),
shortcuts: [
{
key: 'h',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Show available keyboard shortcuts')
summary: t('Show available keyboard shortcuts')
},
{
key: 'shift + f',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Focus searchbar')
summary: t('Focus searchbar')
},
{
key: 'esc',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Unfocus searchbar')
summary: t('Unfocus searchbar')
}
]
}
@ -40,67 +40,67 @@ const general = computed(() => [
const player = computed(() => [
{
title: $pgettext('Popup/Keyboard shortcuts/Title', 'Audio player shortcuts'),
title: t('Audio player shortcuts'),
shortcuts: [
{
key: 'p',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Pause/play the current track')
summary: t('Pause/play the current track')
},
{
key: 'left',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Seek backwards 5s')
summary: t('Seek backwards 5s')
},
{
key: 'right',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Seek forwards 5s')
summary: t('Seek forwards 5s')
},
{
key: 'shift + left',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Seek backwards 30s')
summary: t('Seek backwards 30s')
},
{
key: 'shift + right',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Seek forwards 30s')
summary: t('Seek forwards 30s')
},
{
key: 'ctrl + shift + left',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Play previous track')
summary: t('Play previous track')
},
{
key: 'ctrl + shift + right',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Play next track')
summary: t('Play next track')
},
{
key: 'shift + up',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Increase volume')
summary: t('Increase volume')
},
{
key: 'shift + down',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Decrease volume')
summary: t('Decrease volume')
},
{
key: 'm',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Toggle mute')
summary: t('Toggle mute')
},
{
key: 'e',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Expand queue/player view')
summary: t('Expand queue/player view')
},
{
key: 'l',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Toggle queue looping')
summary: t('Toggle queue looping')
},
{
key: 's',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Shuffle queue')
summary: t('Shuffle queue')
},
{
key: 'q',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Clear queue')
summary: t('Clear queue')
},
{
key: 'f',
summary: $pgettext('Popup/Keyboard shortcuts/Table.Label/Verb', 'Toggle favorite')
summary: t('Toggle favorite')
}
]
}
@ -110,7 +110,7 @@ const player = computed(() => [
<template>
<semantic-modal v-model:show="showRef">
<header class="header">
<translate translate-context="*/*/*/Noun">
<translate >
Keyboard shortcuts
</translate>
</header>
@ -156,7 +156,7 @@ const player = computed(() => [
</section>
<footer class="actions">
<button class="ui basic cancel button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Close
</translate>
</button>

View File

@ -1,20 +1,22 @@
<script setup lang="ts">
import type { RouteRecordName } from 'vue-router'
import { computed, ref, watch, watchEffect, onMounted } from 'vue'
import { SUPPORTED_LOCALES } from '~/init/locale'
import { useCurrentElement } from '@vueuse/core'
import { setupDropdown } from '~/utils/fomantic'
import { useRoute } from 'vue-router'
import { useStore } from '~/store'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import UserModal from '~/components/common/UserModal.vue'
import Logo from '~/components/Logo.vue'
import SearchBar from '~/components/audio/SearchBar.vue'
import UserMenu from '~/components/common/UserMenu.vue'
import SemanticModal from '~/components/semantic/Modal.vue'
import Logo from '~/components/Logo.vue'
import useThemeList from '~/composables/useThemeList'
import useTheme from '~/composables/useTheme'
import { useRoute } from 'vue-router'
import { computed, ref, watch, watchEffect, onMounted } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useStore } from '~/store'
import { setupDropdown } from '~/utils/fomantic'
import { useCurrentElement } from '@vueuse/core'
interface Events {
(e: 'show:set-instance-modal'): void
@ -30,7 +32,7 @@ defineProps<Props>()
const store = useStore()
const { theme } = useTheme()
const themes = useThemeList()
const { $pgettext } = useGettext()
const { t } = useI18n()
const route = useRoute()
const isCollapsed = ref(true)
@ -40,15 +42,15 @@ const additionalNotifications = computed(() => store.getters['ui/additionalNotif
const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index' : 'index')
const labels = computed(() => ({
mainMenu: $pgettext('Sidebar/*/Hidden text', 'Main menu'),
selectTrack: $pgettext('Sidebar/Player/Hidden text', 'Play this track'),
pendingFollows: $pgettext('Sidebar/Notifications/Hidden text', 'Pending follow requests'),
pendingReviewEdits: $pgettext('Sidebar/Moderation/Hidden text', 'Pending review edits'),
pendingReviewReports: $pgettext('Sidebar/Moderation/Hidden text', 'Pending review reports'),
language: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Language'),
theme: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Theme'),
addContent: $pgettext('*/Library/*/Verb', 'Add content'),
administration: $pgettext('Sidebar/Admin/Title/Noun', 'Administration')
mainMenu: t('Main menu'),
selectTrack: t('Play this track'),
pendingFollows: t('Pending follow requests'),
pendingReviewEdits: t('Pending review edits'),
pendingReviewReports: t('Pending review reports'),
language: t('Language'),
theme: t('Theme'),
addContent: t('Add content'),
administration: t('Administration')
}))
type SidebarMenuTabs = 'explore' | 'myLibrary'
@ -104,12 +106,6 @@ const showUserModal = ref(false)
const showLanguageModal = ref(false)
const showThemeModal = ref(false)
const gettext = useGettext()
const languageSelection = ref(gettext.current)
watch(languageSelection, (v) => {
store.dispatch('ui/currentLanguage', v)
})
const el = useCurrentElement()
watchEffect(() => {
if (store.state.auth.authenticated) {
@ -153,7 +149,7 @@ onMounted(() => {
</div>
<div class="menu">
<h3 class="header">
<translate translate-context="Sidebar/Admin/Title/Noun">
<translate >
Administration
</translate>
</h3>
@ -170,7 +166,7 @@ onMounted(() => {
>
{{ $store.state.ui.notifications.pendingReviewEdits }}
</div>
<translate translate-context="*/*/*/Noun">
<translate >
Library
</translate>
</router-link>
@ -186,7 +182,7 @@ onMounted(() => {
>
{{ $store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests }}
</div>
<translate translate-context="*/Moderation/*">
<translate >
Moderation
</translate>
</router-link>
@ -195,7 +191,7 @@ onMounted(() => {
class="item"
:to="{name: 'manage.users.users.list'}"
>
<translate translate-context="*/*/*/Noun">
<translate >
Users
</translate>
</router-link>
@ -204,7 +200,7 @@ onMounted(() => {
class="item"
:to="{path: '/manage/settings'}"
>
<translate translate-context="*/*/*/Noun">
<translate >
Settings
</translate>
</router-link>
@ -300,12 +296,12 @@ onMounted(() => {
</div>
<div class="content">
<fieldset
v-for="(language, key) in $language.available"
v-for="(language, key) in SUPPORTED_LOCALES"
:key="key"
>
<input
:id="`${key}`"
v-model="languageSelection"
v-model="$i18n.locale"
type="radio"
name="language"
:value="key"
@ -366,7 +362,7 @@ onMounted(() => {
class="ui fluid tiny primary button"
:to="{name: 'login'}"
>
<translate translate-context="*/Login/*/Verb">
<translate >
Login
</translate>
</router-link>
@ -375,7 +371,7 @@ onMounted(() => {
class="ui fluid tiny button"
:to="{path: '/signup'}"
>
<translate translate-context="*/Signup/Link/Verb">
<translate >
Create an account
</translate>
</router-link>
@ -389,7 +385,7 @@ onMounted(() => {
id="navigation-label"
class="visually-hidden"
>
<translate translate-context="*/*/*">
<translate >
Main navigation
</translate>
</h1>
@ -411,7 +407,7 @@ onMounted(() => {
@click="expanded = 'explore'"
@focus="expanded = 'explore'"
>
<translate translate-context="*/*/*/Verb">
<translate >
Explore
</translate>
<i
@ -424,7 +420,7 @@ onMounted(() => {
class="item"
:to="{name: 'search'}"
>
<i class="search icon" /><translate translate-context="Sidebar/Navigation/List item.Link/Verb">
<i class="search icon" /><translate >
Search
</translate>
</router-link>
@ -433,7 +429,7 @@ onMounted(() => {
:to="{name: 'library.index'}"
active-class="_active"
>
<i class="music icon" /><translate translate-context="Sidebar/Navigation/List item.Link/Verb">
<i class="music icon" /><translate >
Browse
</translate>
</router-link>
@ -441,7 +437,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.podcasts.browse'}"
>
<i class="podcast icon" /><translate translate-context="*/*/*">
<i class="podcast icon" /><translate >
Podcasts
</translate>
</router-link>
@ -449,7 +445,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.albums.browse'}"
>
<i class="compact disc icon" /><translate translate-context="*/*/*">
<i class="compact disc icon" /><translate >
Albums
</translate>
</router-link>
@ -457,7 +453,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.artists.browse'}"
>
<i class="user icon" /><translate translate-context="*/*/*">
<i class="user icon" /><translate >
Artists
</translate>
</router-link>
@ -465,7 +461,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.playlists.browse'}"
>
<i class="list icon" /><translate translate-context="*/*/*">
<i class="list icon" /><translate >
Playlists
</translate>
</router-link>
@ -473,7 +469,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.radios.browse'}"
>
<i class="feed icon" /><translate translate-context="*/*/*">
<i class="feed icon" /><translate >
Radios
</translate>
</router-link>
@ -490,7 +486,7 @@ onMounted(() => {
@click="expanded = 'myLibrary'"
@focus="expanded = 'myLibrary'"
>
<translate translate-context="*/*/*/Noun">
<translate >
My Library
</translate>
<i
@ -503,7 +499,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.me'}"
>
<i class="music icon" /><translate translate-context="Sidebar/Navigation/List item.Link/Verb">
<i class="music icon" /><translate >
Browse
</translate>
</router-link>
@ -511,7 +507,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.albums.me'}"
>
<i class="compact disc icon" /><translate translate-context="*/*/*">
<i class="compact disc icon" /><translate >
Albums
</translate>
</router-link>
@ -519,7 +515,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.artists.me'}"
>
<i class="user icon" /><translate translate-context="*/*/*">
<i class="user icon" /><translate >
Artists
</translate>
</router-link>
@ -527,7 +523,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.playlists.me'}"
>
<i class="list icon" /><translate translate-context="*/*/*">
<i class="list icon" /><translate >
Playlists
</translate>
</router-link>
@ -535,7 +531,7 @@ onMounted(() => {
class="item"
:to="{name: 'library.radios.me'}"
>
<i class="feed icon" /><translate translate-context="*/*/*">
<i class="feed icon" /><translate >
Radios
</translate>
</router-link>
@ -543,7 +539,7 @@ onMounted(() => {
class="item"
:to="{name: 'favorites'}"
>
<i class="heart icon" /><translate translate-context="Sidebar/Favorites/List item.Link/Noun">
<i class="heart icon" /><translate >
Favorites
</translate>
</router-link>
@ -554,13 +550,13 @@ onMounted(() => {
class="header item"
:to="{name: 'subscriptions'}"
>
<translate translate-context="*/*/*">
<translate >
Channels
</translate>
</router-link>
<div class="item">
<h3 class="header">
<translate translate-context="Footer/About/List item.Link">
<translate >
More
</translate>
</h3>
@ -570,7 +566,7 @@ onMounted(() => {
to="/about"
active-class="router-link-exact-active active"
>
<i class="info icon" /><translate translate-context="Sidebar/*/List item.Link">
<i class="info icon" /><translate >
About this pod
</translate>
</router-link>

View File

@ -110,7 +110,7 @@ const save = async () => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while saving settings
</translate>
</h4>
@ -127,7 +127,7 @@ const save = async () => {
v-if="result"
class="ui positive message"
>
<translate translate-context="Content/Settings/Paragraph">
<translate >
Settings updated successfully.
</translate>
</div>
@ -229,7 +229,7 @@ const save = async () => {
<div v-if="values[setting.identifier]">
<div class="ui hidden divider" />
<h3 class="ui header">
<translate translate-context="Content/Settings/Title/Noun">
<translate >
Current image
</translate>
</h3>
@ -246,7 +246,7 @@ const save = async () => {
type="submit"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Save
</translate>
</button>

View File

@ -4,7 +4,7 @@ import type { Form } from '~/types'
import SignupForm from '~/components/auth/SignupForm.vue'
import { useVModel } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { arrayMove } from '~/utils'
interface Events {
@ -26,11 +26,11 @@ const value = useVModel(props, 'modelValue', emit, { deep: true })
const maxFields = ref(10)
const isPreviewing = ref(false)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
delete: $pgettext('*/*/*', 'Delete'),
up: $pgettext('*/*/*', 'Move up'),
down: $pgettext('*/*/*', 'Move down')
delete: t('Delete'),
up: t('Move up'),
down: t('Move down')
}))
if (!value.value?.fields) {
@ -45,7 +45,7 @@ if (!value.value?.fields) {
const addField = () => {
value.value.fields.push({
label: $pgettext('*/*/Form-builder', 'Additional field') + ' ' + (value.value.fields.length + 1),
label: t('Additional field') + ' ' + (value.value.fields.length + 1),
required: true,
input_type: 'short_text'
})
@ -69,7 +69,7 @@ const move = (idx: number, increment: number) => {
:class="[{active: !isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = false"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Edit form
</translate>
</button>
@ -77,7 +77,7 @@ const move = (idx: number, increment: number) => {
:class="[{active: isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = true"
>
<translate translate-context="*/Form/Menu.item">
<translate >
Preview form
</translate>
</button>
@ -99,10 +99,10 @@ const move = (idx: number, increment: number) => {
>
<div class="field">
<label for="help-text">
<translate translate-context="*/*/Label">Help text</translate>
<translate >Help text</translate>
</label>
<p>
<translate translate-context="*/*/Help">
<translate >
An optional text to be displayed at the start of the sign-up form.
</translate>
</p>
@ -115,10 +115,10 @@ const move = (idx: number, increment: number) => {
</div>
<div class="field">
<label>
<translate translate-context="*/*/Label">Additional fields</translate>
<translate >Additional fields</translate>
</label>
<p>
<translate translate-context="*/*/Help">
<translate >
Additional form fields to be displayed in the form. Only shown if manual sign-up validation is enabled.
</translate>
</p>
@ -126,21 +126,21 @@ const move = (idx: number, increment: number) => {
<thead>
<tr>
<th>
<translate translate-context="*/*/Form-builder,Help">
<translate >
Field label
</translate>
</th>
<th>
<translate translate-context="*/*/Form-builder,Help">
<translate >
Field type
</translate>
</th>
<th>
<translate translate-context="*/*/Form-builder,Help">
<translate >
Required
</translate>
</th>
<th><span class="visually-hidden"><translate translate-context="*/*/Form-builder,Help">Actions</translate></span></th>
<th><span class="visually-hidden"><translate >Actions</translate></span></th>
</tr>
</thead>
<tbody>
@ -158,12 +158,12 @@ const move = (idx: number, increment: number) => {
<td>
<select v-model="field.input_type">
<option value="short_text">
<translate translate-context="*/*/Form-builder">
<translate >
Short text
</translate>
</option>
<option value="long_text">
<translate translate-context="*/*/Form-builder">
<translate >
Long text
</translate>
</option>
@ -172,12 +172,12 @@ const move = (idx: number, increment: number) => {
<td>
<select v-model="field.required">
<option :value="true">
<translate translate-context="*/*/*">
<translate >
Yes
</translate>
</option>
<option :value="false">
<translate translate-context="*/*/*">
<translate >
No
</translate>
</option>
@ -214,7 +214,7 @@ const move = (idx: number, increment: number) => {
class="ui basic button"
@click.stop.prevent="addField"
>
<translate translate-context="*/*/Form-builder">
<translate >
Add a new field
</translate>
</button>

View File

@ -2,7 +2,7 @@
import type { Channel } from '~/types'
import { momentFormat } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import { computed } from 'vue'
@ -30,10 +30,10 @@ const urlId = computed(() => props.object.actor?.is_local
: props.object.uuid
)
const { $pgettext } = useGettext()
const { t } = useI18n()
const updatedTitle = computed(() => {
const date = momentFormat(new Date(props.object.artist?.modification_date ?? '1970-01-01'))
return $pgettext('*/*/*', 'Updated on %{ date }', { date })
return t('Updated on %{ date }', { date })
})
// TODO (wvffle): Use time ago
@ -67,7 +67,7 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
<translate
v-if="object.artist?.content_category === 'podcast'"
class="meta ellipsis"
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } episodes"
:translate-n="object.artist.tracks_count"
:translate-params="{count: object.artist.tracks_count}"
@ -76,7 +76,7 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
</translate>
<translate
v-else
translate-context="*/*/*"
:translate-params="{count: object.artist?.tracks_count}"
:translate-n="object.artist?.tracks_count"
translate-plural="%{ count } tracks"

View File

@ -103,7 +103,7 @@ watch(page, fetchData, { immediate: true })
@refresh="fetchData()"
>
<p>
<translate translate-context="Content/Channels/*">
<translate >
You may need to subscribe to this channel to see its content.
</translate>
</p>

View File

@ -3,7 +3,7 @@ import type { ContentCategory, Channel, BackendError } from '~/types'
import { slugify } from 'transliteration'
import { reactive, computed, ref, watchEffect, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
import AttachmentInput from '~/components/common/AttachmentInput.vue'
@ -29,7 +29,7 @@ const props = withDefaults(defineProps<Props>(), {
step: 1
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const newValues = reactive({
name: props.object?.artist?.name ?? '',
@ -45,13 +45,13 @@ const creating = computed(() => props.object === null)
const categoryChoices = computed(() => [
{
value: 'podcast',
label: $pgettext('*/*/*', 'Podcasts'),
helpText: $pgettext('Content/Channels/Help', 'Host your episodes and keep your community updated.')
label: t('Podcasts'),
helpText: t('Host your episodes and keep your community updated.')
},
{
value: 'music',
label: $pgettext('*/*/*', 'Artist discography'),
helpText: $pgettext('Content/Channels/Help', 'Publish music you make as a nice discography of albums and singles.')
label: t('Artist discography'),
helpText: t('Publish music you make as a nice discography of albums and singles.')
}
])
@ -81,8 +81,8 @@ const itunesSubcategories = computed(() => {
})
const labels = computed(() => ({
namePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'Awesome channel name'),
usernamePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'awesomechannelname')
namePlaceholder: t('Awesome channel name'),
usernamePlaceholder: t('awesomechannelname')
}))
const submittable = computed(() => !!(
@ -165,7 +165,7 @@ defineExpose({
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while saving channel
</translate>
</h4>
@ -184,7 +184,7 @@ defineExpose({
class="ui grouped channel-type required field"
>
<legend>
<translate translate-context="Content/Channel/Paragraph">
<translate >
What will this channel be used for?
</translate>
</legend>
@ -214,7 +214,7 @@ defineExpose({
<template v-if="!creating || step === 2">
<div class="ui required field">
<label for="channel-name">
<translate translate-context="Content/Channel/*">Name</translate>
<translate >Name</translate>
</label>
<input
v-model="newValues.name"
@ -225,7 +225,7 @@ defineExpose({
</div>
<div class="ui required field">
<label for="channel-username">
<translate translate-context="Content/Channel/*">Fediverse handle</translate>
<translate >Fediverse handle</translate>
</label>
<div class="ui left labeled input">
<div class="ui basic label">
@ -242,7 +242,7 @@ defineExpose({
<template v-if="creating">
<div class="ui small hidden divider" />
<p>
<translate translate-context="Content/Channels/Paragraph">
<translate >
Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.
</translate>
</p>
@ -254,7 +254,7 @@ defineExpose({
:image-class="newValues.content_category === 'podcast' ? '' : 'circular'"
@delete="newValues.cover = null"
>
<translate translate-context="Content/Channel/*">
<translate >
Channel Picture
</translate>
</attachment-input>
@ -264,7 +264,7 @@ defineExpose({
<div class="ten wide column">
<div class="ui field">
<label for="channel-tags">
<translate translate-context="*/*/*">Tags</translate>
<translate >Tags</translate>
</label>
<tags-selector
id="channel-tags"
@ -279,7 +279,7 @@ defineExpose({
>
<div class="ui required field">
<label for="channel-language">
<translate translate-context="*/*/*">Language</translate>
<translate >Language</translate>
</label>
<select
id="channel-language"
@ -302,7 +302,7 @@ defineExpose({
<div class="ui small hidden divider" />
<div class="ui field">
<label for="channel-name">
<translate translate-context="*/*/*">Description</translate>
<translate >Description</translate>
</label>
<content-form v-model="newValues.description" />
</div>
@ -312,7 +312,7 @@ defineExpose({
>
<div class="ui required field">
<label for="channel-itunes-category">
<translate translate-context="*/*/*">Category</translate>
<translate >Category</translate>
</label>
<select
id="itunes-category"
@ -332,7 +332,7 @@ defineExpose({
</div>
<div class="ui field">
<label for="channel-itunes-category">
<translate translate-context="*/*/*">Subcategory</translate>
<translate >Subcategory</translate>
</label>
<select
id="itunes-category"
@ -357,7 +357,7 @@ defineExpose({
>
<div class="ui field">
<label for="channel-itunes-email">
<translate translate-context="*/*/*">Owner e-mail address</translate>
<translate >Owner e-mail address</translate>
</label>
<input
id="channel-itunes-email"
@ -368,7 +368,7 @@ defineExpose({
</div>
<div class="ui field">
<label for="channel-itunes-name">
<translate translate-context="*/*/*">Owner name</translate>
<translate >Owner name</translate>
</label>
<input
id="channel-itunes-name"
@ -379,7 +379,7 @@ defineExpose({
</div>
</div>
<p>
<translate translate-context="*/*/*">
<translate >
Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.
</translate>
</p>
@ -390,7 +390,7 @@ defineExpose({
class="ui active inverted dimmer"
>
<div class="ui text loader">
<translate translate-context="*/*/*">
<translate >
Loading
</translate>
</div>

View File

@ -56,7 +56,7 @@ const cover = computed(() => props.serie?.cover ?? null)
</strong>
<div class="description">
<translate
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } episodes"
:translate-n="serie.tracks_count"
:translate-params="{count: serie.tracks_count}"

View File

@ -84,7 +84,7 @@ fetchData()
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>
@ -95,7 +95,7 @@ fetchData()
@refresh="fetchData()"
>
<p>
<translate translate-context="Content/Channels/*">
<translate >
You may need to subscribe to this channel to see its contents.
</translate>
</p>

View File

@ -77,7 +77,7 @@ fetchData()
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>

View File

@ -52,11 +52,11 @@ const { copy, copied } = useClipboard({ source: textarea })
>
<p>
<strong>
<translate translate-context="Content/Embed/Message">Sharing will not work because this pod doesn't allow anonymous users to access content.</translate>
<translate >Sharing will not work because this pod doesn't allow anonymous users to access content.</translate>
</strong>
</p>
<p>
<translate translate-context="Content/Embed/Message">
<translate >
Please contact your admins and ask them to update the corresponding setting.
</translate>
</p>
@ -65,9 +65,9 @@ const { copy, copied } = useClipboard({ source: textarea })
<div class="two fields">
<div class="field">
<div class="field">
<label for="embed-width"><translate translate-context="Popup/Embed/Input.Label">Widget width</translate></label>
<label for="embed-width"><translate >Widget width</translate></label>
<p>
<translate translate-context="Popup/Embed/Paragraph">
<translate >
Leave empty for a responsive widget
</translate>
</p>
@ -82,7 +82,7 @@ const { copy, copied } = useClipboard({ source: textarea })
<template v-if="type != 'track'">
<br>
<div class="field">
<label for="embed-height"><translate translate-context="Popup/Embed/Input.Label">Widget height</translate></label>
<label for="embed-height"><translate >Widget height</translate></label>
<input
id="embed-height"
v-model="height"
@ -99,13 +99,13 @@ const { copy, copied } = useClipboard({ source: textarea })
class="ui right accent labeled icon floated button"
@click="copy()"
>
<i class="copy icon" /><translate translate-context="*/*/Button.Label/Short, Verb">
<i class="copy icon" /><translate >
Copy
</translate>
</button>
<label for="embed-width"><translate translate-context="Popup/Embed/Input.Label/Noun">Embed code</translate></label>
<label for="embed-width"><translate >Embed code</translate></label>
<p>
<translate translate-context="Popup/Embed/Paragraph">
<translate >
Copy/paste this code in your website HTML
</translate>
</p>
@ -120,7 +120,7 @@ const { copy, copied } = useClipboard({ source: textarea })
v-if="copied"
class="message"
>
<translate translate-context="Content/*/Paragraph">
<translate >
Text copied to clipboard!
</translate>
</p>
@ -134,7 +134,7 @@ const { copy, copied } = useClipboard({ source: textarea })
:href="iframeSrc"
target="_blank"
>
<translate translate-context="Popup/Embed/Title/Noun">Preview</translate>
<translate >Preview</translate>
</a>
</h3>
<iframe

View File

@ -40,19 +40,19 @@ const toggle = () => {
<i class="heart icon" />
<translate
v-if="isApproved"
translate-context="Content/Library/Card.Button.Label/Verb"
>
Unfollow
</translate>
<translate
v-else-if="isPending"
translate-context="Content/Library/Card.Button.Label/Verb"
>
Cancel follow request
</translate>
<translate
v-else
translate-context="Content/Library/Card.Button.Label/Verb"
>
Follow
</translate>

View File

@ -3,7 +3,7 @@ import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
import { ref, computed, onMounted } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport'
import { useCurrentElement } from '@vueuse/core'
@ -63,33 +63,33 @@ const {
const { report, getReportableObjects } = useReport()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
playNow: $pgettext('*/Queue/Dropdown/Button/Title', 'Play now'),
addToQueue: $pgettext('*/Queue/Dropdown/Button/Title', 'Add to current queue'),
playNext: $pgettext('*/Queue/Dropdown/Button/Title', 'Play next'),
startRadio: $pgettext('*/Queue/Dropdown/Button/Title', 'Play similar songs'),
report: $pgettext('*/Moderation/*/Button/Label,Verb', 'Report…'),
addToPlaylist: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Add to playlist…'),
hideArtist: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'Hide content from this artist'),
playNow: t('Play now'),
addToQueue: t('Add to current queue'),
playNext: t('Play next'),
startRadio: t('Play similar songs'),
report: t('Report…'),
addToPlaylist: t('Add to playlist…'),
hideArtist: t('Hide content from this artist'),
replacePlay: props.track
? $pgettext('*/Queue/Dropdown/Button/Title', 'Play track')
? t('Play track')
: props.album
? $pgettext('*/Queue/Dropdown/Button/Title', 'Play album')
? t('Play album')
: props.artist
? $pgettext('*/Queue/Dropdown/Button/Title', 'Play artist')
? t('Play artist')
: props.playlist
? $pgettext('*/Queue/Dropdown/Button/Title', 'Play playlist')
: $pgettext('*/Queue/Dropdown/Button/Title', 'Play tracks')
? t('Play playlist')
: t('Play tracks')
}))
const title = computed(() => {
if (playable.value) {
return $pgettext('*/*/Button.Label/Noun', 'More…')
return t('More…')
}
if (props.track) {
return $pgettext('*/Queue/Button/Title', 'This track is not available in any library you have access to')
return t('This track is not available in any library you have access to')
}
return ''
@ -138,7 +138,7 @@ const openMenu = () => {
v-else
:class="[playIconClass, 'icon']"
/>
<template v-if="!discrete && !iconOnly">&nbsp;<slot><translate translate-context="*/Queue/Button.Label/Short, Verb">Play</translate></slot></template>
<template v-if="!discrete && !iconOnly">&nbsp;<slot><translate >Play</translate></slot></template>
</button>
<button
v-if="!discrete && !iconOnly"
@ -156,7 +156,7 @@ const openMenu = () => {
:title="labels.addToQueue"
@click.stop.prevent="enqueue"
>
<i class="plus icon" /><translate translate-context="*/Queue/Dropdown/Button/Label/Short">Add to queue</translate>
<i class="plus icon" /><translate >Add to queue</translate>
</button>
<button
class="item basic"
@ -181,7 +181,7 @@ const openMenu = () => {
:title="labels.startRadio"
@click.stop.prevent="$store.dispatch('radios/start', {type: 'similar', objectId: track?.id})"
>
<i class="feed icon" /><translate translate-context="*/Queue/Button.Label/Short, Verb">Play radio</translate>
<i class="feed icon" /><translate >Play radio</translate>
</button>
<button
v-if="track"
@ -190,7 +190,7 @@ const openMenu = () => {
@click.stop="$store.commit('playlists/chooseTrack', track)"
>
<i class="list icon" />
<translate translate-context="Sidebar/Player/Icon.Tooltip/Verb">Add to playlist</translate>
<translate >Add to playlist</translate>
</button>
<button
v-if="track && $route.name !== 'library.tracks.detail'"
@ -200,11 +200,11 @@ const openMenu = () => {
<i class="info icon" />
<translate
v-if="track.artist?.content_category === 'podcast'"
translate-context="*/Queue/Dropdown/Button/Label/Short"
>Episode details</translate>
<translate
v-else
translate-context="*/Queue/Dropdown/Button/Label/Short"
>Track details</translate>
</button>
<div class="divider" />

View File

@ -3,17 +3,17 @@ import { usePlayer } from '~/composables/audio/player'
import { useQueue } from '~/composables/audio/queue'
import { useMouse, useWindowSize } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { computed, ref } from 'vue'
import { useStore } from '~/store'
import { useI18n } from 'vue-i18n'
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
import time from '~/utils/time'
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import VolumeControl from './VolumeControl.vue'
import PlayerControls from './PlayerControls.vue'
import VolumeControl from './VolumeControl.vue'
const {
LoopingMode,
@ -44,7 +44,7 @@ const {
} = useQueue()
const store = useStore()
const { $pgettext } = useGettext()
const { t } = useI18n()
const toggleMobilePlayer = () => {
store.commit('ui/queueFocused', ['queue', 'player'].includes(store.state.ui.queueFocused as string) ? null : 'player')
@ -72,17 +72,17 @@ onKeyboardShortcut(['ctrl', 'shift', 'left'], playPrevious, true)
onKeyboardShortcut(['ctrl', 'shift', 'right'], playNext, true)
const labels = computed(() => ({
audioPlayer: $pgettext('Sidebar/Player/Hidden text', 'Media player'),
previous: $pgettext('Sidebar/Player/Icon.Tooltip', 'Previous track'),
play: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Play'),
pause: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Pause'),
next: $pgettext('Sidebar/Player/Icon.Tooltip', 'Next track'),
unmute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Unmute'),
mute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Mute'),
expandQueue: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Expand queue'),
shuffle: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Shuffle your queue'),
clear: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Clear your queue'),
addArtistContentFilter: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Hide content from this artist…')
audioPlayer: t('Media player'),
previous: t('Previous track'),
play: t('Play'),
pause: t('Pause'),
next: t('Next track'),
unmute: t('Unmute'),
mute: t('Mute'),
expandQueue: t('Expand queue'),
shuffle: t('Shuffle your queue'),
clear: t('Clear your queue'),
addArtistContentFilter: t('Hide content from this artist…')
}))
const switchTab = () => {
@ -103,10 +103,10 @@ initializeFirstTrack()
const loopingTitle = computed(() => {
const mode = looping.value
return mode === LoopingMode.None
? $pgettext('Sidebar/Player/Icon.Tooltip', 'Looping disabled. Click to switch to single-track looping.')
? t('Looping disabled. Click to switch to single-track looping.')
: mode === LoopingMode.LoopTrack
? $pgettext('Sidebar/Player/Icon.Tooltip', 'Looping on a single track. Click to switch to whole queue looping.')
: $pgettext('Sidebar/Player/Icon.Tooltip', 'Looping on whole queue. Click to disable looping.')
? t('Looping on a single track. Click to switch to whole queue looping.')
: t('Looping on whole queue. Click to disable looping.')
})
const hideArtist = () => {
@ -133,7 +133,7 @@ const hideArtist = () => {
id="player-label"
class="visually-hidden"
>
<translate translate-context="*/*/*">
<translate >
Audio player and controls
</translate>
</h1>
@ -302,7 +302,6 @@ const hideArtist = () => {
>
<i class="stream icon" />
<translate
translate-context="Sidebar/Queue/Text"
:translate-params="{index: currentIndex + 1, length: queue.length}"
>
%{ index } of %{ length }
@ -314,7 +313,6 @@ const hideArtist = () => {
>
<i class="stream icon" />
<translate
translate-context="Sidebar/Queue/Text"
:translate-params="{index: currentIndex + 1, length: queue.length}"
>
%{ index } of %{ length }

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Artist, Album } from '~/types'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { ref, computed, reactive, watch, onMounted } from 'vue'
import { refDebounced } from '@vueuse/core'
@ -21,7 +21,7 @@ const props = withDefaults(defineProps<Props>(), {
})
const logger = useLogger()
const { $pgettext } = useGettext()
const { t } = useI18n()
const query = ref('')
const queryDebounced = refDebounced(query, 500)
@ -65,7 +65,7 @@ onMounted(() => {
})
const labels = computed(() => ({
searchPlaceholder: $pgettext('*/Search/Input.Placeholder', 'Artist, album, track…')
searchPlaceholder: t('Artist, album, track…')
}))
</script>
@ -73,7 +73,7 @@ const labels = computed(() => ({
<template>
<div>
<h2>
<translate translate-context="Content/Search/Title">
<translate >
Search for some music
</translate>
</h2>
@ -91,7 +91,7 @@ const labels = computed(() => ({
</div>
<template v-if="query.length > 0">
<h3 class="ui title">
<translate translate-context="*/*/*/Noun">
<translate >
Artists
</translate>
</h3>
@ -105,14 +105,14 @@ const labels = computed(() => ({
</div>
</div>
<p v-else>
<translate translate-context="Content/Search/Paragraph">
<translate >
No artist matched your query
</translate>
</p>
</template>
<template v-if="query.length > 0">
<h3 class="ui title">
<translate translate-context="*/*/*">
<translate >
Albums
</translate>
</h3>
@ -132,7 +132,7 @@ const labels = computed(() => ({
</div>
</div>
<p v-else>
<translate translate-context="Content/Search/Paragraph">
<translate >
No album matched your query
</translate>
</p>

View File

@ -6,7 +6,7 @@ import jQuery from 'jquery'
import { trim } from 'lodash-es'
import { useFocus, useCurrentElement } from '@vueuse/core'
import { ref, computed, onMounted } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'
@ -48,14 +48,14 @@ const { focused } = useFocus(search)
onKeyboardShortcut(['shift', 'f'], () => (focused.value = true), true)
onKeyboardShortcut(['ctrl', 'k'], () => (focused.value = true), true)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
placeholder: $pgettext('Sidebar/Search/Input.Placeholder', 'Search for artists, albums, tracks…'),
searchContent: $pgettext('Sidebar/Search/Input.Label', 'Search for content'),
artist: $pgettext('*/*/*/Noun', 'Artist'),
album: $pgettext('*/*/*', 'Album'),
track: $pgettext('*/*/*/Noun', 'Track'),
tag: $pgettext('*/*/*/Noun', 'Tag')
placeholder: t('Search for artists, albums, tracks…'),
searchContent: t('Search for content'),
artist: t('Artist'),
album: t('Album'),
track: t('Track'),
tag: t('Tag')
}))
const router = useRouter()
@ -77,11 +77,11 @@ const blur = () => {
const categories = computed(() => [
{
code: 'federation',
name: $pgettext('*/*/*', 'Federation')
name: t('Federation')
},
{
code: 'podcasts',
name: $pgettext('*/*/*', 'Podcasts')
name: t('Podcasts')
},
{
code: 'artists',
@ -138,8 +138,8 @@ onMounted(() => {
showNoResults: true,
error: {
// @ts-expect-error Semantic is broken
noResultsHeader: $pgettext('Sidebar/Search/Error', 'No matches found'),
noResults: $pgettext('Sidebar/Search/Error.Label', 'Sorry, there are no results for this search')
noResultsHeader: t('No matches found'),
noResults: t('Sorry, there are no results for this search')
},
onSelect (result, response) {
@ -179,7 +179,7 @@ onMounted(() => {
if (category.code === 'federation' && id) {
resultsEmpty = false
results[category.code]?.results.push({
title: $pgettext('Search/*/*', 'Search on the fediverse'),
title: t('Search on the fediverse'),
routerUrl: {
name: 'search',
query: { id }
@ -190,7 +190,7 @@ onMounted(() => {
if (category.code === 'podcasts' && id) {
resultsEmpty = false
results[category.code]?.results.push({
title: $pgettext('Search/*/*', 'Subscribe to podcast via RSS'),
title: t('Subscribe to podcast via RSS'),
routerUrl: {
name: 'search',
query: { id, type: 'rss' }
@ -200,7 +200,7 @@ onMounted(() => {
if (category.code === 'more') {
results[category.code]?.results.push({
title: $pgettext('Search/*/*', 'More results 🡒'),
title: t('More results 🡒'),
routerUrl: {
name: 'search',
query: { type: 'artists', q: query.value }

View File

@ -1,17 +1,17 @@
<script setup lang="ts">
import { usePlayer } from '~/composables/audio/player'
import { useTimeoutFn } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { volume, mute } = usePlayer()
const expanded = ref(false)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
unmute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Unmute'),
mute: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Mute'),
slider: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Adjust volume')
unmute: t('Unmute'),
mute: t('Mute'),
slider: t('Adjust volume')
}))
const { start, stop } = useTimeoutFn(() => (expanded.value = false), 500, { immediate: false })

View File

@ -60,7 +60,7 @@ const imageUrl = computed(() => props.album.cover?.urls.original
<div class="extra content">
<span v-if="album.release_date">{{ momentFormat(new Date(album.release_date), 'Y') }} · </span>
<translate
translate-context="*/*/*"
:translate-params="{count: album.tracks_count}"
:translate-n="album.tracks_count"
translate-plural="%{ count } tracks"

View File

@ -112,7 +112,7 @@ watch(
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>

View File

@ -64,7 +64,7 @@ const imageUrl = computed(() => cover.value?.urls.original
<div class="extra content">
<translate
v-if="artist.content_category === 'music'"
translate-context="*/*/*"
:translate-params="{count: artist.tracks_count}"
:translate-n="artist.tracks_count"
translate-plural="%{ count } tracks"
@ -73,7 +73,7 @@ const imageUrl = computed(() => cover.value?.urls.original
</translate>
<translate
v-else
translate-context="*/*/*"
:translate-params="{count: artist.tracks_count}"
:translate-n="artist.tracks_count"
translate-plural="%{ count } episodes"

View File

@ -108,7 +108,7 @@ watch(
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>

View File

@ -2,8 +2,8 @@
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
import { useGettext } from 'vue3-gettext'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { usePlayer } from '~/composables/audio/player'
import { useQueue } from '~/composables/audio/queue'
@ -52,8 +52,8 @@ const { currentTrack } = useQueue()
const { isPlaying } = usePlayer()
const { activateTrack } = usePlayOptions(props)
const { $pgettext } = useGettext()
const actionsButtonLabel = computed(() => $pgettext('Content/Track/Icon.Tooltip/Verb', 'Show track actions'))
const { t } = useI18n()
const actionsButtonLabel = computed(() => t('Show track actions'))
</script>
<template>

View File

@ -4,7 +4,7 @@ import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
// import type { Track } from '~/types'
import { useStore } from '~/store'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import { computed, ref } from 'vue'
import usePlayOptions from '~/composables/audio/usePlayOptions'
@ -58,33 +58,33 @@ const store = useStore()
const isFavorite = computed(() => store.getters['favorites/isFavorite'](props.track.id))
const { $pgettext } = useGettext()
const { t } = useI18n()
const favoriteButton = computed(() => isFavorite.value
? $pgettext('Content/Track/Icon.Tooltip/Verb', 'Remove from favorites')
: $pgettext('Content/Track/*/Verb', 'Add to favorites')
? t('Remove from favorites')
: t('Add to favorites')
)
const trackDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'Episode details')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'Track details')
? t('Episode details')
: t('Track details')
)
const albumDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View series')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View album')
? t('View series')
: t('View album')
)
const artistDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View channel')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View artist')
? t('View channel')
: t('View artist')
)
const labels = computed(() => ({
startRadio: $pgettext('*/Queue/Dropdown/Button/Title', 'Play radio'),
playNow: $pgettext('*/Queue/Dropdown/Button/Title', 'Play now'),
addToQueue: $pgettext('*/Queue/Dropdown/Button/Title', 'Add to queue'),
playNext: $pgettext('*/Queue/Dropdown/Button/Title', 'Play next'),
addToPlaylist: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Add to playlist…')
startRadio: t('Play radio'),
playNow: t('Play now'),
addToQueue: t('Add to queue'),
playNext: t('Play next'),
addToPlaylist: t('Add to playlist…')
}))
</script>

View File

@ -2,8 +2,8 @@
import type { Track, Artist, Album, Playlist, Library, Channel, Actor } from '~/types'
import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
import { useGettext } from 'vue3-gettext'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { usePlayer } from '~/composables/audio/player'
import { useQueue } from '~/composables/audio/queue'
@ -52,8 +52,8 @@ const { currentTrack } = useQueue()
const { isPlaying } = usePlayer()
const { activateTrack } = usePlayOptions(props)
const { $pgettext } = useGettext()
const actionsButtonLabel = computed(() => $pgettext('Content/Track/Icon.Tooltip/Verb', 'Show track actions'))
const { t } = useI18n()
const actionsButtonLabel = computed(() => t('Show track actions'))
</script>
<template>

View File

@ -4,7 +4,7 @@ import type { PlayOptionsProps } from '~/composables/audio/usePlayOptions'
// import type { Track } from '~/types'
import { useStore } from '~/store'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import SemanticModal from '~/components/semantic/Modal.vue'
import { computed, ref } from 'vue'
import usePlayOptions from '~/composables/audio/usePlayOptions'
@ -58,33 +58,33 @@ const store = useStore()
const isFavorite = computed(() => store.getters['favorites/isFavorite'](props.track.id))
const { $pgettext } = useGettext()
const { t } = useI18n()
const favoriteButton = computed(() => isFavorite.value
? $pgettext('Content/Track/Icon.Tooltip/Verb', 'Remove from favorites')
: $pgettext('Content/Track/*/Verb', 'Add to favorites')
? t('Remove from favorites')
: t('Add to favorites')
)
const trackDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'Episode details')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'Track details')
? t('Episode details')
: t('Track details')
)
const albumDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View series')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View album')
? t('View series')
: t('View album')
)
const artistDetailsButton = computed(() => props.track.artist?.content_category === 'podcast'
? $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View channel')
: $pgettext('*/Queue/Dropdown/Button/Label/Short', 'View artist')
? t('View channel')
: t('View artist')
)
const labels = computed(() => ({
startRadio: $pgettext('*/Queue/Dropdown/Button/Title', 'Play radio'),
playNow: $pgettext('*/Queue/Dropdown/Button/Title', 'Play now'),
addToQueue: $pgettext('*/Queue/Dropdown/Button/Title', 'Add to queue'),
playNext: $pgettext('*/Queue/Dropdown/Button/Title', 'Play next'),
addToPlaylist: $pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Add to playlist…')
startRadio: t('Play radio'),
playNow: t('Play now'),
addToQueue: t('Add to queue'),
playNext: t('Play next'),
addToPlaylist: t('Add to playlist…')
}))
</script>

View File

@ -2,7 +2,7 @@
import type { Track } from '~/types'
import { useElementByPoint, useMouse } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { clone, uniqBy } from 'lodash-es'
import { ref, computed } from 'vue'
@ -93,11 +93,11 @@ const allTracks = computed(() => {
: tracks
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('*/*/*/Noun', 'Title'),
album: $pgettext('*/*/*/Noun', 'Album'),
artist: $pgettext('*/*/*/Noun', 'Artist')
title: t('Title'),
album: t('Album'),
artist: t('Artist')
}))
const isLoading = ref(false)

View File

@ -200,7 +200,7 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
>
<div class="ui icon header">
<i class="music icon" />
<translate translate-context="Content/Home/Placeholder">
<translate >
Nothing found
</translate>
</div>
@ -217,7 +217,7 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage as string)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import axios from 'axios'
@ -15,12 +15,12 @@ interface Props {
const props = defineProps<Props>()
const { $pgettext } = useGettext()
const { t } = useI18n()
const application = ref()
const labels = computed(() => ({
title: $pgettext('Content/Applications/Title', 'Edit application')
title: t('Edit application')
}))
const isLoading = ref(false)
@ -72,23 +72,23 @@ store.state.auth.applicationSecret = undefined
</div>
<template v-else>
<router-link :to="{name: 'settings'}">
<translate translate-context="Content/Applications/Link">
<translate >
Back to settings
</translate>
</router-link>
<h2 class="ui header">
<translate translate-context="Content/Applications/Title">
<translate >
Application details
</translate>
</h2>
<div class="ui form">
<p>
<translate translate-context="Content/Application/Paragraph/">
<translate >
Application ID and secret are really sensitive values and must be treated like passwords. Do not share those with anyone else.
</translate>
</p>
<div class="field">
<label for="copy-id"><translate translate-context="Content/Applications/Label">Application ID</translate></label>
<label for="copy-id"><translate >Application ID</translate></label>
<copy-input
id="copy-id"
:value="application.client_id"
@ -100,18 +100,18 @@ store.state.auth.applicationSecret = undefined
>
<div class="ui small warning message">
<h3 class="header">
<translate translate-context="*/*/*">
<translate >
Keep a copy of this token in a safe place
</translate>
</h3>
<p>
<translate translate-context="*/*/*">
<translate >
You won't be able to see it again once you leave this screen.
</translate>
</p>
</div>
<label for="copy-secret"><translate translate-context="Content/Applications/Label">Application secret</translate></label>
<label for="copy-secret"><translate >Application secret</translate></label>
<copy-input
id="copy-secret"
:value="secret"
@ -121,7 +121,7 @@ store.state.auth.applicationSecret = undefined
v-if="application.token != undefined"
class="field"
>
<label for="copy-secret"><translate translate-context="Content/Applications/Label">Access token</translate></label>
<label for="copy-secret"><translate >Access token</translate></label>
<copy-input
id="copy-secret"
:value="application.token"
@ -131,12 +131,12 @@ store.state.auth.applicationSecret = undefined
@click.prevent="refreshToken"
>
<i class="refresh icon" />
<translate translate-context="Content/Applications/Label">Regenerate token</translate>
<translate >Regenerate token</translate>
</a>
</div>
</div>
<h2 class="ui header">
<translate translate-context="Content/Applications/Title">
<translate >
Edit application
</translate>
</h2>

View File

@ -4,7 +4,7 @@ import type { BackendError, Application } from '~/types'
import axios from 'axios'
import { ref, reactive, computed } from 'vue'
import { computedEager } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { uniq } from 'lodash-es'
import useScopes from '~/composables/auth/useScopes'
@ -25,7 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
defaults: () => ({})
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const scopes = useScopes()
.filter(scope => !['reports', 'security'].includes(scope.id))
@ -86,14 +86,14 @@ const toggleAllScopes = (parent: typeof allScopes['value'][number]) => {
const scopeParents = computedEager(() => [
{
id: 'read',
label: $pgettext('Content/OAuth Scopes/Label/Verb', 'Read'),
description: $pgettext('Content/OAuth Scopes/Help Text', 'Read-only access to user data'),
label: t('Read'),
description: t('Read-only access to user data'),
value: scopeArray.value.includes('read')
},
{
id: 'write',
label: $pgettext('Content/OAuth Scopes/Label/Verb', 'Write'),
description: $pgettext('Content/OAuth Scopes/Help Text', 'Write-only access to user data'),
label: t('Write'),
description: t('Write-only access to user data'),
value: scopeArray.value.includes('write')
}
])
@ -120,7 +120,7 @@ const allScopes = computed(() => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
We cannot save your changes
</translate>
</h4>
@ -134,7 +134,7 @@ const allScopes = computed(() => {
</ul>
</div>
<div class="ui field">
<label for="application-name"><translate translate-context="*/*/*/Noun">Name</translate></label>
<label for="application-name"><translate >Name</translate></label>
<input
id="application-name"
v-model="fields.name"
@ -144,7 +144,7 @@ const allScopes = computed(() => {
>
</div>
<div class="ui field">
<label for="redirect-uris"><translate translate-context="Content/Applications/Input.Label/Noun">Redirect URI</translate></label>
<label for="redirect-uris"><translate >Redirect URI</translate></label>
<input
id="redirect-uris"
v-model="fields.redirect_uris"
@ -152,15 +152,15 @@ const allScopes = computed(() => {
type="text"
>
<p class="help">
<translate translate-context="Content/Applications/Help Text">
<translate >
Use "urn:ietf:wg:oauth:2.0:oob" as a redirect URI if your application is not served on the web.
</translate>
</p>
</div>
<div class="ui field">
<label><translate translate-context="Content/*/*/Noun">Scopes</translate></label>
<label><translate >Scopes</translate></label>
<p>
<translate translate-context="Content/Applications/Paragraph/">
<translate >
Checking the parent "Read" or "Write" scopes implies access to all the corresponding children scopes.
</translate>
</p>
@ -210,13 +210,13 @@ const allScopes = computed(() => {
>
<translate
v-if="app !== null"
translate-context="Content/Applications/Button.Label/Verb"
>
Update application
</translate>
<translate
v-else
translate-context="Content/Applications/Button.Label/Verb"
>
Create application
</translate>

View File

@ -1,11 +1,14 @@
<script setup lang="ts">
import ApplicationForm from '~/components/auth/ApplicationForm.vue'
import { computed, reactive } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useRouter } from 'vue-router'
import type { Application } from '~/types'
import { computed, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import ApplicationForm from '~/components/auth/ApplicationForm.vue'
interface Props {
name?: string
scopes?: string
@ -24,9 +27,9 @@ const defaults = reactive({
redirectUris: props.redirectUris
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Content/Settings/Button.Label', 'Create a new application')
title: t('Create a new application')
}))
const router = useRouter()
@ -52,12 +55,12 @@ const created = (application: Application) => {
<div class="ui vertical stripe segment">
<section class="ui text container">
<router-link :to="{name: 'settings'}">
<translate translate-context="Content/Applications/Link">
<translate >
Back to settings
</translate>
</router-link>
<h2 class="ui header">
<translate translate-context="Content/Settings/Button.Label">
<translate >
Create a new application
</translate>
</h2>

View File

@ -2,7 +2,7 @@
import type { BackendError, Application } from '~/types'
import axios from 'axios'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { whenever } from '@vueuse/core'
import { ref, computed } from 'vue'
@ -21,7 +21,7 @@ interface Props {
const props = defineProps<Props>()
const { $pgettext } = useGettext()
const { t } = useI18n()
const sharedLabels = useSharedLabels()
const knownScopes = useScopes()
@ -84,7 +84,7 @@ const submit = async () => {
}
const labels = computed(() => ({
title: $pgettext('Head/Authorize/Title', 'Allow application')
title: t('Allow application')
}))
const requestedScopes = computed(() => props.scope.split(' '))
@ -119,7 +119,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
<section class="ui vertical stripe segment">
<div class="ui small text container">
<h2>
<i class="lock open icon" /><translate translate-context="Content/Auth/Title/Verb">
<i class="lock open icon" /><translate >
Authorize third-party app
</translate>
</h2>
@ -132,7 +132,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
v-if="application"
class="header"
>
<translate translate-context="Popup/Moderation/Error message">
<translate >
Error while authorizing application
</translate>
</h4>
@ -140,7 +140,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
v-else
class="header"
>
<translate translate-context="Popup/Moderation/Error message">
<translate >
Error while fetching application data
</translate>
</h4>
@ -166,7 +166,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
>
<h3>
<translate
translate-context="Content/Auth/Title"
:translate-params="{app: application.name}"
>
%{ app } wants to access your Funkwhale account
@ -183,20 +183,20 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
>
<i class="pencil icon" />
<translate translate-context="Content/Auth/Label/Noun">Write-only</translate>
<translate >Write-only</translate>
</span>
<span
v-else-if="!topic.write && topic.read"
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
>
<translate translate-context="Content/Auth/Label/Noun">Read-only</translate>
<translate >Read-only</translate>
</span>
<span
v-else-if="topic.write && topic.read"
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
>
<i class="pencil icon" />
<translate translate-context="Content/Auth/Label/Noun">Full access</translate>
<translate >Full access</translate>
</span>
<i :class="[topic.icon, 'icon']" />
<div class="content">
@ -207,7 +207,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
</div>
</h4>
<div v-if="unknownRequestedScopes.length > 0">
<p><strong><translate translate-context="Content/Auth/Paragraph">The application is also requesting the following unknown permissions:</translate></strong></p>
<p><strong><translate >The application is also requesting the following unknown permissions:</translate></strong></p>
<ul
v-for="(unknownscope, key) in unknownRequestedScopes"
:key="key"
@ -221,7 +221,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
>
<i class="lock open icon" />
<translate
translate-context="Content/Signup/Button.Label/Verb"
:translate-params="{app: application.name}"
>
Authorize %{ app }
@ -230,21 +230,21 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
<p
v-if="redirectUri === 'urn:ietf:wg:oauth:2.0:oob'"
v-translate
translate-context="Content/Auth/Paragraph"
>
You will be shown a code to copy-paste in the application.
</p>
<p
v-else
v-translate="{url: redirectUri}"
translate-context="Content/Auth/Paragraph"
:translate-params="{url: redirectUri}"
>
You will be redirected to <strong>%{ url }</strong>
</p>
</form>
<div v-else-if="code">
<p><strong><translate translate-context="Content/Auth/Paragraph">Copy-paste the following code in the application:</translate></strong></p>
<p><strong><translate >Copy-paste the following code in the application:</translate></strong></p>
<copy-input :value="code" />
</div>
</div>

View File

@ -3,7 +3,7 @@ import type { BackendError } from '~/types'
import type { RouteLocationRaw } from 'vue-router'
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import PasswordInput from '~/components/forms/PasswordInput.vue'
@ -21,7 +21,7 @@ const props = withDefaults(defineProps<Props>(), {
})
const domain = location.hostname
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const credentials = reactive({
@ -30,7 +30,7 @@ const credentials = reactive({
})
const labels = computed(() => ({
usernamePlaceholder: $pgettext('Content/Login/Input.Placeholder', 'Enter your username or e-mail address')
usernamePlaceholder: t('Enter your username or e-mail address')
}))
const username = ref()
@ -75,18 +75,18 @@ const submit = async () => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Login/Error message.Title">
<translate >
We cannot log you in
</translate>
</h4>
<ul class="list">
<li v-if="errors[0] == 'invalid_credentials' && $store.state.instance.settings.moderation.signup_approval_enabled.value">
<translate translate-context="Content/Login/Error message.List item/Call to action">
<translate >
If you signed-up recently, you may need to wait before our moderation team review your account, or verify your e-mail address.
</translate>
</li>
<li v-else-if="errors[0] == 'invalid_credentials'">
<translate translate-context="Content/Login/Error message.List item/Call to action">
<translate >
Please double-check that your username and password combination is correct and make sure you verified your e-mail address.
</translate>
</li>
@ -98,11 +98,11 @@ const submit = async () => {
<template v-if="domain === $store.getters['instance/domain']">
<div class="field">
<label for="username-field">
<translate translate-context="Content/Login/Input.Label/Noun">Username or e-mail address</translate>
<translate >Username or e-mail address</translate>
<template v-if="showSignup">
|
<router-link :to="{path: '/signup'}">
<translate translate-context="*/Signup/Link/Verb">Create an account</translate>
<translate >Create an account</translate>
</router-link>
</template>
</label>
@ -119,12 +119,12 @@ const submit = async () => {
</div>
<div class="field">
<label for="password-field">
<translate translate-context="*/*/*">Password</translate> |
<translate >Password</translate> |
<router-link
tabindex="1"
:to="{name: 'auth.password-reset', query: {email: credentials.username}}"
>
<translate translate-context="*/Login/*/Verb">Reset your password</translate>
<translate >Reset your password</translate>
</router-link>
</label>
<password-input
@ -137,7 +137,6 @@ const submit = async () => {
<template v-else>
<p>
<translate
translate-context="Content/Auth/Paragraph"
:translate-params="{domain: $store.getters['instance/domain']}"
>
You will be redirected to %{ domain } to authenticate.
@ -148,7 +147,7 @@ const submit = async () => {
:class="['ui', {'loading': isLoading}, 'right', 'floated', buttonClasses, 'button']"
type="submit"
>
<translate translate-context="*/Login/*/Verb">
<translate >
Login
</translate>
</button>

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/Login/Title', 'Log Out')
title: t('Log Out')
}))
</script>
@ -19,13 +19,13 @@ const labels = computed(() => ({
class="ui small text container"
>
<h2>
<translate translate-context="Content/Login/Title">
<translate >
Are you sure you want to log out?
</translate>
</h2>
<p
v-translate="{username: $store.state.auth.username}"
translate-context="Content/Login/Paragraph"
>
You are currently logged in as %{ username }
</p>
@ -33,7 +33,7 @@ const labels = computed(() => ({
class="ui button"
@click="$store.dispatch('auth/logout')"
>
<translate translate-context="Content/Login/Button.Label">
<translate >
Yes, log me out!
</translate>
</button>
@ -43,7 +43,7 @@ const labels = computed(() => ({
class="ui small text container"
>
<h2>
<translate translate-context="Content/Login/Title">
<translate >
You aren't currently logged in
</translate>
</h2>
@ -51,7 +51,7 @@ const labels = computed(() => ({
to="/login"
class="ui button"
>
<translate translate-context="Content/Login/Button.Label">
<translate >
Log in!
</translate>
</router-link>

View File

@ -69,7 +69,7 @@ const submitAndScan = async () => {
target="_blank"
>
<i class="external icon" />
<translate translate-context="Footer/*/List item.Link/Short, Noun">Documentation</translate>
<translate >Documentation</translate>
</a>
</template>
<div class="ui clearing hidden divider" />
@ -79,7 +79,7 @@ const submitAndScan = async () => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while saving plugin
</translate>
</h4>
@ -99,7 +99,7 @@ const submitAndScan = async () => {
v-model="enabled"
type="checkbox"
>
<label :for="`${plugin.name}-enabled`"><translate translate-context="*/*/*">Enabled</translate></label>
<label :for="`${plugin.name}-enabled`"><translate >Enabled</translate></label>
</div>
</div>
<div class="ui clearing hidden divider" />
@ -107,7 +107,7 @@ const submitAndScan = async () => {
v-if="plugin.source"
class="field"
>
<label for="plugin-library"><translate translate-context="*/*/*/Noun">Library</translate></label>
<label for="plugin-library"><translate >Library</translate></label>
<select
id="plugin-library"
v-model="values['library']"
@ -121,7 +121,7 @@ const submitAndScan = async () => {
</option>
</select>
<div>
<translate translate-context="*/*/Paragraph/Noun">
<translate >
Library where files should be imported.
</translate>
</div>
@ -198,7 +198,7 @@ const submitAndScan = async () => {
type="submit"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Save
</translate>
</button>
@ -207,7 +207,7 @@ const submitAndScan = async () => {
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
@click.prevent="submitAndScan"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Scan
</translate>
</button>

View File

@ -6,7 +6,7 @@ import axios from 'axios'
import $ from 'jquery'
import { computed, reactive, ref, onMounted } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'
@ -30,7 +30,7 @@ interface Settings {
fields: Record<FieldId, Field>
}
const { $pgettext } = useGettext()
const { t } = useI18n()
const sharedLabels = useSharedLabels()
const logger = useLogger()
const router = useRouter()
@ -57,7 +57,7 @@ const settings = reactive({
const orderedSettingsFields = SETTINGS_ORDER.map(id => settings.fields[id])
const labels = computed(() => ({
title: $pgettext('Head/Settings/Title', 'Account Settings')
title: t('Account Settings')
}))
const isLoading = ref(false)
@ -224,7 +224,7 @@ const deleteAccount = async () => {
await axios.delete('users/me/', { data: payload })
store.commit('ui/addMessage', {
content: $pgettext('*/Auth/Message', 'Your deletion request was submitted, your account and content will be deleted shortly'),
content: t('Your deletion request was submitted, your account and content will be deleted shortly'),
date: new Date()
})
@ -276,7 +276,7 @@ fetchOwnedApps()
<div class="ui vertical stripe segment">
<section class="ui text container">
<h2 class="ui header">
<translate translate-context="Content/Settings/Title">
<translate >
Account settings
</translate>
</h2>
@ -289,7 +289,7 @@ fetchOwnedApps()
class="ui positive message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Message">
<translate >
Settings updated
</translate>
</h4>
@ -300,7 +300,7 @@ fetchOwnedApps()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Error message.Title">
<translate >
Your settings can't be updated
</translate>
</h4>
@ -346,7 +346,7 @@ fetchOwnedApps()
:class="['ui', { loading: isLoading }, 'button']"
type="submit"
>
<translate translate-context="Content/Settings/Button.Label/Verb">
<translate >
Update settings
</translate>
</button>
@ -355,7 +355,7 @@ fetchOwnedApps()
<section class="ui text container">
<div class="ui hidden divider" />
<h2 class="ui header">
<translate translate-context="Content/Settings/Title">
<translate >
Avatar
</translate>
</h2>
@ -366,7 +366,7 @@ fetchOwnedApps()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Error message.Title">
<translate >
Your avatar cannot be saved
</translate>
</h4>
@ -385,7 +385,7 @@ fetchOwnedApps()
@update:model-value="submitAvatar($event)"
@delete="avatar = {uuid: null}"
>
<translate translate-context="Content/Channel/*">
<translate >
Avatar
</translate>
</attachment-input>
@ -395,14 +395,14 @@ fetchOwnedApps()
<section class="ui text container">
<div class="ui hidden divider" />
<h2 class="ui header">
<translate translate-context="Content/Settings/Title/Verb">
<translate >
Change my password
</translate>
</h2>
<div class="ui message">
<translate translate-context="Content/Settings/Paragraph'">
<translate >
Changing your password will also change your Subsonic API password if you have requested one.
</translate>&nbsp;<translate translate-context="Content/Settings/Paragraph">
</translate>&nbsp;<translate >
You will have to update your password on your clients that use this password.
</translate>
</div>
@ -416,20 +416,20 @@ fetchOwnedApps()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Error message.Title">
<translate >
Your password cannot be changed
</translate>
</h4>
<ul class="list">
<li v-if="passwordError == 'invalid_credentials'">
<translate translate-context="Content/Settings/Error message.List item/Call to action">
<translate >
Please double-check your password is correct
</translate>
</li>
</ul>
</div>
<div class="field">
<label for="old-password-field"><translate translate-context="Content/Settings/Input.Label">Current password</translate></label>
<label for="old-password-field"><translate >Current password</translate></label>
<password-input
v-model="credentials.oldPassword"
field-id="old-password-field"
@ -437,7 +437,7 @@ fetchOwnedApps()
/>
</div>
<div class="field">
<label for="new-password-field"><translate translate-context="Content/Settings/Input.Label">New password</translate></label>
<label for="new-password-field"><translate >New password</translate></label>
<password-input
v-model="credentials.newPassword"
field-id="new-password-field"
@ -448,12 +448,12 @@ fetchOwnedApps()
:class="['ui', {'loading': isLoadingPassword}, {disabled: !credentials.newPassword || !credentials.oldPassword}, 'warning', 'button']"
:action="submitPassword"
>
<translate translate-context="Content/Settings/Button.Label">
<translate >
Change password
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Settings/Title">
<translate >
Change your password?
</translate>
</p>
@ -461,18 +461,18 @@ fetchOwnedApps()
<template #modal-content>
<div>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
Changing your password will have the following consequences:
</translate>
</p>
<ul>
<li>
<translate translate-context="Popup/Settings/List item">
<translate >
You will be logged out from this session and have to log in with the new one
</translate>
</li>
<li>
<translate translate-context="Popup/Settings/List item">
<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>
@ -481,7 +481,7 @@ fetchOwnedApps()
</template>
<template #modal-confirm>
<div>
<translate translate-context="Popup/Settings/Button.Label">
<translate >
Disable access
</translate>
</div>
@ -500,13 +500,13 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="eye slash outline icon" />
<div class="content">
<translate translate-context="Content/Settings/Title/Noun">
<translate >
Content filters
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph">
<translate >
Content filters help you hide content you don't want to see on the service.
</translate>
</p>
@ -516,12 +516,12 @@ fetchOwnedApps()
@click="$store.dispatch('moderation/fetchContentFilters')"
>
<i class="refresh icon" />&nbsp;
<translate translate-context="Content/*/Button.Label/Short, Verb">
<translate >
Refresh
</translate>
</button>
<h3 class="ui header">
<translate translate-context="Content/Settings/Title">
<translate >
Hidden artists
</translate>
</h3>
@ -529,12 +529,12 @@ fetchOwnedApps()
<thead>
<tr>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Name
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Creation date
</translate>
</th>
@ -559,7 +559,7 @@ fetchOwnedApps()
class="ui basic tiny button"
@click="$store.dispatch('moderation/deleteContentFilter', filter.uuid)"
>
<translate translate-context="*/*/*/Verb">
<translate >
Delete
</translate>
</button>
@ -576,13 +576,13 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="open lock icon" />
<div class="content">
<translate translate-context="Content/Settings/Title/Noun">
<translate >
Authorized apps
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph">
<translate >
This is the list of applications that have access to your account data.
</translate>
</p>
@ -591,7 +591,7 @@ fetchOwnedApps()
@click="fetchApps()"
>
<i class="refresh icon" />&nbsp;
<translate translate-context="Content/*/Button.Label/Short, Verb">
<translate >
Refresh
</translate>
</button>
@ -602,12 +602,12 @@ fetchOwnedApps()
<thead>
<tr>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Application
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Permissions
</translate>
</th>
@ -630,27 +630,27 @@ fetchOwnedApps()
:class="['ui', 'tiny', 'danger', { loading: isRevoking.has(app.client_id) }, 'button']"
@confirm="revokeApp(app.client_id)"
>
<translate translate-context="*/*/*/Verb">
<translate >
Revoke
</translate>
<template #modal-header>
<p
v-translate="{application: app.name}"
translate-context="Popup/Settings/Title"
>
Revoke access for application "%{ application }"?
</p>
</template>
<template #modal-content>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
This will prevent this application from accessing the service on your behalf.
</translate>
</p>
</template>
<template #modal-confirm>
<div>
<translate translate-context="*/Settings/Button.Label/Verb">
<translate >
Revoke access
</translate>
</div>
@ -662,11 +662,11 @@ fetchOwnedApps()
</table>
<empty-state v-else>
<template #title>
<translate translate-context="Content/Applications/Paragraph">
<translate >
You don't have any application connected with your account.
</translate>
</template>
<translate translate-context="Content/Applications/Paragraph">
<translate >
If you authorize third-party applications to access your data, those applications will be listed here.
</translate>
</empty-state>
@ -679,13 +679,13 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="code icon" />
<div class="content">
<translate translate-context="Content/Settings/Title/Noun">
<translate >
Your applications
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph">
<translate >
This is the list of applications that you have registered.
</translate>
</p>
@ -693,7 +693,7 @@ fetchOwnedApps()
class="ui success button"
:to="{name: 'settings.applications.new'}"
>
<translate translate-context="Content/Settings/Button.Label">
<translate >
Register a new application
</translate>
</router-link>
@ -704,17 +704,17 @@ fetchOwnedApps()
<thead>
<tr>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Application
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Scopes
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Creation date
</translate>
</th>
@ -742,7 +742,7 @@ fetchOwnedApps()
class="ui tiny success button"
:to="{name: 'settings.applications.edit', params: {id: app.client_id}}"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Edit
</translate>
</router-link>
@ -750,27 +750,27 @@ fetchOwnedApps()
:class="['ui', 'tiny', 'danger', { loading: isDeleting.has(app.client_id) }, 'button']"
@confirm="deleteApp(app.client_id)"
>
<translate translate-context="*/*/*/Verb">
<translate >
Remove
</translate>
<template #modal-header>
<p
v-translate="{application: app.name}"
translate-context="Popup/Settings/Title"
>
Remove application "%{ application }"?
</p>
</template>
<template #modal-content>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
This will permanently remove the application and all the associated tokens.
</translate>
</p>
</template>
<template #modal-confirm>
<div>
<translate translate-context="*/Settings/Button.Label/Verb">
<translate >
Remove application
</translate>
</div>
@ -782,11 +782,11 @@ fetchOwnedApps()
</table>
<empty-state v-else>
<template #title>
<translate translate-context="Content/Applications/Paragraph">
<translate >
You don't have registered any application yet.
</translate>
</template>
<translate translate-context="Content/Applications/Paragraph">
<translate >
Register one to integrate Funkwhale with third-party applications.
</translate>
</empty-state>
@ -800,13 +800,13 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="code icon" />
<div class="content">
<translate translate-context="Content/Settings/Title/Noun">
<translate >
Plugins
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph">
<translate >
Use plugins to extend Funkwhale and get additional features.
</translate>
</p>
@ -814,7 +814,7 @@ fetchOwnedApps()
class="ui success button"
:to="{name: 'settings.plugins'}"
>
<translate translate-context="Content/Settings/Button.Label">
<translate >
Manage plugins
</translate>
</router-link>
@ -824,20 +824,20 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="comment icon" />
<div class="content">
<translate translate-context="*/*/Button.Label">
<translate >
Change my e-mail address
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph'">
<translate >
Change the e-mail address associated with your account. We will send a confirmation to the new address.
</translate>
</p>
<p>
<translate
:translate-params="{email: $store.state.auth.profile?.email}"
translate-context="Content/Settings/Paragraph'"
>
Your current e-mail address is %{ email }.
</translate>
@ -852,7 +852,7 @@ fetchOwnedApps()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Error message.Title">
<translate >
We cannot change your e-mail address
</translate>
</h4>
@ -866,7 +866,7 @@ fetchOwnedApps()
</ul>
</div>
<div class="field">
<label for="new-email"><translate translate-context="*/*/*">New e-mail address</translate></label>
<label for="new-email"><translate >New e-mail address</translate></label>
<input
id="new-email"
v-model="newEmail"
@ -875,7 +875,7 @@ fetchOwnedApps()
>
</div>
<div class="field">
<label for="current-password-field-email"><translate translate-context="*/*/*">Password</translate></label>
<label for="current-password-field-email"><translate >Password</translate></label>
<password-input
v-model="emailPassword"
field-id="current-password-field-email"
@ -886,7 +886,7 @@ fetchOwnedApps()
type="submit"
class="ui button"
>
<translate translate-context="*/*/*">
<translate >
Update
</translate>
</button>
@ -897,13 +897,13 @@ fetchOwnedApps()
<h2 class="ui header">
<i class="trash icon" />
<div class="content">
<translate translate-context="*/*/Button.Label">
<translate >
Delete my account
</translate>
</div>
</h2>
<p>
<translate translate-context="Content/Settings/Paragraph'">
<translate >
You can permanently and irreversibly delete your account and all the associated data using the form below. You will be asked for confirmation.
</translate>
</p>
@ -911,7 +911,7 @@ fetchOwnedApps()
role="alert"
class="ui warning message"
>
<translate translate-context="Content/Settings/Paragraph'">
<translate >
Your account will be deleted from our servers within a few minutes. We will also notify other servers who may have a copy of some of your data so they can proceed to deletion. Please note that some of these servers may be offline or unwilling to comply though.
</translate>
</div>
@ -922,7 +922,7 @@ fetchOwnedApps()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Settings/Error message.Title">
<translate >
We cannot delete your account
</translate>
</h4>
@ -936,7 +936,7 @@ fetchOwnedApps()
</ul>
</div>
<div class="field">
<label for="current-password-field"><translate translate-context="*/*/*">Password</translate></label>
<label for="current-password-field"><translate >Password</translate></label>
<password-input
v-model="deleteAccountPassword"
field-id="current-password-field"
@ -947,12 +947,12 @@ fetchOwnedApps()
:class="['ui', {'loading': isDeletingAccount}, {disabled: !deleteAccountPassword}, {danger: deleteAccountPassword}, 'button']"
:action="deleteAccount"
>
<translate translate-context="*/*/Button.Label">
<translate >
Delete my account
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Settings/Title">
<translate >
Do you want to delete your account?
</translate>
</p>
@ -960,7 +960,7 @@ fetchOwnedApps()
<template #modal-content>
<div>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
This is irreversible and will permanently remove your data from our servers. You will we immediately logged out.
</translate>
</p>
@ -968,7 +968,7 @@ fetchOwnedApps()
</template>
<template #modal-confirm>
<div>
<translate translate-context="*/*/Button.Label">
<translate >
Delete my account
</translate>
</div>

View File

@ -3,7 +3,7 @@ import type { RouteLocationRaw } from 'vue-router'
import type { BackendError, Form } from '~/types'
import { computed, reactive, ref, watchEffect } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import axios from 'axios'
@ -30,14 +30,14 @@ const props = withDefaults(defineProps<Props>(), {
signupApprovalEnabled: undefined
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const logger = useLogger()
const store = useStore()
const labels = computed(() => ({
placeholder: $pgettext('Content/Signup/Form/Placeholder', 'Enter your invitation code (case insensitive)'),
usernamePlaceholder: $pgettext('Content/Signup/Form/Placeholder', 'Enter your username'),
emailPlaceholder: $pgettext('Content/Signup/Form/Placeholder', 'Enter your e-mail address')
placeholder: t('Enter your invitation code (case insensitive)'),
usernamePlaceholder: t('Enter your username'),
emailPlaceholder: t('Enter your e-mail address')
}))
const signupRequiresApproval = computed(() => props.signupApprovalEnabled ?? store.state.instance.settings.moderation.signup_approval_enabled.value)
@ -88,18 +88,18 @@ fetchInstanceSettings()
<div v-if="submitted">
<div class="ui success message">
<p v-if="signupRequiresApproval">
<translate translate-context="Content/Signup/Form/Paragraph">
<translate >
Your account request was successfully submitted. You will be notified by e-mail when our moderation team has reviewed your request.
</translate>
</p>
<p v-else>
<translate translate-context="Content/Signup/Form/Paragraph">
<translate >
Your account was successfully created. Please verify your e-mail address before trying to login.
</translate>
</p>
</div>
<h2>
<translate translate-context="Content/Login/Title/Verb">
<translate >
Log in to your Funkwhale account
</translate>
</h2>
@ -117,7 +117,7 @@ fetchInstanceSettings()
v-if="!$store.state.instance.settings.users.registration_enabled.value"
class="ui message"
>
<translate translate-context="Content/Signup/Form/Paragraph">
<translate >
Public registrations are not possible on this instance. You will need an invitation code to sign up.
</translate>
</p>
@ -125,7 +125,7 @@ fetchInstanceSettings()
v-else-if="signupRequiresApproval"
class="ui message"
>
<translate translate-context="Content/Signup/Form/Paragraph">
<translate >
Registrations on this pod are open, but reviewed by moderators before approval.
</translate>
</p>
@ -143,7 +143,7 @@ fetchInstanceSettings()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Signup/Form/Paragraph">
<translate >
Your account cannot be created.
</translate>
</h4>
@ -157,7 +157,7 @@ fetchInstanceSettings()
</ul>
</div>
<div class="required field">
<label for="username-field"><translate translate-context="Content/*/*">Username</translate></label>
<label for="username-field"><translate >Username</translate></label>
<input
id="username-field"
ref="username"
@ -170,7 +170,7 @@ fetchInstanceSettings()
>
</div>
<div class="required field">
<label for="email-field"><translate translate-context="Content/*/*/Noun">E-mail address</translate></label>
<label for="email-field"><translate >E-mail address</translate></label>
<input
id="email-field"
ref="email"
@ -182,7 +182,7 @@ fetchInstanceSettings()
>
</div>
<div class="required field">
<label for="password-field"><translate translate-context="*/*/*">Password</translate></label>
<label for="password-field"><translate >Password</translate></label>
<password-input
v-model="payload.password1"
field-id="password-field"
@ -192,7 +192,7 @@ fetchInstanceSettings()
v-if="!$store.state.instance.settings.users.registration_enabled.value"
class="required field"
>
<label for="invitation-code"><translate translate-context="Content/*/Input.Label">Invitation code</translate></label>
<label for="invitation-code"><translate >Invitation code</translate></label>
<input
id="invitation-code"
v-model="payload.invitation"
@ -229,7 +229,7 @@ fetchInstanceSettings()
:class="['ui', buttonClasses, {'loading': isLoading}, ' right floated button']"
type="submit"
>
<translate translate-context="Content/Signup/Button.Label">
<translate >
Create my account
</translate>
</button>

View File

@ -1,19 +1,19 @@
<script setup lang="ts">
import type { BackendError } from '~/types'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { useStore } from '~/store'
import axios from 'axios'
import PasswordInput from '~/components/forms/PasswordInput.vue'
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const subsonicEnabled = computed(() => store.state.instance.settings.subsonic.enabled.value)
const labels = computed(() => ({
subsonicField: $pgettext('Content/Password/Input.label', 'Your subsonic API password')
subsonicField: t('Your subsonic API password')
}))
const errors = ref([] as string[])
@ -38,7 +38,7 @@ const fetchToken = async () => {
const showToken = ref(false)
const successMessage = ref('')
const requestNewToken = async () => {
successMessage.value = $pgettext('Content/Settings/Message', 'Password updated')
successMessage.value = t('Password updated')
success.value = false
errors.value = []
isLoading.value = true
@ -56,7 +56,7 @@ const requestNewToken = async () => {
}
const disable = async () => {
successMessage.value = $pgettext('Content/Settings/Message', 'Access disabled')
successMessage.value = t('Access disabled')
success.value = false
errors.value = []
isLoading.value = true
@ -82,7 +82,7 @@ fetchToken()
@submit.prevent="requestNewToken()"
>
<h2>
<translate translate-context="Content/Settings/Title">
<translate >
Subsonic API password
</translate>
</h2>
@ -90,19 +90,19 @@ fetchToken()
v-if="!subsonicEnabled"
class="ui message"
>
<translate translate-context="Content/Settings/Paragraph">
<translate >
The Subsonic API is not available on this Funkwhale instance.
</translate>
</p>
<p>
<translate translate-context="Content/Settings/Paragraph'">
<translate >
Funkwhale is compatible with other music players that support the Subsonic API.
</translate>&nbsp;<translate translate-context="Content/Settings/Paragraph">
</translate>&nbsp;<translate >
You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.
</translate>
</p>
<p>
<translate translate-context="Content/Settings/Paragraph">
<translate >
However, accessing Funkwhale from those clients requires a separate password you can set below.
</translate>
</p>
@ -111,7 +111,7 @@ fetchToken()
href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients"
target="_blank"
>
<translate translate-context="Content/Settings/Link">Discover how to use Funkwhale from other apps</translate>
<translate >Discover how to use Funkwhale from other apps</translate>
</a>
</p>
<div
@ -128,7 +128,7 @@ fetchToken()
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error
</translate>
</h4>
@ -164,26 +164,26 @@ fetchToken()
:class="['ui', {'loading': isLoading}, 'button']"
:action="requestNewToken"
>
<translate translate-context="*/Settings/Button.Label/Verb">
<translate >
Request a new password
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Settings/Title">
<translate >
Request a new Subsonic API password?
</translate>
</p>
</template>
<template #modal-content>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
This will log you out from existing devices that use the current password.
</translate>
</p>
</template>
<template #modal-confirm>
<div>
<translate translate-context="*/Settings/Button.Label/Verb">
<translate >
Request a new password
</translate>
</div>
@ -195,7 +195,7 @@ fetchToken()
:class="['ui', {'loading': isLoading}, 'button']"
@click="requestNewToken"
>
<translate translate-context="Content/Settings/Button.Label/Verb">
<translate >
Request a password
</translate>
</button>
@ -204,26 +204,26 @@ fetchToken()
:class="['ui', {'loading': isLoading}, 'warning', 'button']"
:action="disable"
>
<translate translate-context="Content/Settings/Button.Label/Verb">
<translate >
Disable Subsonic access
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Settings/Title">
<translate >
Disable Subsonic API access?
</translate>
</p>
</template>
<template #modal-content>
<p>
<translate translate-context="Popup/Settings/Paragraph">
<translate >
This will completely disable access to the Subsonic API using from account.
</translate>
</p>
</template>
<template #modal-confirm>
<div>
<translate translate-context="Popup/Settings/Button.Label">
<translate >
Disable access
</translate>
</div>

View File

@ -59,7 +59,7 @@ defineExpose({
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while creating
</translate>
</h4>
@ -74,7 +74,7 @@ defineExpose({
</div>
<div class="ui required field">
<label for="album-title">
<translate translate-context="*/*/*/Noun">Title</translate>
<translate >Title</translate>
</label>
<input
v-model="title"

View File

@ -35,13 +35,13 @@ const albumForm = ref()
<h4 class="header">
<translate
v-if="channel.content_category === 'podcast'"
translate-context="Popup/Channels/Title/Verb"
>
New series
</translate>
<translate
v-else
translate-context="Popup/Channels/Title"
>
New album
</translate>
@ -57,7 +57,7 @@ const albumForm = ref()
</div>
<div class="actions">
<button class="ui basic cancel button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -66,7 +66,7 @@ const albumForm = ref()
:disabled="!submittable"
@click.stop.prevent="albumForm.submit()"
>
<translate translate-context="*/*/Button.Label">
<translate >
Create
</translate>
</button>

View File

@ -49,11 +49,11 @@ watch(() => props.channel, fetchData, { immediate: true })
<label for="album-dropdown">
<translate
v-if="channel && channel.artist && channel.artist.content_category === 'podcast'"
translate-context="*/*/*"
>Series</translate>
<translate
v-else
translate-context="*/*/*"
>Album</translate>
</label>
<select
@ -62,7 +62,7 @@ watch(() => props.channel, fetchData, { immediate: true })
class="ui search normal dropdown"
>
<option value="">
<translate translate-context="*/*/*">
<translate >
None
</translate>
</option>
@ -72,7 +72,7 @@ watch(() => props.channel, fetchData, { immediate: true })
:value="album.id"
>
{{ album.title }} (<translate
translate-context="*/*/*"
:translate-params="{count: album.tracks_count}"
:translate-n="album.tracks_count"
translate-plural="%{ count } tracks"

View File

@ -55,7 +55,7 @@ fetchLicenses()
<template>
<div>
<label for="license-dropdown">
<translate translate-context="Content/*/*/Noun">License</translate>
<translate >License</translate>
</label>
<select
id="license-dropdown"
@ -63,7 +63,7 @@ fetchLicenses()
class="ui search normal dropdown"
>
<option value="">
<translate translate-context="*/*/*">
<translate >
None
</translate>
</option>
@ -86,7 +86,7 @@ fetchLicenses()
target="_blank"
rel="noreferrer noopener"
>
<translate translate-context="Content/*/*">About this license</translate>
<translate >About this license</translate>
</a>
</p>
</div>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Channel } from '~/types'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { useStore } from '~/store'
@ -19,17 +19,17 @@ interface Props {
const emit = defineEmits<Events>()
const props = defineProps<Props>()
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const isSubscribed = computed(() => store.getters['channels/isSubscribed'](props.channel.uuid))
const title = computed(() => isSubscribed.value
? $pgettext('Content/Channel/Button/Verb', 'Unsubscribe')
: $pgettext('Content/Channel/Button/Verb', 'Subscribe')
? t('Unsubscribe')
: t('Subscribe')
)
const message = computed(() => ({
authMessage: $pgettext('Popup/Message/Paragraph', 'You need to be logged in to subscribe to this channel')
authMessage: t('You need to be logged in to subscribe to this channel')
}))
const toggle = async () => {

View File

@ -5,7 +5,7 @@ import type { VueUploadItem } from 'vue-upload-component'
import { computed, ref, reactive, watchEffect, watch } from 'vue'
import { whenever, useCurrentElement } from '@vueuse/core'
import { humanSize } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import axios from 'axios'
@ -52,7 +52,7 @@ const props = withDefaults(defineProps<Props>(), {
channel: null
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const errors = ref([] as string[])
@ -396,7 +396,7 @@ watchEffect(() => {
})
const labels = computed(() => ({
editTitle: $pgettext('Content/*/Button.Label/Verb', 'Edit')
editTitle: t('Edit')
}))
</script>
@ -411,7 +411,7 @@ const labels = computed(() => ({
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while publishing
</translate>
</h4>
@ -426,7 +426,7 @@ const labels = computed(() => ({
</div>
<div :class="['ui', 'required', {hidden: step > 1}, 'field']">
<label for="channel-dropdown">
<translate translate-context="*/*/*">Channel</translate>
<translate >Channel</translate>
</label>
<div
id="channel-dropdown"
@ -449,7 +449,7 @@ const labels = computed(() => ({
<div class="content">
<p>
<i class="copyright icon" />
<translate translate-context="Content/Channels/Popup.Paragraph">
<translate >
Add a license to your upload to ensure some freedoms to your public.
</translate>
</p>
@ -464,7 +464,7 @@ const labels = computed(() => ({
<div class="content">
<p>
<i class="warning icon" />
<translate translate-context="Content/Library/Paragraph">
<translate >
You don't have any space left to upload your files. Please contact the moderators.
</translate>
</p>
@ -477,7 +477,7 @@ const labels = computed(() => ({
>
<p>
<i class="redo icon" />
<translate translate-context="Popup/Channels/Paragraph">
<translate >
You have some draft uploads pending publication.
</translate>
</p>
@ -485,7 +485,7 @@ const labels = computed(() => ({
class="ui basic button"
@click.stop.prevent="includeDraftUploads = false"
>
<translate translate-context="*/*/*">
<translate >
Ignore
</translate>
</button>
@ -493,7 +493,7 @@ const labels = computed(() => ({
class="ui basic button"
@click.stop.prevent="includeDraftUploads = true"
>
<translate translate-context="*/*/*">
<translate >
Resume
</translate>
</button>
@ -547,19 +547,19 @@ const labels = computed(() => ({
<template v-else>
<translate
v-if="file.active"
translate-context="Channels/*/*"
>
Uploading
</translate>
<translate
v-else-if="file.error"
translate-context="Channels/*/*"
>
Errored
</translate>
<translate
v-else
translate-context="Channels/*/*"
>
Pending
</translate>
@ -567,12 +567,12 @@ const labels = computed(() => ({
· {{ parseFloat(file.progress ?? '0') }}%
</template>
· <a @click.stop.prevent="remove(file)">
<translate translate-context="Content/Radio/Button.Label/Verb">Remove</translate>
<translate >Remove</translate>
</a>
<template v-if="file.error">
·
<a @click.stop.prevent="retry(file)">
<translate translate-context="*/*/*">Retry</translate>
<translate >Retry</translate>
</a>
</template>
</div>
@ -592,7 +592,7 @@ const labels = computed(() => ({
<p>
<i class="info icon" />
<translate
translate-context="Content/Library/Paragraph"
:translate-params="{extensions: $store.state.ui.supportedExtensions.join(', ')}"
>
Supported extensions: %{ extensions }
@ -615,13 +615,13 @@ const labels = computed(() => ({
>
<div>
<i class="upload icon" />&nbsp;
<translate translate-context="Content/Channels/Paragraph">
<translate >
Drag and drop your files here or open the browser to upload your files
</translate>
</div>
<div class="ui very small divider" />
<div>
<translate translate-context="*/*/*">
<translate >
Browse
</translate>
</div>

View File

@ -33,7 +33,7 @@ watch(newValues, (values) => emit('update:values', values), { immediate: true })
<div :class="['ui', {loading: isLoading}, 'form']">
<div class="ui required field">
<label for="upload-title">
<translate translate-context="*/*/*/Noun">Title</translate>
<translate >Title</translate>
</label>
<input
v-model="newValues.title"
@ -44,7 +44,7 @@ watch(newValues, (values) => emit('update:values', values), { immediate: true })
v-model="newValues.cover"
@delete="newValues.cover = ''"
>
<translate translate-context="Content/Channel/*">
<translate >
Track Picture
</translate>
</attachment-input>
@ -52,7 +52,7 @@ watch(newValues, (values) => emit('update:values', values), { immediate: true })
<div class="ui two fields">
<div class="ui field">
<label for="upload-tags">
<translate translate-context="*/*/*/Noun">Tags</translate>
<translate >Tags</translate>
</label>
<tags-selector
id="upload-tags"
@ -62,7 +62,7 @@ watch(newValues, (values) => emit('update:values', values), { immediate: true })
</div>
<div class="ui field">
<label for="upload-position">
<translate translate-context="*/*/*/Short, Noun">Position</translate>
<translate >Position</translate>
</label>
<input
v-model="newValues.position"
@ -74,7 +74,7 @@ watch(newValues, (values) => emit('update:values', values), { immediate: true })
</div>
<div class="ui field">
<label for="upload-description">
<translate translate-context="*/*/*">Description</translate>
<translate >Description</translate>
</label>
<content-form
v-model="newValues.description"

View File

@ -5,7 +5,7 @@ import { humanSize } from '~/utils/filters'
import { useRouter } from 'vue-router'
import { useStore } from '~/store'
import { ref, computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
const store = useStore()
const router = useRouter()
@ -13,7 +13,7 @@ router.beforeEach(() => store.commit('channels/showUploadModal', { show: false }
const update = (value: boolean) => store.commit('channels/showUploadModal', { show: value })
const { $npgettext, $gettext } = useGettext()
const { t } = useI18n()
const uploadForm = ref()
@ -29,8 +29,8 @@ const statusInfo = computed(() => {
}
if (statusData.value.totalFiles) {
const msg = $npgettext('*/*/*', '%{ count } file', '%{ count } files', statusData.value.totalFiles)
info.push($gettext(msg, { count: statusData.value.totalFiles }))
const msg = t('no files | %{ count } file | %{ count } files', statusData.value.totalFiles)
info.push(t(msg, { count: statusData.value.totalFiles }))
}
if (statusData.value.progress) {
@ -56,25 +56,25 @@ const isLoading = ref(false)
<h4 class="header">
<translate
v-if="step === 1"
translate-context="Popup/Channels/Title/Verb"
>
Publish audio
</translate>
<translate
v-else-if="step === 2"
translate-context="Popup/Channels/Title"
>
Files to upload
</translate>
<translate
v-else-if="step === 3"
translate-context="Popup/Channels/Title"
>
Upload details
</translate>
<translate
v-else-if="step === 4"
translate-context="Popup/Channels/Title"
>
Processing uploads
</translate>
@ -95,7 +95,7 @@ const isLoading = ref(false)
</template>
<div class="ui very small hidden divider" />
<template v-if="statusData && statusData.quotaStatus">
<translate translate-context="Content/Library/Paragraph">
<translate >
Remaining storage space:
</translate>
{{ humanSize((statusData.quotaStatus.remaining - statusData.uploadedSize) * 1000 * 1000) }}
@ -106,7 +106,7 @@ const isLoading = ref(false)
v-if="step === 1"
class="ui basic cancel button"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -115,7 +115,7 @@ const isLoading = ref(false)
class="ui basic button"
@click.stop.prevent="uploadForm.step -= 1"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Previous step
</translate>
</button>
@ -124,7 +124,7 @@ const isLoading = ref(false)
class="ui basic button"
@click.stop.prevent="uploadForm.step -= 1"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Update
</translate>
</button>
@ -133,7 +133,7 @@ const isLoading = ref(false)
class="ui primary button"
@click.stop.prevent="uploadForm.step += 1"
>
<translate translate-context="*/*/Button.Label">
<translate >
Next step
</translate>
</button>
@ -147,7 +147,7 @@ const isLoading = ref(false)
:disabled="!statusData?.canSubmit || undefined"
@click.prevent.stop="uploadForm.publish"
>
<translate translate-context="*/Channels/Button.Label">
<translate >
Publish
</translate>
</button>
@ -164,7 +164,7 @@ const isLoading = ref(false)
class="basic item"
@click="update(false)"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Finish later
</translate>
</div>
@ -176,7 +176,7 @@ const isLoading = ref(false)
class="ui basic cancel button"
@click="update(false)"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Close
</translate>
</button>

View File

@ -2,7 +2,7 @@
import type { BackendError } from '~/types'
import { ref, computed, reactive, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
@ -41,7 +41,7 @@ const props = withDefaults(defineProps<Props>(), {
customObjects: () => []
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const currentActionName = ref(props.actions[0]?.name ?? null)
const currentAction = computed(() => props.actions.find(action => action.name === currentActionName.value))
@ -123,10 +123,10 @@ const toggleCheck = (event: MouseEvent, id: string, index: number) => {
}
const labels = computed(() => ({
refresh: $pgettext('Content/*/Button.Tooltip/Verb', 'Refresh table content'),
selectAllItems: $pgettext('Content/*/Select/Verb', 'Select all items'),
performAction: $pgettext('Content/*/Button.Label', 'Perform actions'),
selectItem: $pgettext('Content/*/Select/Verb', 'Select')
refresh: t('Refresh table content'),
selectAllItems: t('Select all items'),
performAction: t('Perform actions'),
selectItem: t('Select')
}))
const errors = ref([] as string[])
@ -167,7 +167,7 @@ const launchAction = async () => {
class="right floated"
>
<span v-if="needsRefresh">
<translate translate-context="Content/*/Button.Help text.Paragraph">Content has been updated, click refresh to see up-to-date content</translate>
<translate >Content has been updated, click refresh to see up-to-date content</translate>
</span>
<button
class="ui basic icon button"
@ -185,7 +185,7 @@ const launchAction = async () => {
>
<div class="ui inline fields">
<div class="field">
<label for="actions-select"><translate translate-context="Content/*/*/Noun">Actions</translate></label>
<label for="actions-select"><translate >Actions</translate></label>
<select
id="actions-select"
v-model="currentActionName"
@ -208,14 +208,14 @@ const launchAction = async () => {
:aria-label="labels.performAction"
@confirm="launchAction"
>
<translate translate-context="Content/*/Button.Label/Short, Verb">
<translate >
Go
</translate>
<template #modal-header>
<p>
<translate
key="1"
translate-context="Modal/*/Title"
:translate-n="affectedObjectsCount"
:translate-params="{count: affectedObjectsCount, action: currentActionName}"
translate-plural="Do you want to launch %{ action } on %{ count } elements?"
@ -231,7 +231,7 @@ const launchAction = async () => {
</template>
<translate
v-else
translate-context="Modal/*/Paragraph"
>
This may affect a lot of elements or have irreversible consequences, please double check this is really what you want.
</translate>
@ -239,7 +239,7 @@ const launchAction = async () => {
</template>
<template #modal-confirm>
<div :aria-label="labels.performAction">
<translate translate-context="Modal/*/Button.Label/Short, Verb">
<translate >
Launch
</translate>
</div>
@ -252,7 +252,7 @@ const launchAction = async () => {
:class="['ui', {disabled: checked.length === 0}, {'loading': isLoading}, 'button']"
@click="launchAction"
>
<translate translate-context="Content/*/Button.Label/Short, Verb">
<translate >
Go
</translate>
</button>
@ -260,7 +260,7 @@ const launchAction = async () => {
<div class="count field">
<translate
v-if="selectAll"
translate-context="Content/*/Paragraph"
tag="span"
:translate-n="objectsData.count"
:translate-params="{count: objectsData.count, total: objectsData.count}"
@ -270,7 +270,7 @@ const launchAction = async () => {
</translate>
<translate
v-else
translate-context="Content/*/Paragraph"
tag="span"
:translate-n="checked.length"
:translate-params="{count: checked.length, total: objectsData.count}"
@ -286,7 +286,7 @@ const launchAction = async () => {
>
<translate
key="3"
translate-context="Content/*/Link/Verb"
:translate-n="objectsData.count"
:translate-params="{total: objectsData.count}"
translate-plural="Select all %{ total } elements"
@ -301,7 +301,7 @@ const launchAction = async () => {
>
<translate
key="4"
translate-context="Content/*/Link/Verb"
>Select only current page</translate>
</a>
</template>
@ -313,7 +313,7 @@ const launchAction = async () => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message/Header">
<translate >
Error while applying action
</translate>
</h4>
@ -332,7 +332,7 @@ const launchAction = async () => {
>
<p>
<translate
translate-context="Content/*/Paragraph"
:translate-n="result.updated"
:translate-params="{count: result.updated, action: result.action}"
translate-plural="Action %{ action } was launched successfully on %{ count } elements"

View File

@ -107,7 +107,7 @@ const getAttachmentUrl = (uuid: string) => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Your attachment cannot be saved
</translate>
</h4>
@ -146,7 +146,7 @@ const getAttachmentUrl = (uuid: string) => {
<div class="eleven wide column">
<div class="file-input">
<label :for="attachmentId">
<translate translate-context="*/*/*">Upload New Picture</translate>
<translate >Upload New Picture</translate>
</label>
<input
:id="attachmentId"
@ -161,7 +161,7 @@ const getAttachmentUrl = (uuid: string) => {
</div>
<div class="ui very small hidden divider" />
<p>
<translate translate-context="Content/*/Paragraph">
<translate >
PNG or JPG. Dimensions should be between 1400x1400px and 3000x3000px. Maximum file size allowed is 5MB.
</translate>
</p>
@ -170,7 +170,7 @@ const getAttachmentUrl = (uuid: string) => {
class="ui basic tiny button"
@click.stop.prevent="remove(value as string)"
>
<translate translate-context="Content/Radio/Button.Label/Verb">
<translate >
Remove
</translate>
</button>
@ -179,7 +179,7 @@ const getAttachmentUrl = (uuid: string) => {
class="ui active inverted dimmer"
>
<div class="ui indeterminate text loader">
<translate translate-context="Content/*/*/Noun">
<translate >
Uploading file
</translate>
</div>

View File

@ -22,13 +22,13 @@ const value = useVModel(props, 'modelValue', emit)
>
<translate
v-if="value"
translate-context="*/*/Button,Label"
>
Expand
</translate>
<translate
v-else
translate-context="*/*/Button,Label"
>
Collapse
</translate>

View File

@ -2,7 +2,7 @@
import axios from 'axios'
import { useVModel, watchDebounced, useTextareaAutosize, syncRef } from '@vueuse/core'
import { ref, computed, watchEffect, onMounted, nextTick, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface Events {
(e: 'update:modelValue', value: string): void
@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
required: false
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const { textarea, input } = useTextareaAutosize()
const value = useVModel(props, 'modelValue', emit)
syncRef(value, input)
@ -36,7 +36,7 @@ const preview = ref()
const isLoadingPreview = ref(false)
const labels = computed(() => ({
placeholder: props.placeholder ?? $pgettext('*/Form/Placeholder', 'Write a few words here…')
placeholder: props.placeholder ?? t('Write a few words here…')
}))
const remainingChars = computed(() => props.charLimit - props.modelValue.length)
@ -86,7 +86,7 @@ onMounted(async () => {
:class="[{active: !isPreviewing}, 'item']"
@click.prevent="isPreviewing = false"
>
<translate translate-context="*/Form/Menu.item">
<translate >
Write
</translate>
</button>
@ -94,7 +94,7 @@ onMounted(async () => {
:class="[{active: isPreviewing}, 'item']"
@click.prevent="isPreviewing = true"
>
<translate translate-context="*/Form/Menu.item">
<translate >
Preview
</translate>
</button>
@ -112,7 +112,7 @@ onMounted(async () => {
</div>
</div>
<p v-else-if="!preview">
<translate translate-context="*/Form/Paragraph">
<translate >
Nothing to preview.
</translate>
</p>
@ -141,7 +141,7 @@ onMounted(async () => {
{{ remainingChars }}
</span>
<p>
<translate translate-context="*/Form/Paragraph">
<translate >
Markdown syntax is supported.
</translate>
</p>

View File

@ -22,7 +22,7 @@ const { copy, isSupported: canCopy, copied } = useClipboard({ source: value, cop
v-if="copied"
class="message"
>
<translate translate-context="Content/*/Paragraph">
<translate >
Text copied to clipboard!
</translate>
</p>
@ -39,7 +39,7 @@ const { copy, isSupported: canCopy, copied } = useClipboard({ source: value, cop
@click="copy()"
>
<i class="copy icon" />
<translate translate-context="*/*/Button.Label/Short, Verb">
<translate >
Copy
</translate>
</button>

View File

@ -42,7 +42,7 @@ const confirm = () => {
>
<h4 class="header">
<slot name="modal-header">
<translate translate-context="Modal/*/Title">
<translate >
Do you want to confirm this action?
</translate>
</slot>
@ -54,7 +54,7 @@ const confirm = () => {
</div>
<div class="actions">
<button class="ui basic cancel button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -63,7 +63,7 @@ const confirm = () => {
@click="confirm"
>
<slot name="modal-confirm">
<translate translate-context="Modal/*/Button.Label/Short, Verb">
<translate >
Confirm
</translate>
</slot>

View File

@ -20,12 +20,12 @@ const duration = computed(() => {
<span>
<translate
v-if="duration.hours > 0"
translate-context="Content/*/Paragraph"
:translate-params="duration"
>%{ hours } h %{ minutes } min</translate>
<translate
v-else
translate-context="Content/*/Paragraph"
:translate-params="duration"
>%{ minutes } min</translate>
</span>

View File

@ -19,7 +19,7 @@ withDefaults(defineProps<Props>(), {
<div class="content">
<slot name="title">
<i class="search icon" />
<translate translate-context="Content/*/Paragraph">
<translate >
No results were found.
</translate>
</slot>
@ -32,7 +32,7 @@ withDefaults(defineProps<Props>(), {
class="ui button"
@click="emit('refresh')"
>
<translate translate-context="Content/*/Button.Label/Short, Verb">
<translate >
Refresh
</translate>
</button>

View File

@ -28,11 +28,11 @@ const truncated = computed(() => props.content.slice(0, props.length))
<br>
<translate
v-if="expanded"
translate-context="*/*/Button,Label"
>Show less</translate>
<translate
v-else
translate-context="*/*/Button,Label"
>Show more</translate>
</a>
</div>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useVModel } from '@vueuse/core'
import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface Events {
(e: 'update:modelValue', value: string): void
@ -20,10 +20,10 @@ const props = withDefaults(defineProps<Props>(), {
const value = useVModel(props, 'modelValue', emit)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search…'),
clear: $pgettext('Content/Library/Button.Label', 'Clear')
searchPlaceholder: t('Search…'),
clear: t('Clear')
}))
const search = () => {
@ -42,7 +42,7 @@ const search = () => {
for="search-query"
class="hidden"
>
<translate translate-context="Content/Search/Input.Label/Noun">Search</translate>
<translate >Search</translate>
</label>
<input
id="search-query"

View File

@ -4,7 +4,7 @@ import type { Cover } from '~/types'
import SemanticModal from '~/components/semantic/Modal.vue'
import { ref, computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface Props {
nextRoute: RouteLocationRaw
@ -16,12 +16,12 @@ defineProps<Props>()
const show = ref(false)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
header: $pgettext('Popup/Title/Noun', 'Unauthenticated'),
login: $pgettext('*/*/Button.Label/Verb', 'Log in'),
signup: $pgettext('*/*/Button.Label/Verb', 'Sign up'),
description: $pgettext('Popup/*/Paragraph', "You don't have access!")
header: t('Unauthenticated'),
login: t('Log in'),
signup: t('Sign up'),
description: t("You don't have access!")
}))
</script>

View File

@ -91,19 +91,19 @@ const submit = async () => {
href=""
@click.stop.prevent="showMore = true"
>
<translate translate-context="*/*/Button,Label">Show more</translate>
<translate >Show more</translate>
</a>
<a
v-else
href=""
@click.stop.prevent="showMore = false"
>
<translate translate-context="*/*/Button,Label">Show less</translate>
<translate >Show less</translate>
</a>
</template>
</template>
<p v-else-if="!isUpdating">
<translate translate-context="*/*/Placeholder">
<translate >
No description available
</translate>
</p>
@ -114,7 +114,7 @@ const submit = async () => {
@click="isUpdating = true"
>
<i class="pencil icon" />
<translate translate-context="Content/*/Button.Label/Verb">Edit</translate>
<translate >Edit</translate>
</span>
</template>
<form
@ -128,7 +128,7 @@ const submit = async () => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Channels/Error message.Title">
<translate >
Error while updating description
</translate>
</h4>
@ -149,14 +149,14 @@ const submit = async () => {
class="left floated"
@click.prevent="isUpdating = false"
>
<translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
<translate >Cancel</translate>
</a>
<button
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
type="submit"
:disabled="isLoading"
>
<translate translate-context="Content/Channels/Button.Label/Verb">
<translate >
Update description
</translate>
</button>

View File

@ -1,9 +1,10 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import { SUPPORTED_LOCALES } from '~/init/locale'
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import useThemeList from '~/composables/useThemeList'
import useTheme from '~/composables/useTheme'
import { computed } from 'vue'
interface Events {
(e: 'show:shortcuts-modal'): void
@ -11,26 +12,26 @@ interface Events {
const emit = defineEmits<Events>()
const { $pgettext } = useGettext()
const { t } = useI18n()
const themes = useThemeList()
const { theme } = useTheme()
const labels = computed(() => ({
profile: $pgettext('*/*/*/Noun', 'Profile'),
settings: $pgettext('*/*/*/Noun', 'Settings'),
logout: $pgettext('Sidebar/Login/List item.Link/Verb', 'Log out'),
about: $pgettext('Sidebar/About/List item.Link', 'About'),
shortcuts: $pgettext('*/*/*/Noun', 'Keyboard shortcuts'),
support: $pgettext('Sidebar/*/Listitem.Link', 'Help'),
forum: $pgettext('Sidebar/*/Listitem.Link', 'Forum'),
docs: $pgettext('Sidebar/*/Listitem.Link', 'Documentation'),
language: $pgettext('Footer/Settings/Dropdown.Label/Short, Verb', 'Change language'),
theme: $pgettext('Footer/Settings/Dropdown.Label/Short, Verb', 'Change theme'),
chat: $pgettext('Sidebar/*/Listitem.Link', 'Chat room'),
git: $pgettext('Footer/*/List item.Link', 'Issue tracker'),
login: $pgettext('*/*/Button.Label/Verb', 'Log in'),
signup: $pgettext('*/*/Button.Label/Verb', 'Sign up'),
notifications: $pgettext('*/Notifications/*', 'Notifications')
profile: t('Profile'),
settings: t('Settings'),
logout: t('Log out'),
about: t('About'),
shortcuts: t('Keyboard shortcuts'),
support: t('Help'),
forum: t('Forum'),
docs: t('Documentation'),
language: t('Change language'),
theme: t('Change theme'),
chat: t('Chat room'),
git: t('Issue tracker'),
login: t('Log in'),
signup: t('Sign up'),
notifications: t('Notifications')
}))
</script>
@ -45,11 +46,11 @@ const labels = computed(() => ({
class="menu"
>
<a
v-for="(language, key) in $language.available"
v-for="(language, key) in SUPPORTED_LOCALES"
:key="key"
:class="[{'active': $language.current === key},'item']"
:class="[{'active': $i18n.locale === key},'item']"
:value="key"
@click="$store.dispatch('ui/currentLanguage', key)"
@click="$i18n.locale = key"
>{{ language }}</a>
</div>
</div>

View File

@ -4,7 +4,7 @@ import useThemeList from '~/composables/useThemeList'
import useTheme from '~/composables/useTheme'
import { useVModel } from '@vueuse/core'
import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface Events {
(e: 'update:show', value: boolean): void
@ -24,26 +24,26 @@ const show = useVModel(props, 'show', emit)
const { theme } = useTheme()
const themes = useThemeList()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
header: $pgettext('Popup/Title/Noun', 'Options'),
profile: $pgettext('*/*/*/Noun', 'Profile'),
settings: $pgettext('*/*/*/Noun', 'Settings'),
logout: $pgettext('Sidebar/Login/List item.Link/Verb', 'Log out'),
about: $pgettext('Sidebar/About/List item.Link', 'About'),
shortcuts: $pgettext('*/*/*/Noun', 'Keyboard shortcuts'),
support: $pgettext('Sidebar/*/Listitem.Link', 'Help'),
forum: $pgettext('Sidebar/*/Listitem.Link', 'Forum'),
docs: $pgettext('Sidebar/*/Listitem.Link', 'Documentation'),
help: $pgettext('Sidebar/*/Listitem.Link', 'Help'),
language: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Language'),
theme: $pgettext('Sidebar/Settings/Dropdown.Label/Short, Verb', 'Theme'),
chat: $pgettext('Sidebar/*/Listitem.Link', 'Chat room'),
git: $pgettext('Sidebar/*/List item.Link', 'Issue tracker'),
login: $pgettext('*/*/Button.Label/Verb', 'Log in'),
signup: $pgettext('*/*/Button.Label/Verb', 'Sign up'),
notifications: $pgettext('*/Notifications/*', 'Notifications'),
useOtherInstance: $pgettext('Sidebar/*/List item.Link', 'Use another instance')
header: t('Options'),
profile: t('Profile'),
settings: t('Settings'),
logout: t('Log out'),
about: t('About'),
shortcuts: t('Keyboard shortcuts'),
support: t('Help'),
forum: t('Forum'),
docs: t('Documentation'),
help: t('Help'),
language: t('Language'),
theme: t('Theme'),
chat: t('Chat room'),
git: t('Issue tracker'),
login: t('Log in'),
signup: t('Sign up'),
notifications: t('Notifications'),
useOtherInstance: t('Use another instance')
}))
</script>

View File

@ -5,7 +5,7 @@ import type { OrderingField } from '~/store/ui'
import type { Track } from '~/types'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { sortedUniq } from 'lodash-es'
import { useStore } from '~/store'
@ -96,9 +96,9 @@ onOrderingUpdate(() => {
onMounted(() => $('.ui.dropdown').dropdown())
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/Favorites/Title', 'Your Favorites')
title: t('Your Favorites')
}))
const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value].sort((a, b) => a - b)))
@ -112,7 +112,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<section class="ui vertical center aligned stripe segment">
<div :class="['ui', { 'active': isLoading }, 'inverted', 'dimmer']">
<div class="ui text loader">
<translate translate-context="Content/Favorites/Message">
<translate >
Loading your favorites
</translate>
</div>
@ -126,7 +126,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
translate-plural="%{ count } favorites"
:translate-n="$store.state.favorites.count"
:translate-params="{ count }"
translate-context="Content/Favorites/Title"
>
%{ count } favorite
</translate>
@ -144,7 +144,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="fields">
<div class="field">
<label for="favorites-ordering">
<translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate>
<translate >Ordering</translate>
</label>
<select
id="favorites-ordering"
@ -162,7 +162,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</div>
<div class="field">
<label for="favorites-ordering-direction">
<translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate>
<translate >Order</translate>
</label>
<select
id="favorites-ordering-direction"
@ -170,12 +170,12 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
@ -183,7 +183,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</div>
<div class="field">
<label for="favorites-results">
<translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate>
<translate >Results per page</translate>
</label>
<select
id="favorites-results"
@ -223,7 +223,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="ui icon header">
<i class="broken heart icon" />
<translate
translate-context="Content/Home/Placeholder"
>
No tracks have been added to your favorites yet
</translate>
@ -233,7 +233,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui success labeled icon button"
>
<i class="headphones icon" />
<translate translate-context="Content/*/Verb">
<translate >
Browse the library
</translate>
</router-link>

View File

@ -2,7 +2,7 @@
import type { QueueTrack } from '~/composables/audio/queue'
import type { Track } from '~/types'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import { computed } from 'vue'
@ -18,13 +18,13 @@ const props = withDefaults(defineProps<Props>(), {
border: false
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const isFavorite = computed(() => store.getters['favorites/isFavorite'](props.track.id))
const title = computed(() => isFavorite.value
? $pgettext('Content/Track/Icon.Tooltip/Verb', 'Remove from favorites')
: $pgettext('Content/Track/*/Verb', 'Add to favorites')
? t('Remove from favorites')
: t('Add to favorites')
)
</script>
@ -37,13 +37,13 @@ const title = computed(() => isFavorite.value
<i class="heart icon" />
<translate
v-if="isFavorite"
translate-context="Content/Track/Button.Message"
>
In favorites
</translate>
<translate
v-else
translate-context="Content/Track/*/Verb"
>
Add to favorites
</translate>

View File

@ -80,7 +80,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="small"
>
<h3 class="header">
<translate translate-context="Popup/*/Title">
<translate >
Refreshing object from remote server
</translate>
</h3>
@ -91,12 +91,12 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui message"
>
<h4 class="header">
<translate translate-context="Popup/*/Message.Title">
<translate >
Refresh was skipped
</translate>
</h4>
<p>
<translate translate-context="Popup/*/Message.Content">
<translate >
The remote server answered, but returned data was unsupported by Funkwhale.
</translate>
</p>
@ -106,12 +106,12 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui success message"
>
<h4 class="header">
<translate translate-context="Popup/*/Message.Title">
<translate >
Refresh successful
</translate>
</h4>
<p>
<translate translate-context="Popup/*/Message.Content">
<translate >
Data was refreshed successfully from remote server.
</translate>
</p>
@ -121,12 +121,12 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui error message"
>
<h4 class="header">
<translate translate-context="Popup/*/Message.Title">
<translate >
Refresh error
</translate>
</h4>
<p>
<translate translate-context="Popup/*/Message.Content">
<translate >
An error occurred while trying to refresh data:
</translate>
</p>
@ -134,7 +134,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
<tbody>
<tr>
<td>
<translate translate-context="Popup/Import/Table.Label/Noun">
<translate >
Error type
</translate>
</td>
@ -144,7 +144,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
</tr>
<tr>
<td>
<translate translate-context="Popup/Import/Table.Label/Noun">
<translate >
Error detail
</translate>
</td>
@ -152,49 +152,49 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
<translate
v-if="data.detail.error_code === 'http' && data.detail.status_code"
:translate-params="{status: data.detail.status_code}"
translate-context="*/*/Error"
>
The remote server answered with HTTP %{ status }
</translate>
<translate
v-else-if="['http', 'request'].indexOf(data.detail.error_code) > -1"
translate-context="*/*/Error"
>
An HTTP error occurred while contacting the remote server
</translate>
<translate
v-else-if="data.detail.error_code === 'timeout'"
translate-context="*/*/Error"
>
The remote server didn't respond quickly enough
</translate>
<translate
v-else-if="data.detail.error_code === 'connection'"
translate-context="*/*/Error"
>
Impossible to connect to the remote server
</translate>
<translate
v-else-if="['invalid_json', 'invalid_jsonld', 'missing_jsonld_type'].indexOf(data.detail.error_code) > -1"
translate-context="*/*/Error"
>
The remote server returned invalid JSON or JSON-LD data
</translate>
<translate
v-else-if="data.detail.error_code === 'validation'"
translate-context="*/*/Error"
>
Data returned by the remote server had invalid or missing attributes
</translate>
<translate
v-else-if="data.detail.error_code === 'unhandled'"
translate-context="*/*/Error"
>
Unknown error
</translate>
<translate
v-else
translate-context="*/*/Error"
>
Unknown error
</translate>
@ -209,7 +209,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui active inverted dimmer"
>
<div class="ui text loader">
<translate translate-context="Popup/*/Loading.Title">
<translate >
Requesting a fetch
</translate>
</div>
@ -219,7 +219,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui active inverted dimmer"
>
<div class="ui text loader">
<translate translate-context="Popup/*/Loading.Title">
<translate >
Waiting for result
</translate>
</div>
@ -230,7 +230,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while saving settings
</translate>
</h4>
@ -249,12 +249,12 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui warning message"
>
<h4 class="header">
<translate translate-context="Popup/*/Message.Title">
<translate >
Refresh pending
</translate>
</h4>
<p>
<translate translate-context="Popup/*/Message.Content">
<translate >
The refresh request hasn't been processed in time by our server. It will be processed later.
</translate>
</p>
@ -262,7 +262,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
</div>
<div class="actions">
<button class="ui basic cancel button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Close
</translate>
</button>
@ -271,7 +271,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui confirm success button"
@click.prevent="showModal = false; emit('refresh')"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Close and reload page
</translate>
</button>

View File

@ -64,7 +64,7 @@ fetchData()
v-if="!isLoading && libraries.length === 0"
class="ui subtitle"
>
<translate translate-context="Content/Federation/Paragraph">
<translate >
No matching library.
</translate>
</p>
@ -92,7 +92,7 @@ fetchData()
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
<translate translate-context="*/*/Button,Label">
<translate >
Show more
</translate>
</button>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useClipboard, useVModel } from '@vueuse/core'
import { useStore } from '~/store'
@ -25,10 +25,10 @@ const value = useVModel(props, 'modelValue', emit)
const showPassword = ref(props.defaultShow)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Content/Settings/Button.Tooltip/Verb', 'Show/hide password'),
copy: $pgettext('*/*/Button.Label/Short, Verb', 'Copy')
title: t('Show/hide password'),
copy: t('Copy')
}))
const passwordInputType = computed(() => showPassword.value ? 'text' : 'password')
@ -38,7 +38,7 @@ const { isSupported: canCopy, copy } = useClipboard({ source: value })
const copyPassword = () => {
copy()
store.commit('ui/addMessage', {
content: $pgettext('Content/*/Paragraph', 'Text copied to clipboard!'),
content: t('Text copied to clipboard!'),
date: new Date()
})
}

View File

@ -2,8 +2,8 @@
import type { Track, Album, Artist, Library } from '~/types'
import { momentFormat } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { computed, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { sum } from 'lodash-es'
@ -39,9 +39,9 @@ const isSerie = computed(() => object.value?.artist.content_category === 'podcas
const totalDuration = computed(() => sum((object.value?.tracks ?? []).map(track => track.uploads[0]?.duration ?? 0)))
const publicLibraries = computed(() => libraries.value?.filter(library => library.privacy_level === 'everyone') ?? [])
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('*/*/*', 'Album')
title: t('Album')
}))
const isLoading = ref(false)
@ -179,7 +179,7 @@ const remove = async () => {
<div class="ui hidden very small divider" />
<translate
v-if="isSerie"
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } episodes"
:translate-n="totalTracks"
:translate-params="{count: totalTracks}"
@ -188,7 +188,7 @@ const remove = async () => {
</translate>
<translate
v-else
translate-context="*/*/*"
:translate-params="{count: totalTracks}"
:translate-n="totalTracks"
translate-plural="%{ count } tracks"
@ -267,7 +267,7 @@ const remove = async () => {
<template v-if="totalTracks > 0">
<translate
v-if="isSerie"
translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } episodes"
:translate-n="totalTracks"
:translate-params="{count: totalTracks}"
@ -276,7 +276,7 @@ const remove = async () => {
</translate>
<translate
v-else
translate-context="*/*/*"
:translate-params="{count: totalTracks}"
:translate-n="totalTracks"
translate-plural="%{ count } tracks"
@ -323,7 +323,7 @@ const remove = async () => {
:to="{name: 'library.albums.edit', params: {id: object.id }}"
>
<i class="pencil icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Add a description
</translate>
</router-link>
@ -341,7 +341,7 @@ const remove = async () => {
:to="{name: 'library.albums.edit', params: {id: object.id }}"
>
<i class="pencil icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Add a description
</translate>
</router-link>

View File

@ -62,13 +62,13 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
<h2 class="ui header">
<translate
v-if="isSerie"
translate-context="Content/Channels/*"
>
Episodes
</translate>
<translate
v-else
translate-context="*/*/*"
>
Tracks
</translate>
@ -96,7 +96,6 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
<translate
tag="h3"
:translate-params="{number: tracks[0]?.disc_number ?? index + 1}"
translate-context="Content/Album/"
>
Volume %{ number }
</translate>
@ -138,7 +137,7 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
<template v-if="!artist.channel && !isSerie">
<h2>
<translate translate-context="Content/*/Title/Noun">
<translate >
User libraries
</translate>
</h2>
@ -146,7 +145,7 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
:url="'albums/' + object.id + '/libraries/'"
@loaded="emit('libraries-loaded', $event)"
>
<translate translate-context="Content/Album/Paragraph">
<translate >
This album is present in the following libraries:
</translate>
</library-widget>

View File

@ -2,7 +2,7 @@
import type { Album, Artist, Library } from '~/types'
import { computed, ref } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { getDomain } from '~/utils'
@ -33,9 +33,9 @@ const showEmbedModal = ref(false)
const domain = computed(() => getDomain(props.object.fid))
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
more: $pgettext('*/*/Button.Label/Noun', 'More…')
more: t('More…')
}))
const isEmbedable = computed(() => (props.isChannel && props.artist?.channel?.actor) || props.publicLibraries.length)
@ -53,7 +53,7 @@ const remove = () => emit('remove')
v-model:show="showEmbedModal"
>
<h4 class="header">
<translate translate-context="Popup/Album/Title/Verb">Embed this album on your website</translate>
<translate >Embed this album on your website</translate>
</h4>
<div class="scrolling content">
<div class="description">
@ -66,7 +66,7 @@ const remove = () => emit('remove')
</div>
<div class="actions">
<button class="ui basic deny button">
<translate translate-context="*/*/Button.Label/Verb">Cancel</translate>
<translate >Cancel</translate>
</button>
</div>
</semantic-modal>
@ -86,7 +86,7 @@ const remove = () => emit('remove')
<i class="external icon" />
<translate
:translate-params="{domain: domain}"
translate-context="Content/*/Button.Label/Verb"
>View on %{ domain }</translate>
</a>
@ -97,7 +97,7 @@ const remove = () => emit('remove')
@click="showEmbedModal = !showEmbedModal"
>
<i class="code icon" />
<translate translate-context="Content/*/Button.Label/Verb">Embed</translate>
<translate >Embed</translate>
</div>
<a
v-if="isAlbum && musicbrainzUrl"
@ -107,7 +107,7 @@ const remove = () => emit('remove')
class="basic item"
>
<i class="external icon" />
<translate translate-context="Content/*/*/Clickable, Verb">View on MusicBrainz</translate>
<translate >View on MusicBrainz</translate>
</a>
<a
v-if="!isChannel && isAlbum"
@ -117,7 +117,7 @@ const remove = () => emit('remove')
class="basic item"
>
<i class="external icon" />
<translate translate-context="Content/*/Button.Label/Verb">Search on Discogs</translate>
<translate >Search on Discogs</translate>
</a>
<router-link
v-if="object.is_local"
@ -125,7 +125,7 @@ const remove = () => emit('remove')
class="basic item"
>
<i class="edit icon" />
<translate translate-context="Content/*/Button.Label/Verb">Edit</translate>
<translate >Edit</translate>
</router-link>
<dangerous-button
v-if="artist && $store.state.auth.authenticated && artist.channel && artist.attributed_to.full_username === $store.state.auth.fullUsername"
@ -133,16 +133,16 @@ const remove = () => emit('remove')
@confirm="remove()"
>
<i class="ui trash icon" />
<translate translate-context="*/*/*/Verb">Delete</translate>
<translate >Delete</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Channel/Title">Delete this album?</translate>
<translate >Delete this album?</translate>
</p>
</template>
<template #modal-content>
<div>
<p>
<translate translate-context="Content/Moderation/Paragraph">
<translate >
The album will be deleted, as well as any related files and data. This action is irreversible.
</translate>
</p>
@ -150,7 +150,7 @@ const remove = () => emit('remove')
</template>
<template #modal-confirm>
<p>
<translate translate-context="*/*/*/Verb">Delete</translate>
<translate >Delete</translate>
</p>
</template>
</dangerous-button>
@ -171,7 +171,7 @@ const remove = () => emit('remove')
:to="{name: 'manage.library.albums.detail', params: {id: object.id}}"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link">Open in moderation interface</translate>
<translate >Open in moderation interface</translate>
</router-link>
<a
v-if="$store.state.auth.profile && $store.state.auth.profile?.is_superuser"
@ -181,7 +181,7 @@ const remove = () => emit('remove')
rel="noopener noreferrer"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link/Verb">View in Django's admin</translate>&nbsp;
<translate >View in Django's admin</translate>&nbsp;
</a>
</div>
</button>

View File

@ -24,13 +24,13 @@ const canEdit = store.state.auth.availablePermissions.library
<h2>
<translate
v-if="canEdit"
translate-context="Content/*/Title"
>
Edit this album
</translate>
<translate
v-else
translate-context="Content/*/Title"
>
Suggest an edit on this album
</translate>
@ -39,7 +39,7 @@ const canEdit = store.state.auth.availablePermissions.library
v-if="!object.is_local"
class="ui message"
>
<translate translate-context="Content/*/Message">
<translate >
This object is managed by another server, you cannot edit it.
</translate>
</div>

View File

@ -6,7 +6,7 @@ import type { OrderingField } from '~/store/ui'
import { computed, onMounted, ref, watch } from 'vue'
import { useRouteQuery } from '@vueuse/router'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { syncRef } from '@vueuse/core'
import { sortedUniq } from 'lodash-es'
import { useStore } from '~/store'
@ -108,10 +108,10 @@ onOrderingUpdate(() => {
onMounted(() => $('.ui.dropdown').dropdown())
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Enter album title…'),
title: $pgettext('*/*/*', 'Albums')
searchPlaceholder: t('Enter album title…'),
title: t('Albums')
}))
const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value].sort((a, b) => a - b)))
@ -121,7 +121,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate translate-context="Content/Album/Title">
<translate >
Browsing albums
</translate>
</h2>
@ -132,7 +132,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="fields">
<div class="field">
<label for="albums-search">
<translate translate-context="Content/Search/Input.Label/Noun">Search</translate>
<translate >Search</translate>
</label>
<div class="ui action input">
<input
@ -145,18 +145,18 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<button
class="ui icon button"
type="submit"
:aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"
:aria-label="t('Search')"
>
<i class="search icon" />
</button>
</div>
</div>
<div class="field">
<label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label>
<label for="tags-search"><translate >Tags</translate></label>
<tags-selector v-model="tags" />
</div>
<div class="field">
<label for="album-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="album-ordering"><translate >Ordering</translate></label>
<select
id="album-ordering"
v-model="ordering"
@ -172,26 +172,26 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</select>
</div>
<div class="field">
<label for="album-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="album-ordering-direction"><translate >Ordering direction</translate></label>
<select
id="album-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
</select>
</div>
<div class="field">
<label for="album-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label>
<label for="album-results"><translate >Results per page</translate></label>
<select
id="album-results"
v-model="paginateBy"
@ -234,7 +234,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
>
<div class="ui icon header">
<i class="compact disc icon" />
<translate translate-context="Content/Albums/Placeholder">
<translate >
No results matching your query
</translate>
</div>
@ -244,7 +244,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui success button labeled icon"
>
<i class="upload icon" />
<translate translate-context="Content/*/Verb">
<translate >
Add some music
</translate>
</router-link>

View File

@ -2,7 +2,7 @@
import type { Track, Album, Artist, Library } from '~/types'
import { computed, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { getDomain } from '~/utils'
import { useStore } from '~/store'
@ -57,9 +57,9 @@ const headerStyle = computed(() => cover.value?.urls.original
: ''
)
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('*/*/*', 'Artist')
title: t('Artist')
}))
const isLoading = ref(false)
@ -118,7 +118,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="sub header"
>
<translate
translate-context="Content/Artist/Paragraph"
tag="div"
translate-plural="%{ count } tracks in %{ albumsCount } albums"
:translate-n="totalTracks"
@ -147,7 +147,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="vibrant"
:artist="object"
>
<translate translate-context="Content/Artist/Button.Label/Verb">
<translate >
Play all albums
</translate>
</play-button>
@ -158,7 +158,7 @@ watch(() => props.id, fetchData, { immediate: true })
v-model:show="showEmbedModal"
>
<h4 class="header">
<translate translate-context="Popup/Artist/Title/Verb">
<translate >
Embed this artist work on your website
</translate>
</h4>
@ -172,7 +172,7 @@ watch(() => props.id, fetchData, { immediate: true })
</div>
<div class="actions">
<button class="ui deny button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -183,7 +183,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="ui button"
@click="dropdown.click()"
>
<translate translate-context="*/*/Button.Label/Noun">
<translate >
More
</translate>
</button>
@ -203,7 +203,7 @@ watch(() => props.id, fetchData, { immediate: true })
<i class="external icon" />
<translate
:translate-params="{domain: domain}"
translate-context="Content/*/Button.Label/Verb"
>View on %{ domain }</translate>
</a>
@ -214,7 +214,7 @@ watch(() => props.id, fetchData, { immediate: true })
@click.prevent="showEmbedModal = !showEmbedModal"
>
<i class="code icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Embed
</translate>
</button>
@ -225,7 +225,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item"
>
<i class="wikipedia w icon" />
<translate translate-context="Content/*/Button.Label/Verb">Search on Wikipedia</translate>
<translate >Search on Wikipedia</translate>
</a>
<a
v-if="musicbrainzUrl"
@ -235,7 +235,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item"
>
<i class="external icon" />
<translate translate-context="Content/*/*/Clickable, Verb">View on MusicBrainz</translate>
<translate >View on MusicBrainz</translate>
</a>
<a
:href="discogsUrl"
@ -244,7 +244,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item"
>
<i class="external icon" />
<translate translate-context="Content/*/Button.Label/Verb">Search on Discogs</translate>
<translate >Search on Discogs</translate>
</a>
<router-link
v-if="object.is_local"
@ -252,7 +252,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item"
>
<i class="edit icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Edit
</translate>
</router-link>
@ -274,7 +274,7 @@ watch(() => props.id, fetchData, { immediate: true })
:to="{name: 'manage.library.artists.detail', params: {id: object.id}}"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link">
<translate >
Open in moderation interface
</translate>
</router-link>
@ -286,7 +286,7 @@ watch(() => props.id, fetchData, { immediate: true })
rel="noopener noreferrer"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link/Verb">View in Django's admin</translate>&nbsp;
<translate >View in Django's admin</translate>&nbsp;
</a>
</div>
</button>

View File

@ -65,7 +65,7 @@ const loadMoreAlbums = async () => {
<div class="ui hidden divider" />
<div class="ui message">
<p>
<translate translate-context="Content/Artist/Paragraph">
<translate >
You are currently hiding content related to this artist.
</translate>
</p>
@ -73,7 +73,7 @@ const loadMoreAlbums = async () => {
class="right floated"
:to="{name: 'settings'}"
>
<translate translate-context="Content/Moderation/Link">
<translate >
Review my filters
</translate>
</router-link>
@ -81,7 +81,7 @@ const loadMoreAlbums = async () => {
class="ui basic tiny button"
@click="$store.dispatch('moderation/deleteContentFilter', contentFilter.uuid)"
>
<translate translate-context="Content/Moderation/Button.Label">
<translate >
Remove filter
</translate>
</button>
@ -98,7 +98,7 @@ const loadMoreAlbums = async () => {
class="ui vertical stripe segment"
>
<h2>
<translate translate-context="Content/Artist/Title">
<translate >
Albums by this artist
</translate>
</h2>
@ -115,7 +115,7 @@ const loadMoreAlbums = async () => {
:class="['ui', {loading: isLoadingMoreAlbums}, 'button']"
@click="loadMoreAlbums()"
>
<translate translate-context="Content/*/Button.Label">
<translate >
Load more
</translate>
</button>
@ -132,7 +132,7 @@ const loadMoreAlbums = async () => {
>
<template #header>
<h2>
<translate translate-context="Content/Artist/Title">
<translate >
New tracks by this artist
</translate>
</h2>
@ -142,7 +142,7 @@ const loadMoreAlbums = async () => {
</section>
<section class="ui vertical stripe segment">
<h2>
<translate translate-context="Content/*/Title/Noun">
<translate >
User libraries
</translate>
</h2>
@ -150,7 +150,7 @@ const loadMoreAlbums = async () => {
:url="'artists/' + object.id + '/libraries/'"
@loaded="emit('libraries-loaded', $event)"
>
<translate translate-context="Content/Artist/Paragraph">
<translate >
This artist is present in the following libraries:
</translate>
</library-widget>

View File

@ -24,13 +24,13 @@ const canEdit = store.state.auth.availablePermissions.library
<h2>
<translate
v-if="canEdit"
translate-context="Content/*/Title"
>
Edit this artist
</translate>
<translate
v-else
translate-context="Content/*/Title"
>
Suggest an edit on this artist
</translate>
@ -39,7 +39,7 @@ const canEdit = store.state.auth.availablePermissions.library
v-if="!object.is_local"
class="ui message"
>
<translate translate-context="Content/*/Message">
<translate >
This object is managed by another server, you cannot edit it.
</translate>
</div>

View File

@ -6,7 +6,7 @@ import type { OrderingField } from '~/store/ui'
import { computed, ref, watch, onMounted } from 'vue'
import { useRouteQuery } from '@vueuse/router'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { syncRef } from '@vueuse/core'
import { sortedUniq } from 'lodash-es'
import { useStore } from '~/store'
@ -109,10 +109,10 @@ onOrderingUpdate(() => {
onMounted(() => $('.ui.dropdown').dropdown())
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search…'),
title: $pgettext('*/*/*/Noun', 'Artists')
searchPlaceholder: t('Search…'),
title: t('Artists')
}))
const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value].sort((a, b) => a - b)))
@ -122,7 +122,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate translate-context="Content/Artist/Title">
<translate >
Browsing artists
</translate>
</h2>
@ -133,7 +133,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<div class="fields">
<div class="field">
<label for="artist-search">
<translate translate-context="Content/Search/Input.Label/Noun">Artist name</translate>
<translate >Artist name</translate>
</label>
<div class="ui action input">
<input
@ -146,18 +146,18 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<button
class="ui icon button"
type="submit"
:aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"
:aria-label="t('Search')"
>
<i class="search icon" />
</button>
</div>
</div>
<div class="field">
<label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label>
<label for="tags-search"><translate >Tags</translate></label>
<tags-selector v-model="tags" />
</div>
<div class="field">
<label for="artist-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="artist-ordering"><translate >Ordering</translate></label>
<select
id="artist-ordering"
v-model="ordering"
@ -173,26 +173,26 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
</select>
</div>
<div class="field">
<label for="artist-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="artist-ordering-direction"><translate >Ordering direction</translate></label>
<select
id="artist-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
</select>
</div>
<div class="field">
<label for="artist-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label>
<label for="artist-results"><translate >Results per page</translate></label>
<select
id="artist-results"
v-model="paginateBy"
@ -223,7 +223,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<label
for="exclude-compilation"
class="visually-hidden"
><translate translate-context="Content/Search/Checkbox/Noun">Exclude Compilation Artists</translate></label>
><translate >Exclude Compilation Artists</translate></label>
</div>
</div>
</div>
@ -252,7 +252,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
>
<div class="ui icon header">
<i class="compact disc icon" />
<translate translate-context="Content/Artists/Placeholder">
<translate >
No results matching your query
</translate>
</div>
@ -262,7 +262,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
class="ui success button labeled icon"
>
<i class="upload icon" />
<translate translate-context="Content/*/Verb">
<translate >
Add some music
</translate>
</router-link>

View File

@ -157,7 +157,7 @@ const approve = async (approved: boolean) => {
<h4 class="header">
<router-link :to="detailUrl">
<translate
translate-context="Content/Library/Card/Short"
:translate-params="{id: obj.uuid.substring(0, 8)}"
>
Modification %{ id }
@ -171,7 +171,7 @@ const approve = async (approved: boolean) => {
>
<i class="music icon" />
<translate
translate-context="Content/Library/Card/Short"
:translate-params="{id: obj.target.id, name: obj.target.repr}"
>
Track #%{ id } - %{ name }
@ -186,19 +186,19 @@ const approve = async (approved: boolean) => {
<span class="right floated">
<span v-if="obj.is_approved && obj.is_applied">
<i class="success check icon" />
<translate translate-context="Content/Library/Card/Short">Approved and applied</translate>
<translate >Approved and applied</translate>
</span>
<span v-else-if="obj.is_approved">
<i class="success check icon" />
<translate translate-context="Content/*/*/Short">Approved</translate>
<translate >Approved</translate>
</span>
<span v-else-if="obj.is_approved === null">
<i class="warning hourglass icon" />
<translate translate-context="Content/Admin/*/Noun">Pending review</translate>
<translate >Pending review</translate>
</span>
<span v-else-if="obj.is_approved === false">
<i class="danger x icon" />
<translate translate-context="Content/Library/*/Short">Rejected</translate>
<translate >Rejected</translate>
</span>
</span>
</div>
@ -217,17 +217,17 @@ const approve = async (approved: boolean) => {
<thead>
<tr>
<th>
<translate translate-context="Content/Library/Card.Table.Header/Short">
<translate >
Field
</translate>
</th>
<th>
<translate translate-context="Content/Library/Card.Table.Header/Short">
<translate >
Old value
</translate>
</th>
<th>
<translate translate-context="Content/Library/Card.Table.Header/Short">
<translate >
New value
</translate>
</th>
@ -259,7 +259,7 @@ const approve = async (approved: boolean) => {
</template>
</td>
<td v-else>
<translate translate-context="*/*/*">
<translate >
N/A
</translate>
</td>
@ -319,7 +319,7 @@ const approve = async (approved: boolean) => {
:class="['ui', {loading: isLoading}, 'success', 'basic', 'button']"
@click="approve(true)"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Approve
</translate>
</button>
@ -328,7 +328,7 @@ const approve = async (approved: boolean) => {
:class="['ui', {loading: isLoading}, 'warning', 'basic', 'button']"
@click="approve(false)"
>
<translate translate-context="Content/Library/Button.Label">
<translate >
Reject
</translate>
</button>
@ -337,12 +337,12 @@ const approve = async (approved: boolean) => {
:class="['ui', {loading: isLoading}, 'basic danger button']"
:action="remove"
>
<translate translate-context="*/*/*/Verb">
<translate >
Delete
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Library/Title">
<translate >
Delete this suggestion?
</translate>
</p>
@ -350,7 +350,7 @@ const approve = async (approved: boolean) => {
<template #modal-content>
<div>
<p>
<translate translate-context="Popup/Library/Paragraph">
<translate >
The suggestion will be completely removed, this action is irreversible.
</translate>
</p>
@ -358,7 +358,7 @@ const approve = async (approved: boolean) => {
</template>
<template #modal-confirm>
<p>
<translate translate-context="*/*/*/Verb">
<translate >
Delete
</translate>
</p>

View File

@ -4,7 +4,7 @@ import type { BackendError, License, ReviewState } from '~/types'
import { computed, onMounted, reactive, ref, watchEffect } from 'vue'
import { isEqual, clone } from 'lodash-es'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store'
import axios from 'axios'
@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
licenses: () => []
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const configs = useEditConfigs()
const store = useStore()
@ -46,7 +46,7 @@ const canEdit = computed(() => {
})
const labels = computed(() => ({
summaryPlaceholder: $pgettext('*/*/Placeholder', 'A short summary describing your changes.')
summaryPlaceholder: t('A short summary describing your changes.')
}))
const mutationsUrl = computed(() => props.objectType === 'track'
@ -148,7 +148,7 @@ const resetField = (fieldId: string) => {
<div v-if="submittedMutation">
<div class="ui positive message">
<h4 class="header">
<translate translate-context="Content/Library/Paragraph">
<translate >
Your edit was successfully submitted.
</translate>
</h4>
@ -161,7 +161,7 @@ const resetField = (fieldId: string) => {
class="ui button"
@click.prevent="submittedMutation = null"
>
<translate translate-context="Content/Library/Button.Label">
<translate >
Submit another edit
</translate>
</button>
@ -175,27 +175,27 @@ const resetField = (fieldId: string) => {
>
<div>
<template v-if="showPendingReview">
<translate translate-context="Content/Library/Paragraph">
<translate >
Recent edits awaiting review
</translate>
<button
class="ui tiny basic right floated button"
@click.prevent="showPendingReview = false"
>
<translate translate-context="Content/Library/Button.Label">
<translate >
Show all edits
</translate>
</button>
</template>
<template v-else>
<translate translate-context="Content/Library/Paragraph">
<translate >
Recent edits
</translate>
<button
class="ui tiny basic right floated button"
@click.prevent="showPendingReview = true"
>
<translate translate-context="Content/Library/Button.Label">
<translate >
Restrict to unreviewed edits
</translate>
</button>
@ -203,7 +203,7 @@ const resetField = (fieldId: string) => {
</div>
<template #empty-state>
<empty-state>
<translate translate-context="Content/Library/Paragraph">
<translate >
Suggest a change using the form below.
</translate>
</empty-state>
@ -220,7 +220,7 @@ const resetField = (fieldId: string) => {
class="ui negative message"
>
<h4 class="header">
<translate translate-context="Content/Library/Error message.Title">
<translate >
Error while submitting edit
</translate>
</h4>
@ -237,7 +237,7 @@ const resetField = (fieldId: string) => {
v-if="!canEdit"
class="ui message"
>
<translate translate-context="Content/Library/Paragraph">
<translate >
You don't have the permission to edit this object, but you can suggest changes. Once submitted, suggestions will be reviewed before approval.
</translate>
</div>
@ -268,7 +268,7 @@ const resetField = (fieldId: string) => {
class="ui fluid search dropdown"
>
<option :value="null">
<translate translate-context="*/*/*">
<translate >
N/A
</translate>
</option>
@ -286,7 +286,7 @@ const resetField = (fieldId: string) => {
@click.prevent="values[fieldConfig.id] = null"
>
<i class="x icon" />
<translate translate-context="Content/Library/Button.Label">
<translate >
Clear
</translate>
</button>
@ -325,7 +325,7 @@ const resetField = (fieldId: string) => {
@click.prevent="values[fieldConfig.id] = []"
>
<i class="x icon" />
<translate translate-context="Content/Library/Button.Label">
<translate >
Clear
</translate>
</button>
@ -337,7 +337,7 @@ const resetField = (fieldId: string) => {
@click.prevent="resetField(fieldConfig.id)"
>
<i class="undo icon" />
<translate translate-context="Content/Library/Button.Label">
<translate >
Reset to initial value
</translate>
</button>
@ -345,7 +345,7 @@ const resetField = (fieldId: string) => {
</div>
</template>
<div class="field">
<label for="summary"><translate translate-context="*/*/*">Summary (optional)</translate></label>
<label for="summary"><translate >Summary (optional)</translate></label>
<textarea
id="change-summary"
v-model="summary"
@ -359,7 +359,7 @@ const resetField = (fieldId: string) => {
class="ui left floated button"
:to="{name: 'library.tracks.detail', params: {id: object.id }}"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</router-link>
@ -370,13 +370,13 @@ const resetField = (fieldId: string) => {
>
<translate
v-if="canEdit"
translate-context="Content/Library/Button.Label/Verb"
>
Submit and apply edit
</translate>
<translate
v-else
translate-context="Content/Library/Button.Label/Verb"
>
Submit suggestion
</translate>

View File

@ -5,7 +5,7 @@ import type { VueUploadItem } from 'vue-upload-component'
import { computed, ref, reactive, watch, nextTick } from 'vue'
import { useEventListener, useIntervalFn } from '@vueuse/core'
import { humanSize, truncate } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { sortBy } from 'lodash-es'
import { useStore } from '~/store'
@ -34,7 +34,7 @@ const props = withDefaults(defineProps<Props>(), {
defaultImportReference: ''
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const store = useStore()
const upload = ref()
@ -43,13 +43,12 @@ const supportedExtensions = computed(() => store.state.ui.supportedExtensions)
const labels = computed(() => ({
tooltips: {
denied: $pgettext('Content/Library/Help text', 'Upload denied, ensure the file is not too big and that you have not reached your quota'),
server: $pgettext('Content/Library/Help text', 'Cannot upload this file, ensure it is not too big'),
network: $pgettext('Content/Library/Help text', 'A network error occurred while uploading this file'),
timeout: $pgettext('Content/Library/Help text', 'Upload timeout, please try again'),
retry: $pgettext('*/*/*/Verb', 'Retry'),
extension: $pgettext(
'Content/Library/Help text',
denied: t('Upload denied, ensure the file is not too big and that you have not reached your quota'),
server: t('Cannot upload this file, ensure it is not too big'),
network: t('A network error occurred while uploading this file'),
timeout: t('Upload timeout, please try again'),
retry: t('Retry'),
extension: t(
'Invalid file type, ensure you are uploading an audio file. Supported file extensions are %{ extensions }',
{ extensions: supportedExtensions.value.join(', ') }
)
@ -283,7 +282,7 @@ const retry = (files: Omit<VueUploadItem, 'xhr'>[]) => {
useEventListener(window, 'beforeunload', (event) => {
if (!hasActiveUploads.value) return null
event.preventDefault()
return (event.returnValue = $pgettext('*/*/*', 'This page is asking you to confirm that you want to leave - data you have entered may not be saved.'))
return (event.returnValue = t('This page is asking you to confirm that you want to leave - data you have entered may not be saved.'))
})
</script>
@ -295,7 +294,7 @@ useEventListener(window, 'beforeunload', (event) => {
:class="['item', {active: currentTab === 'uploads'}]"
@click.prevent="currentTab = 'uploads'"
>
<translate translate-context="Content/Library/Tab.Title/Short">Uploading</translate>
<translate >Uploading</translate>
<div
v-if="files.length === 0"
class="ui label"
@ -320,7 +319,7 @@ useEventListener(window, 'beforeunload', (event) => {
:class="['item', {active: currentTab === 'processing'}]"
@click.prevent="currentTab = 'processing'"
>
<translate translate-context="Content/Library/Tab.Title/Short">Processing</translate>
<translate >Processing</translate>
<div
v-if="processableFiles === 0"
class="ui label"
@ -345,7 +344,7 @@ useEventListener(window, 'beforeunload', (event) => {
<div :class="['ui', {loading: isLoadingQuota}, 'container']">
<div :class="['ui', {red: remainingSpace === 0}, {warning: remainingSpace > 0 && remainingSpace <= 50}, 'small', 'statistic']">
<div class="label">
<translate translate-context="Content/Library/Paragraph">
<translate >
Remaining storage space
</translate>
</div>
@ -355,33 +354,33 @@ useEventListener(window, 'beforeunload', (event) => {
</div>
<div class="ui divider" />
<h2 class="ui header">
<translate translate-context="Content/Library/Title/Verb">
<translate >
Upload music from '~/your local storage
</translate>
</h2>
<div class="ui message">
<p>
<translate translate-context="Content/Library/Paragraph">
<translate >
You are about to upload music to your library. Before proceeding, please ensure that:
</translate>
</p>
<ul>
<li v-if="library.privacy_level != 'me'">
<translate translate-context="Content/Library/List item">
<translate >
You are not uploading copyrighted content in a public library, otherwise you may be infringing the law
</translate>
</li>
<li>
<translate translate-context="Content/Library/List item">
<translate >
The music files you are uploading are tagged properly.
</translate>&nbsp;
<a
href="http://picard.musicbrainz.org/"
target="_blank"
><translate translate-context="Content/Library/Link">We recommend using Picard for that purpose.</translate></a>
><translate >We recommend using Picard for that purpose.</translate></a>
</li>
<li>
<translate translate-context="Content/Library/List item">
<translate >
The music files you are uploading are in OGG, Flac, MP3 or AIFF format
</translate>
</li>
@ -401,14 +400,14 @@ useEventListener(window, 'beforeunload', (event) => {
@input-file="inputFile"
>
<i class="upload icon" />&nbsp;
<translate translate-context="Content/Library/Paragraph/Call to action">
<translate >
Click to select files to upload or drag and drop files or directories
</translate>
<br>
<br>
<i>
<translate
translate-context="Content/Library/Paragraph"
:translate-params="{extensions: supportedExtensions.join(', ')}"
>
Supported extensions: %{ extensions }
@ -425,22 +424,22 @@ useEventListener(window, 'beforeunload', (event) => {
<thead>
<tr>
<th class="ten wide">
<translate translate-context="Content/Library/Table.Label">
<translate >
Filename
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Size
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Status
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Actions
</translate>
</th>
@ -454,7 +453,7 @@ useEventListener(window, 'beforeunload', (event) => {
class="ui right floated small basic button"
@click.prevent="retry(retryableFiles)"
>
<translate translate-context="Content/Library/Table">
<translate >
Retry failed uploads
</translate>
</button>
@ -486,7 +485,7 @@ useEventListener(window, 'beforeunload', (event) => {
>
<translate
key="1"
translate-context="Content/Library/Table"
>Uploaded</translate>
</span>
<span
@ -495,7 +494,7 @@ useEventListener(window, 'beforeunload', (event) => {
>
<translate
key="2"
translate-context="Content/Library/Table"
>
Uploading
</translate>
@ -507,7 +506,7 @@ useEventListener(window, 'beforeunload', (event) => {
>
<translate
key="3"
translate-context="Content/Library/*/Short"
>
Pending
</translate>
@ -539,7 +538,7 @@ useEventListener(window, 'beforeunload', (event) => {
</div>
<div class="ui divider" />
<h2 class="ui header">
<translate translate-context="Content/Library/Title/Verb">
<translate >
Import music from your server
</translate>
</h2>
@ -549,7 +548,7 @@ useEventListener(window, 'beforeunload', (event) => {
class="ui negative message"
>
<h3 class="header">
<translate translate-context="Content/*/Error message.Title">
<translate >
Error while launching import
</translate>
</h3>
@ -570,17 +569,17 @@ useEventListener(window, 'beforeunload', (event) => {
/>
<template v-if="fsStatus && fsStatus.import">
<h3 class="ui header">
<translate translate-context="Content/Library/Title/Verb">
<translate >
Import status
</translate>
</h3>
<p v-if="fsStatus.import.reference !== importReference">
<translate translate-context="Content/Library/Paragraph">
<translate >
Results of your previous import:
</translate>
</p>
<p v-else>
<translate translate-context="Content/Library/Paragraph">
<translate >
Results of your import:
</translate>
</p>
@ -590,7 +589,7 @@ useEventListener(window, 'beforeunload', (event) => {
class="ui button"
@click="cancelFsScan"
>
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>

View File

@ -42,7 +42,7 @@ const handleClick = (entry: FSEntry) => {
class="ui button"
@click.prevent="emit('import')"
>
<translate translate-context="Content/Library/Button/Verb">
<translate >
Import
</translate>
</button>

View File

@ -15,7 +15,7 @@ defineProps<Props>()
class="ui active dimmer"
>
<div class="ui text loader">
<translate translate-context="Content/Library/Paragraph">
<translate >
Import hasn't started yet
</translate>
</div>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { ref, computed } from 'vue'
import axios from 'axios'
@ -24,9 +24,9 @@ const artists = ref([])
const logger = useLogger()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('Head/Home/Title', 'Library')
title: t('Library')
}))
const isLoading = ref(false)
@ -67,7 +67,7 @@ fetchData()
:websocket-handlers="['Listen']"
>
<template #title>
<translate translate-context="Content/Home/Title">
<translate >
Recently listened
</translate>
</template>
@ -79,7 +79,7 @@ fetchData()
:filters="{scope: scope, ordering: '-creation_date'}"
>
<template #title>
<translate translate-context="Content/Home/Title">
<translate >
Recently favorited
</translate>
</template>
@ -91,7 +91,7 @@ fetchData()
:filters="{scope: scope, playable: true, ordering: '-modification_date'}"
>
<template #title>
<translate translate-context="*/*/*">
<translate >
Playlists
</translate>
</template>
@ -103,7 +103,7 @@ fetchData()
<div class="column">
<album-widget :filters="{scope: scope, playable: true, ordering: '-creation_date'}">
<template #title>
<translate translate-context="Content/Home/Title">
<translate >
Recently added
</translate>
</template>
@ -112,7 +112,7 @@ fetchData()
</div>
<template v-if="scope === 'all'">
<h3 class="ui header">
<translate translate-context="*/*/*">
<translate >
New channels
</translate>
</h3>

View File

@ -3,7 +3,7 @@ import type { Upload } from '~/types'
import SemanticModal from '~/components/semantic/Modal.vue'
import { useVModel } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
interface ErrorEntry {
key: string
@ -44,7 +44,7 @@ const getErrors = (details: object): ErrorEntry[] => {
return errors
}
const { $pgettext } = useGettext()
const { t } = useI18n()
const getErrorData = (upload: Upload) => {
const payload = upload.import_details ?? { error_code: '', detail: {} }
@ -58,11 +58,11 @@ const getErrorData = (upload: Upload) => {
supportUrl: 'https://forum.funkwhale.audio/t/support',
documentationUrl: `https://docs.funkwhale.audio/users/upload.html#${errorCode}`,
label: errorCode === 'invalid_metadata'
? $pgettext('Popup/Import/Error.Label', 'Invalid metadata')
: $pgettext('*/*/Error', 'Unknown error'),
? t('Invalid metadata')
: t('Unknown error'),
detail: errorCode === 'invalid_metadata'
? $pgettext('Popup/Import/Error.Label', 'The metadata included in the file is invalid or some mandatory fields are missing.')
: $pgettext('Popup/Import/Error.Label', 'An unknown error occurred'),
? t('The metadata included in the file is invalid or some mandatory fields are missing.')
: t('An unknown error occurred'),
errorRows: errorCode === 'invalid_metadata'
? getErrors(payload.detail ?? {})
: [],
@ -77,7 +77,7 @@ const getErrorData = (upload: Upload) => {
<template>
<semantic-modal v-model:show="show">
<h4 class="header">
<translate translate-context="Popup/Import/Title">
<translate >
Import detail
</translate>
</h4>
@ -90,7 +90,7 @@ const getErrorData = (upload: Upload) => {
v-if="upload.import_status === 'pending'"
class="ui message"
>
<translate translate-context="Popup/Import/Message">
<translate >
Upload is still pending and will soon be processed by the server.
</translate>
</div>
@ -98,7 +98,7 @@ const getErrorData = (upload: Upload) => {
v-if="upload.import_status === 'finished'"
class="ui success message"
>
<translate translate-context="Popup/Import/Message">
<translate >
Upload was successfully processed by the server.
</translate>
</div>
@ -107,7 +107,7 @@ const getErrorData = (upload: Upload) => {
role="alert"
class="ui warning message"
>
<translate translate-context="Popup/Import/Message">
<translate >
Upload was skipped because a similar one is already available in one of your libraries.
</translate>
</div>
@ -115,7 +115,7 @@ const getErrorData = (upload: Upload) => {
v-if="upload.import_status === 'errored'"
class="ui error message"
>
<translate translate-context="Popup/Import/Message">
<translate >
An error occurred during upload processing. You will find more information below.
</translate>
</div>
@ -124,7 +124,7 @@ const getErrorData = (upload: Upload) => {
<tbody>
<tr>
<td>
<translate translate-context="Popup/Import/Table.Label/Noun">
<translate >
Error type
</translate>
</td>
@ -134,7 +134,7 @@ const getErrorData = (upload: Upload) => {
</tr>
<tr>
<td>
<translate translate-context="Popup/Import/Table.Label/Noun">
<translate >
Error detail
</translate>
</td>
@ -152,7 +152,7 @@ const getErrorData = (upload: Upload) => {
</tr>
<tr>
<td>
<translate translate-context="Footer/*/Link">
<translate >
Getting help
</translate>
</td>
@ -163,7 +163,7 @@ const getErrorData = (upload: Upload) => {
:href="getErrorData(upload).documentationUrl"
target="_blank"
>
<translate translate-context="Popup/Import/Table.Label/Value">Read our documentation for this error</translate>
<translate >Read our documentation for this error</translate>
</a>
</li>
<li>
@ -171,7 +171,7 @@ const getErrorData = (upload: Upload) => {
:href="getErrorData(upload).supportUrl"
target="_blank"
>
<translate translate-context="Popup/Import/Table.Label/Value">Open a support thread (include the debug information below in your message)</translate>
<translate >Open a support thread (include the debug information below in your message)</translate>
</a>
</li>
</ul>
@ -179,7 +179,7 @@ const getErrorData = (upload: Upload) => {
</tr>
<tr>
<td>
<translate translate-context="Popup/Import/Table.Label/Noun">
<translate >
Debug information
</translate>
</td>
@ -200,7 +200,7 @@ const getErrorData = (upload: Upload) => {
</div>
<div class="actions">
<button class="ui deny button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Close
</translate>
</button>

View File

@ -6,7 +6,7 @@ import type { OrderingField } from '~/store/ui'
import { computed, ref, watch, onMounted } from 'vue'
import { useRouteQuery } from '@vueuse/router'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { syncRef } from '@vueuse/core'
import { sortedUniq } from 'lodash-es'
import { useStore } from '~/store'
@ -110,10 +110,10 @@ onOrderingUpdate(() => {
onMounted(() => $('.ui.dropdown').dropdown())
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search…'),
title: $pgettext('*/*/*/Noun', 'Podcasts')
searchPlaceholder: t('Search…'),
title: t('Podcasts')
}))
const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value].sort((a, b) => a - b)))
@ -123,7 +123,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate translate-context="Content/Podcasts/Title">
<translate >
Browsing podcasts
</translate>
</h2>
@ -134,7 +134,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<div class="fields">
<div class="field">
<label for="artist-search">
<translate translate-context="Content/Search/Input.Label/Noun">Podcast title</translate>
<translate >Podcast title</translate>
</label>
<div class="ui action input">
<input
@ -147,18 +147,18 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<button
class="ui icon button"
type="submit"
:aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"
:aria-label="t('Search')"
>
<i class="search icon" />
</button>
</div>
</div>
<div class="field">
<label for="tags-search"><translate translate-context="*/*/*/Noun">Tags</translate></label>
<label for="tags-search"><translate >Tags</translate></label>
<tags-selector v-model="tags" />
</div>
<div class="field">
<label for="artist-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="artist-ordering"><translate >Ordering</translate></label>
<select
id="artist-ordering"
v-model="ordering"
@ -174,26 +174,26 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
</select>
</div>
<div class="field">
<label for="artist-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="artist-ordering-direction"><translate >Ordering direction</translate></label>
<select
id="artist-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
</select>
</div>
<div class="field">
<label for="artist-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label>
<label for="artist-results"><translate >Results per page</translate></label>
<select
id="artist-results"
v-model="paginateBy"
@ -234,7 +234,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
>
<div class="ui icon header">
<i class="podcast icon" />
<translate translate-context="Content/Artists/Placeholder">
<translate >
No results matching your query
</translate>
</div>
@ -244,7 +244,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
class="ui success button labeled icon"
>
<i class="upload icon" />
<translate translate-context="Content/*/Verb">
<translate >
Create a Channel
</translate>
</router-link>
@ -255,7 +255,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
<div class="actions">
<a @click.stop.prevent="showSubscribeModal = true">
<i class="plus icon" />
<translate translate-context="Content/Profile/Button">Subscribe to feed</translate>
<translate >Subscribe to feed</translate>
</a>
</div>
</h1>
@ -275,7 +275,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
:fullscreen="false"
>
<h2 class="header">
<translate translate-context="*/*/*/Noun">
<translate >
Subscription
</translate>
</h2>
@ -293,7 +293,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
</div>
<div class="actions">
<button class="ui basic deny button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -303,7 +303,7 @@ const paginateOptions = computed(() => sortedUniq([12, 30, 50, paginateBy.value]
class="ui primary button"
>
<i class="bookmark icon" />
<translate translate-context="*/*/*/Verb">
<translate >
Subscribe
</translate>
</button>

View File

@ -6,7 +6,7 @@ import type { OrderingField } from '~/store/ui'
import { computed, onMounted, ref, watch } from 'vue'
import { useRouteQuery } from '@vueuse/router'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { syncRef } from '@vueuse/core'
import { sortedUniq } from 'lodash-es'
import { useStore } from '~/store'
@ -99,10 +99,10 @@ onOrderingUpdate(() => {
onMounted(() => $('.ui.dropdown').dropdown())
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Enter a radio name…'),
title: $pgettext('*/*/*', 'Radios')
searchPlaceholder: t('Enter a radio name…'),
title: t('Radios')
}))
const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value].sort((a, b) => a - b)))
@ -112,14 +112,14 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<main v-title="labels.title">
<section class="ui vertical stripe segment">
<h2 class="ui header">
<translate translate-context="Content/Radio/Title">
<translate >
Browsing radios
</translate>
</h2>
<div class="ui hidden divider" />
<div class="ui row">
<h3 class="ui header">
<translate translate-context="Content/Radio/Title">
<translate >
Instance radios
</translate>
</h3>
@ -144,7 +144,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="ui hidden divider" />
<h3 class="ui header">
<translate translate-context="Content/Radio/Title">
<translate >
User radios
</translate>
</h3>
@ -153,7 +153,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui success button"
to="/library/radios/build"
>
<translate translate-context="Content/Radio/Button.Label/Verb">
<translate >
Create your own radio
</translate>
</router-link>
@ -164,7 +164,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
>
<div class="fields">
<div class="field">
<label for="radios-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label>
<label for="radios-search"><translate >Search</translate></label>
<div class="ui action input">
<input
id="radios-search"
@ -176,14 +176,14 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<button
class="ui icon button"
type="submit"
:aria-label="$pgettext('Content/Search/Input.Label/Noun', 'Search')"
:aria-label="t('Search')"
>
<i class="search icon" />
</button>
</div>
</div>
<div class="field">
<label for="radios-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="radios-ordering"><translate >Ordering</translate></label>
<select
id="radios-ordering"
v-model="ordering"
@ -199,26 +199,26 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</select>
</div>
<div class="field">
<label for="radios-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Order</translate></label>
<label for="radios-ordering-direction"><translate >Order</translate></label>
<select
id="radios-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
</select>
</div>
<div class="field">
<label for="radios-results"><translate translate-context="Content/Search/Dropdown.Label/Noun">Results per page</translate></label>
<label for="radios-results"><translate >Results per page</translate></label>
<select
id="radios-results"
v-model="paginateBy"
@ -242,7 +242,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
>
<div class="ui icon header">
<i class="feed icon" />
<translate translate-context="Content/Radios/Placeholder">
<translate >
No results matching your query
</translate>
</div>
@ -252,7 +252,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui success button labeled icon"
>
<i class="rss icon" />
<translate translate-context="Content/*/Verb">
<translate >
Create a radio
</translate>
</router-link>

View File

@ -36,7 +36,7 @@ const labels = computed(() => ({
:to="{name: 'manage.library.tags.detail', params: {id: id}}"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link">
<translate >
Open in moderation interface
</translate>
</router-link>
@ -50,7 +50,7 @@ const labels = computed(() => ({
>
<template #title>
<router-link :to="{name: 'library.artists.browse', query: {tag: id}}">
<translate translate-context="*/*/*/Noun">
<translate >
Artists
</translate>
</router-link>
@ -59,7 +59,7 @@ const labels = computed(() => ({
<div class="ui hidden divider" />
<div class="ui hidden divider" />
<h3 class="ui header">
<translate translate-context="*/*/*">
<translate >
Channels
</translate>
</h3>
@ -79,7 +79,7 @@ const labels = computed(() => ({
>
<template #title>
<router-link :to="{name: 'library.albums.browse', query: {tag: id}}">
<translate translate-context="*/*/*">
<translate >
Albums
</translate>
</router-link>
@ -97,7 +97,7 @@ const labels = computed(() => ({
:filters="{playable: true, ordering: '-creation_date', tag: id}"
>
<template #title>
<translate translate-context="*/*/*">
<translate >
Tracks
</translate>
</template>

View File

@ -99,7 +99,7 @@ onMounted(async () => {
class="search"
>
<div class="default text">
<translate translate-context="*/Dropdown/Placeholder/Verb">
<translate >
Search
</translate>
</div>

View File

@ -3,7 +3,7 @@ import type { Track, Artist, Library } from '~/types'
import { momentFormat } from '~/utils/filters'
import { computed, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { getDomain } from '~/utils'
import { useStore } from '~/store'
@ -67,8 +67,7 @@ const attributedToUrl = computed(() => router.resolve({
const escapeHtml = (unsafe: string) => document.createTextNode(unsafe).textContent ?? ''
const subtitle = computed(() => {
if (track.value?.attributed_to) {
return $pgettext(
'Content/Track/Paragraph',
return t(
'Uploaded by <a class="internal" href="%{ uploaderUrl }">%{ uploader }</a> on <time title="%{ date }" datetime="%{ date }">%{ prettyDate }</time>',
{
uploaderUrl: attributedToUrl.value,
@ -79,8 +78,7 @@ const subtitle = computed(() => {
)
}
return $pgettext(
'Content/Track/Paragraph',
return t(
'Uploaded on <time title="%{ date }" datetime="%{ date }">%{ prettyDate }</time>',
{
date: escapeHtml(track.value?.creation_date ?? ''),
@ -89,11 +87,11 @@ const subtitle = computed(() => {
)
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
title: $pgettext('*/*/*/Noun', 'Track'),
download: $pgettext('Content/Track/Link/Verb', 'Download'),
more: $pgettext('*/*/Button.Label/Noun', 'More…')
title: t('Track'),
download: t('Download'),
more: t('More…')
}))
const isLoading = ref(false)
@ -157,7 +155,7 @@ const remove = async () => {
class="vibrant"
:track="track"
>
<translate translate-context="*/Queue/Button.Label/Short, Verb">
<translate >
Play
</translate>
</play-button>
@ -189,7 +187,7 @@ const remove = async () => {
v-model:show="showEmbedModal"
>
<h4 class="header">
<translate translate-context="Popup/Track/Title">
<translate >
Embed this track on your website
</translate>
</h4>
@ -203,7 +201,7 @@ const remove = async () => {
</div>
<div class="actions">
<button class="ui basic deny button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -228,7 +226,7 @@ const remove = async () => {
<i class="external icon" />
<translate
:translate-params="{domain: domain}"
translate-context="Content/*/Button.Label/Verb"
>View on %{ domain }</translate>
</a>
<div
@ -238,7 +236,7 @@ const remove = async () => {
@click="showEmbedModal = !showEmbedModal"
>
<i class="code icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Embed
</translate>
</div>
@ -249,7 +247,7 @@ const remove = async () => {
class="basic item"
>
<i class="wikipedia w icon" />
<translate translate-context="Content/*/Button.Label/Verb">Search on Wikipedia</translate>
<translate >Search on Wikipedia</translate>
</a>
<a
v-if="discogsUrl"
@ -259,7 +257,7 @@ const remove = async () => {
class="basic item"
>
<i class="external icon" />
<translate translate-context="Content/*/Button.Label/Verb">Search on Discogs</translate>
<translate >Search on Discogs</translate>
</a>
<router-link
v-if="track.is_local"
@ -267,7 +265,7 @@ const remove = async () => {
class="basic item"
>
<i class="edit icon" />
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Edit
</translate>
</router-link>
@ -277,12 +275,12 @@ const remove = async () => {
@confirm="remove()"
>
<i class="ui trash icon" />
<translate translate-context="*/*/*/Verb">
<translate >
Delete
</translate>
<template #modal-header>
<p>
<translate translate-context="Popup/Channel/Title">
<translate >
Delete this track?
</translate>
</p>
@ -290,7 +288,7 @@ const remove = async () => {
<template #modal-content>
<div>
<p>
<translate translate-context="Content/Moderation/Paragraph">
<translate >
The track will be deleted, as well as any related files and data. This action is irreversible.
</translate>
</p>
@ -298,7 +296,7 @@ const remove = async () => {
</template>
<template #modal-confirm>
<p>
<translate translate-context="*/*/*/Verb">
<translate >
Delete
</translate>
</p>
@ -321,7 +319,7 @@ const remove = async () => {
:to="{name: 'manage.library.tracks.detail', params: {id: track.id}}"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link">
<translate >
Open in moderation interface
</translate>
</router-link>
@ -333,7 +331,7 @@ const remove = async () => {
rel="noopener noreferrer"
>
<i class="wrench icon" />
<translate translate-context="Content/Moderation/Link/Verb">View in Django's admin</translate>&nbsp;
<translate >View in Django's admin</translate>&nbsp;
</a>
</div>
</button>

View File

@ -78,13 +78,13 @@ watchEffect(() => {
<h3 class="ui header">
<translate
v-if="track.artist?.content_category === 'music'"
translate-context="Content/*/*"
>
Track Details
</translate>
<translate
v-else
translate-context="Content/*/*"
>
Episode Details
</translate>
@ -93,7 +93,7 @@ watchEffect(() => {
<tbody>
<tr>
<td>
<translate translate-context="Content/*/*">
<translate >
Duration
</translate>
</td>
@ -103,7 +103,7 @@ watchEffect(() => {
</template>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -111,7 +111,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/*/*/Noun">
<translate >
Size
</translate>
</td>
@ -121,7 +121,7 @@ watchEffect(() => {
</template>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -129,7 +129,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/*/*/Noun">
<translate >
Codec
</translate>
</td>
@ -139,7 +139,7 @@ watchEffect(() => {
</template>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -147,7 +147,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/Track/*/Noun">
<translate >
Bitrate
</translate>
</td>
@ -157,7 +157,7 @@ watchEffect(() => {
</template>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -165,7 +165,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/*/*">
<translate >
Downloads
</translate>
</td>
@ -188,7 +188,7 @@ watchEffect(() => {
:can-update="false"
/>
<h2 class="ui header">
<translate translate-context="Content/*/*">
<translate >
Release Details
</translate>
</h2>
@ -196,7 +196,7 @@ watchEffect(() => {
<tbody>
<tr>
<td>
<translate translate-context="*/*/*/Noun">
<translate >
Artist
</translate>
</td>
@ -210,13 +210,13 @@ watchEffect(() => {
<td>
<translate
v-if="track.album.artist.content_category === 'music'"
translate-context="*/*/*/Noun"
>
Album
</translate>
<translate
v-else
translate-context="*/*/*"
>
Serie
</translate>
@ -229,7 +229,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="*/*/*">
<translate >
Year
</translate>
</td>
@ -238,7 +238,7 @@ watchEffect(() => {
{{ momentFormat(new Date(track.album.release_date), 'Y') }}
</template>
<template v-else>
<translate translate-context="*/*/*">
<translate >
N/A
</translate>
</template>
@ -246,7 +246,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/Track/*/Noun">
<translate >
Copyright
</translate>
</td>
@ -256,7 +256,7 @@ watchEffect(() => {
:title="track.copyright"
>{{ truncate(track.copyright, 50) }}</span>
<template v-else>
<translate translate-context="*/*/*">
<translate >
N/A
</translate>
</template>
@ -264,7 +264,7 @@ watchEffect(() => {
</tr>
<tr>
<td>
<translate translate-context="Content/*/*/Noun">
<translate >
License
</translate>
</td>
@ -277,7 +277,7 @@ watchEffect(() => {
>{{ license.name }}</a>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -285,7 +285,7 @@ watchEffect(() => {
</tr>
<tr v-if="!track.is_local">
<td>
<translate translate-context="Content/*/*/Noun">
<translate >
URL
</translate>
</td>
@ -308,10 +308,10 @@ watchEffect(() => {
rel="noreferrer noopener"
>
<i class="external icon" />
<translate translate-context="Content/*/*/Clickable, Verb">View on MusicBrainz</translate>
<translate >View on MusicBrainz</translate>
</a>
<h2 class="ui header">
<translate translate-context="Content/*/Title/Noun">
<translate >
Related Playlists
</translate>
</h2>
@ -321,7 +321,7 @@ watchEffect(() => {
/>
<h2 class="ui header">
<translate translate-context="Content/*/Title/Noun">
<translate >
Related Libraries
</translate>
</h2>
@ -329,7 +329,7 @@ watchEffect(() => {
:url="`tracks/${track.id}/libraries/`"
@loaded="emit('libraries-loaded', $event)"
>
<translate translate-context="Content/Track/Paragraph">
<translate >
This track is present in the following libraries:
</translate>
</library-widget>

View File

@ -46,13 +46,13 @@ fetchLicenses()
<h2>
<translate
v-if="canEdit"
translate-context="Content/*/Title"
>
Edit this track
</translate>
<translate
key="2"
translate-context="Content/*/Title"
>
Suggest an edit on this track
</translate>
@ -61,7 +61,7 @@ fetchLicenses()
v-if="!object.is_local"
class="ui message"
>
<translate translate-context="Content/*/Message">
<translate >
This object is managed by another server, you cannot edit it.
</translate>
</div>

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref, reactive, watch, watchEffect, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
import $ from 'jquery'
@ -51,14 +51,14 @@ const props = withDefaults(defineProps<Props>(), {
id: 0
})
const { $pgettext } = useGettext()
const { t } = useI18n()
const router = useRouter()
const labels = computed(() => ({
title: $pgettext('Head/Radio/Title', 'Radio Builder'),
title: t('Radio Builder'),
placeholder: {
description: $pgettext('Content/Radio/Input.Placeholder', 'My awesome description'),
name: $pgettext('Content/Radio/Input.Placeholder', 'My awesome radio')
description: t('My awesome description'),
name: t('My awesome radio')
}
}))
@ -203,12 +203,12 @@ onMounted(() => {
<div>
<section>
<h2 class="ui header">
<translate translate-context="Content/Radio/Title">
<translate >
Builder
</translate>
</h2>
<p>
<translate translate-context="Content/Radio/Paragraph">
<translate >
You can use this interface to build your own custom radio, which will play tracks according to your criteria.
</translate>
</p>
@ -219,12 +219,12 @@ onMounted(() => {
>
<h4 class="header">
<template v-if="radioName">
<translate translate-context="Content/Radio/Message">
<translate >
Radio updated
</translate>
</template>
<template v-else>
<translate translate-context="Content/Radio/Message">
<translate >
Radio created
</translate>
</template>
@ -232,7 +232,7 @@ onMounted(() => {
</div>
<div class="">
<div class="field">
<label for="name"><translate translate-context="Content/Radio/Input.Label/Noun">Radio name</translate></label>
<label for="name"><translate >Radio name</translate></label>
<input
id="name"
v-model="radioName"
@ -242,7 +242,7 @@ onMounted(() => {
>
</div>
<div class="field">
<label for="description"><translate translate-context="*/*/*/Noun">Description</translate></label>
<label for="description"><translate >Description</translate></label>
<textarea
id="description"
v-model="radioDesc"
@ -257,7 +257,7 @@ onMounted(() => {
v-model="isPublic"
type="checkbox"
>
<label for="public"><translate translate-context="Content/Radio/Checkbox.Label/Verb">Display publicly</translate></label>
<label for="public"><translate >Display publicly</translate></label>
</div>
<div class="ui hidden divider" />
<button
@ -265,7 +265,7 @@ onMounted(() => {
:class="['ui', 'success', {loading: isLoading}, 'button']"
@click="save"
>
<translate translate-context="Content/*/Button.Label/Verb">
<translate >
Save
</translate>
</button>
@ -281,14 +281,14 @@ onMounted(() => {
<label
id="radioFilterLabel"
for="radio-filters"
><translate translate-context="Content/Radio/Paragraph">Add filters to customize your radio</translate></label>
><translate >Add filters to customize your radio</translate></label>
<select
id="radio-filters"
v-model="currentFilterType"
class="ui dropdown"
>
<option value="">
<translate translate-context="Content/Radio/Dropdown.Placeholder/Verb">
<translate >
Select a filter
</translate>
</option>
@ -306,7 +306,7 @@ onMounted(() => {
class="ui button"
@click="add"
>
<translate translate-context="Content/Radio/Button.Label/Verb">
<translate >
Add filter
</translate>
</button>
@ -319,27 +319,27 @@ onMounted(() => {
<thead>
<tr>
<th class="two wide">
<translate translate-context="Content/Radio/Table.Label/Noun">
<translate >
Filter name
</translate>
</th>
<th class="one wide">
<translate translate-context="Content/Radio/Table.Label/Verb">
<translate >
Exclude
</translate>
</th>
<th class="six wide">
<translate translate-context="Content/Radio/Table.Label/Verb (Value is a List of Parameters)">
<translate >
Config
</translate>
</th>
<th class="five wide">
<translate translate-context="Content/Radio/Table.Label/Noun (Value is a number of Tracks)">
<translate >
Candidates
</translate>
</th>
<th class="two wide">
<translate translate-context="Content/*/*/Noun">
<translate >
Actions
</translate>
</th>
@ -363,7 +363,7 @@ onMounted(() => {
class="ui header"
:translate-n="checkResult.candidates.count"
translate-plural="%{ count } tracks matching combined filters"
translate-context="Content/Radio/Table.Paragraph/Short"
>
%{ count } track matching combined filters
</h3>

View File

@ -124,7 +124,7 @@ watch(exclude, fetchCandidates)
for="exclude-filter"
class="visually-hidden"
>
<translate translate-context="Popup/Radio/Title/Noun">Exclude</translate>
<translate >Exclude</translate>
</label>
</div>
</td>
@ -180,7 +180,7 @@ watch(exclude, fetchCandidates)
v-model:show="showCandidadesModal"
>
<h4 class="header">
<translate translate-context="Popup/Radio/Title/Noun">
<translate >
Tracks matching filter
</translate>
</h4>
@ -194,7 +194,7 @@ watch(exclude, fetchCandidates)
</div>
<div class="actions">
<button class="ui deny button">
<translate translate-context="*/*/Button.Label/Verb">
<translate >
Cancel
</translate>
</button>
@ -206,7 +206,7 @@ watch(exclude, fetchCandidates)
class="ui danger button"
@click="$emit('delete', index)"
>
<translate translate-context="Content/Radio/Button.Label/Verb">
<translate >
Remove
</translate>
</button>

View File

@ -5,7 +5,7 @@ import type { RouteRecordName } from 'vue-router'
import type { OrderingField } from '~/store/ui'
import { computed, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
@ -80,10 +80,10 @@ onOrderingUpdate(fetchData)
fetchData()
const sharedLabels = useSharedLabels()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search by domain, name, account…'),
openModeration: $pgettext('Content/Moderation/Verb', 'Open in moderation interface')
searchPlaceholder: t('Search by domain, name, account…'),
openModeration: t('Open in moderation interface')
}))
</script>
@ -92,7 +92,7 @@ const labels = computed(() => ({
<div class="ui inline form">
<div class="fields">
<div class="ui six wide field">
<label for="channel-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label>
<label for="channel-search"><translate >Search</translate></label>
<form @submit.prevent="query = search.value">
<input
id="channel-search"
@ -105,7 +105,7 @@ const labels = computed(() => ({
</form>
</div>
<div class="field">
<label for="channel-category"><translate translate-context="*/*/*">Category</translate></label>
<label for="channel-category"><translate >Category</translate></label>
<select
id="channel-category"
class="ui dropdown"
@ -113,7 +113,7 @@ const labels = computed(() => ({
@change="addSearchToken('category', ($event.target as HTMLSelectElement).value)"
>
<option value="">
<translate translate-context="Content/*/Dropdown">
<translate >
All
</translate>
</option>
@ -129,7 +129,7 @@ const labels = computed(() => ({
</select>
</div>
<div class="field">
<label for="channel-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="channel-ordering"><translate >Ordering</translate></label>
<select
id="channel-ordering"
v-model="ordering"
@ -145,19 +145,19 @@ const labels = computed(() => ({
</select>
</div>
<div class="field">
<label for="channel-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="channel-ordering-direction"><translate >Ordering direction</translate></label>
<select
id="channel-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
@ -182,32 +182,32 @@ const labels = computed(() => ({
>
<template #header-cells>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Name
</translate>
</th>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Account
</translate>
</th>
<th>
<translate translate-context="Content/Moderation/*/Noun">
<translate >
Domain
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Albums
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Tracks
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Creation date
</translate>
</th>
@ -250,7 +250,7 @@ const labels = computed(() => ({
@click.prevent="addSearchToken('domain', scope.obj.attributed_to.domain)"
>
<i class="home icon" />
<translate translate-context="Content/Moderation/*/Short, Noun">Local</translate>
<translate >Local</translate>
</a>
</td>
<td>
@ -276,7 +276,7 @@ const labels = computed(() => ({
<span v-if="result && result.results.length > 0">
<translate
translate-context="Content/*/Paragraph"
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}"
>
Showing results %{ start }-%{ end } on %{ total }

View File

@ -5,7 +5,7 @@ import type { RouteRecordName } from 'vue-router'
import type { OrderingField } from '~/store/ui'
import { computed, ref, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
@ -49,13 +49,13 @@ const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
['name', 'name']
]
const { $pgettext } = useGettext()
const { t } = useI18n()
const actionFilters = computed(() => ({ q: query.value, ...props.filters }))
const actions = computed(() => [
{
name: 'delete',
label: $pgettext('*/*/*/Verb', 'Delete'),
confirmationMessage: $pgettext('Popup/*/Paragraph', 'The selected albums will be removed, as well as associated tracks, uploads, favorites and listening history. This action is irreversible.'),
label: t('Delete'),
confirmationMessage: t('The selected albums will be removed, as well as associated tracks, uploads, favorites and listening history. This action is irreversible.'),
isDangerous: true,
allowAll: false,
confirmColor: 'danger'
@ -94,8 +94,8 @@ fetchData()
const sharedLabels = useSharedLabels()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search by domain, title, artist, MusicBrainz ID…'),
openModeration: $pgettext('Content/Moderation/Verb', 'Open in moderation interface')
searchPlaceholder: t('Search by domain, title, artist, MusicBrainz ID…'),
openModeration: t('Open in moderation interface')
}))
</script>
@ -104,7 +104,7 @@ const labels = computed(() => ({
<div class="ui inline form">
<div class="fields">
<div class="ui six wide field">
<label for="albums-search"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label>
<label for="albums-search"><translate >Search</translate></label>
<form @submit.prevent="query = search.value">
<input
id="albums-search"
@ -117,7 +117,7 @@ const labels = computed(() => ({
</form>
</div>
<div class="field">
<label for="albums-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="albums-ordering"><translate >Ordering</translate></label>
<select
id="albums-ordering"
v-model="ordering"
@ -133,18 +133,18 @@ const labels = computed(() => ({
</select>
</div>
<div class="field">
<label for="albums-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="albums-ordering-direction"><translate >Ordering direction</translate></label>
<select
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
@ -169,32 +169,32 @@ const labels = computed(() => ({
>
<template #header-cells>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Title
</translate>
</th>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Artist
</translate>
</th>
<th>
<translate translate-context="Content/Moderation/*/Noun">
<translate >
Domain
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Tracks
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Release date
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Creation date
</translate>
</th>
@ -238,7 +238,7 @@ const labels = computed(() => ({
@click.prevent="addSearchToken('domain', scope.obj.domain)"
>
<i class="home icon" />
<translate translate-context="Content/Moderation/*/Short, Noun">Local</translate>
<translate >Local</translate>
</a>
</td>
<td>
@ -251,7 +251,7 @@ const labels = computed(() => ({
/>
<translate
v-else
translate-context="*/*/*"
>
N/A
</translate>
@ -273,7 +273,7 @@ const labels = computed(() => ({
<span v-if="result && result.results.length > 0">
<translate
translate-context="Content/*/Paragraph"
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}"
>
Showing results %{ start }-%{ end } on %{ total }

View File

@ -5,7 +5,7 @@ import type { RouteRecordName } from 'vue-router'
import type { OrderingField } from '~/store/ui'
import { ref, computed, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useI18n } from 'vue-i18n'
import axios from 'axios'
@ -52,8 +52,8 @@ const actionFilters = computed(() => ({ q: query.value, ...props.filters }))
const actions = computed(() => [
{
name: 'delete',
label: $pgettext('*/*/*/Verb', 'Delete'),
confirmationMessage: $pgettext('Popup/*/Paragraph', 'The selected artist will be removed, as well as associated uploads, tracks, albums, favorites and listening history. This action is irreversible.'),
label: t('Delete'),
confirmationMessage: t('The selected artist will be removed, as well as associated uploads, tracks, albums, favorites and listening history. This action is irreversible.'),
isDangerous: true,
allowAll: false,
confirmColor: 'danger'
@ -91,9 +91,9 @@ onOrderingUpdate(fetchData)
fetchData()
const sharedLabels = useSharedLabels()
const { $pgettext } = useGettext()
const { t } = useI18n()
const labels = computed(() => ({
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search by domain, name, MusicBrainz ID…')
searchPlaceholder: t('Search by domain, name, MusicBrainz ID…')
}))
const getUrl = (artist: { channel?: number; id: number }) => {
@ -108,7 +108,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
<div class="ui inline form">
<div class="fields">
<div class="ui six wide field">
<label for="artists-serarch"><translate translate-context="Content/Search/Input.Label/Noun">Search</translate></label>
<label for="artists-serarch"><translate >Search</translate></label>
<form @submit.prevent="query = search.value">
<input
id="artists-search"
@ -121,7 +121,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
</form>
</div>
<div class="field">
<label for="artists-category"><translate translate-context="*/*/*">Category</translate></label>
<label for="artists-category"><translate >Category</translate></label>
<select
id="artists-category"
class="ui dropdown"
@ -129,7 +129,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
@change="addSearchToken('category', ($event.target as HTMLSelectElement).value)"
>
<option value="">
<translate translate-context="Content/*/Dropdown">
<translate >
All
</translate>
</option>
@ -145,7 +145,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
</select>
</div>
<div class="field">
<label for="artists-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label>
<label for="artists-ordering"><translate >Ordering</translate></label>
<select
id="artists-ordering"
v-model="ordering"
@ -161,19 +161,19 @@ const getUrl = (artist: { channel?: number; id: number }) => {
</select>
</div>
<div class="field">
<label for="artists-ordering-direction"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering direction</translate></label>
<label for="artists-ordering-direction"><translate >Ordering direction</translate></label>
<select
id="artists-ordering-direction"
v-model="orderingDirection"
class="ui dropdown"
>
<option value="+">
<translate translate-context="Content/Search/Dropdown">
<translate >
Ascending
</translate>
</option>
<option value="-">
<translate translate-context="Content/Search/Dropdown">
<translate >
Descending
</translate>
</option>
@ -198,27 +198,27 @@ const getUrl = (artist: { channel?: number; id: number }) => {
>
<template #header-cells>
<th>
<translate translate-context="*/*/*/Noun">
<translate >
Name
</translate>
</th>
<th>
<translate translate-context="Content/Moderation/*/Noun">
<translate >
Domain
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Albums
</translate>
</th>
<th>
<translate translate-context="*/*/*">
<translate >
Tracks
</translate>
</th>
<th>
<translate translate-context="Content/*/*/Noun">
<translate >
Creation date
</translate>
</th>
@ -250,7 +250,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
@click.prevent="addSearchToken('domain', scope.obj.domain)"
>
<i class="home icon" />
<translate translate-context="Content/Moderation/*/Short, Noun">Local</translate>
<translate >Local</translate>
</a>
</td>
<td>
@ -276,7 +276,7 @@ const getUrl = (artist: { channel?: number; id: number }) => {
<span v-if="result && result.results.length > 0">
<translate
translate-context="Content/*/Paragraph"
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}"
>
Showing results %{ start }-%{ end } on %{ total }

Some files were not shown because too many files have changed in this diff Show More