Start to clean up structure

This commit is contained in:
Ciarán Ainsworth 2022-09-18 23:12:39 +00:00 committed by Kasper Seweryn
parent 58df446539
commit 9e34fbc47e
23 changed files with 772 additions and 582 deletions

View File

@ -67,10 +67,10 @@ const headerStyle = computed(() => {
<div class="column" />
</div>
<h2 class="header">
{{ $t('components.About.funkwhaleHeader') }}
{{ $t('components.About.header.funkwhale') }}
</h2>
<p>
{{ $t('components.About.funkwhaleDescription') }}
{{ $t('components.About.description.funkwhale') }}
</p>
</div>
</div>
@ -84,14 +84,14 @@ const headerStyle = computed(() => {
class="signup-form content"
>
<h3 class="header">
{{ $t('components.About.signupHeader') }}
{{ $t('components.About.header.signup') }}
</h3>
<template v-if="openRegistrations">
<p>
{{ $t('components.About.signupDescription') }}
{{ $t('components.About.description.signup') }}
</p>
<p v-if="defaultUploadQuota">
{{ $t('components.About.quotaDescription', {quota: defaultUploadQuota}) }}
{{ $t('components.About.description.quota', {quota: defaultUploadQuota}) }}
</p>
<signup-form
button-classes="success"
@ -100,7 +100,7 @@ const headerStyle = computed(() => {
</template>
<div v-else>
<p>
{{ $t('components.About.registrationsClosedHelp') }}
{{ $t('components.About.help.closedRegistrations') }}
</p>
<a
@ -108,7 +108,7 @@ const headerStyle = computed(() => {
rel="noopener"
href="https://funkwhale.audio/#get-started"
>
{{ $t('components.About.findOtherPod') }}
{{ $t('components.About.link.findOtherPod') }}
&nbsp;<i class="external alternate icon" />
</a>
</div>
@ -118,13 +118,13 @@ const headerStyle = computed(() => {
class="signup-form content"
>
<h3 class="header">
{{ $t('components.About.signupHeader') }}
{{ $t('components.About.header.signup') }}
<div class="ui positive message">
<div class="header">
{{ $t('components.About.alreadyLoggedIn') }}
{{ $t('components.About.message.loggedIn') }}
</div>
<p>
{{ $t('components.About.greetingMessage', {username: $store.state.auth.username}) }}
{{ $t('components.About.message.greeting', {username: $store.state.auth.username}) }}
</p>
</div>
</h3>
@ -145,7 +145,7 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
{{ $t('components.About.aboutPodHeader') }}
{{ $t('components.About.header.aboutPod') }}
</h3>
<div
v-if="shortDescription"
@ -154,7 +154,7 @@ const headerStyle = computed(() => {
{{ shortDescription }}
</div>
<p v-else>
{{ $t('components.About.noDescription') }}
{{ $t('components.About.placeholder.noDescription') }}
</p>
<template v-if="stats">
@ -164,14 +164,14 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.About.activeUsers', {users: stats.users}) }}
{{ $t('components.About.stat.activeUsers', {users: stats.users}) }}
</span>
</div>
<div class="column">
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.About.hoursOfMusic', {hours: stats.hours}) }}
{{ $t('components.About.stat.hoursOfMusic', {hours: stats.hours}) }}
</span>
</div>
</div>
@ -182,7 +182,7 @@ const headerStyle = computed(() => {
to="/about/pod"
class="ui fluid basic secondary button"
>
{{ $t('components.About.learnMoreLink') }}
{{ $t('components.About.link.learnMore') }}
</router-link>
</div>
</div>
@ -201,10 +201,10 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
{{ $t('components.About.publicContentHeader') }}
{{ $t('components.About.header.publicContent') }}
</h3>
<p>
{{ $t('components.About.publicContentDescription') }}
{{ $t('components.About.description.publicContent') }}
</p>
</div>
</router-link>
@ -218,11 +218,11 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
{{ $t('components.About.findOtherPod') }}
{{ $t('components.About.link.findOtherPod') }}
&nbsp;<i class="external alternate icon" />
</h3>
<p>
{{ $t('components.About.publicContentDescription') }}
{{ $t('components.About.description.publicContent') }}
</p>
</div>
</a>
@ -236,11 +236,11 @@ const headerStyle = computed(() => {
id="description"
class="ui header"
>
{{ $t('components.About.findAppHeader') }}
{{ $t('components.About.header.findApp') }}
&nbsp;<i class="external alternate icon" />
</h3>
<p>
{{ $t('components.About.findAppDescription') }}
{{ $t('components.About.description.findApp') }}
</p>
</div>
</a>
@ -250,7 +250,7 @@ const headerStyle = computed(() => {
to="/about/pod"
class="ui right floated basic secondary button"
>
{{ $t('components.About.aboutPodHeader') }}
{{ $t('components.About.header.aboutPod') }}
<i class="icon arrow right" />
</router-link>
</div>

View File

@ -99,32 +99,32 @@ const headerStyle = computed(() => {
to="/about/pod"
class="item"
>
{{ $t('components.AboutPod.aboutPod') }}
{{ $t('components.AboutPod.link.about') }}
</router-link>
<router-link
to="/about/pod#rules"
class="item"
>
{{ $t('components.AboutPod.rules') }}
{{ $t('components.AboutPod.link.rules') }}
</router-link>
<router-link
to="/about/pod#terms"
class="item"
>
{{ $t('components.AboutPod.termsAndPrivacy') }}
{{ $t('components.AboutPod.link.terms') }}
</router-link>
<router-link
to="/about/pod#features"
class="item"
>
{{ $t('components.AboutPod.features') }}
{{ $t('components.AboutPod.link.features') }}
</router-link>
<router-link
v-if="stats"
to="/about/pod#statistics"
class="item"
>
{{ $t('components.AboutPod.statistics') }}
{{ $t('components.AboutPod.link.statistics') }}
</router-link>
</div>
</div>
@ -134,49 +134,49 @@ const headerStyle = computed(() => {
id="description about-this-pod"
class="ui header"
>
{{ $t('components.AboutPod.aboutPod') }}
{{ $t('components.AboutPod.header.about') }}
</h2>
<sanitized-html
v-if="longDescription"
:html="longDescription"
/>
<p v-else>
{{ $t('components.AboutPod.noDescription') }}
{{ $t('components.AboutPod.placeholder.noDescription') }}
</p>
<h3
id="rules"
class="ui header"
>
{{ $t('components.AboutPod.rules') }}
{{ $t('components.AboutPod.header.rules') }}
</h3>
<sanitized-html
v-if="rules"
:html="rules"
/>
<p v-else>
{{ $t('components.AboutPod.noRules') }}
{{ $t('components.AboutPod.placeholder.noRules') }}
</p>
<h3
id="terms"
class="ui header"
>
{{ $t('components.AboutPod.termsAndPrivacy') }}
{{ $t('components.AboutPod.header.terms') }}
</h3>
<sanitized-html
v-if="terms"
:html="terms"
/>
<p v-else>
{{ $t('components.AboutPod.noTerms') }}
{{ $t('components.AboutPod.placeholder.noTerms') }}
</p>
<h3
id="features"
class="header"
>
{{ $t('components.AboutPod.features') }}
{{ $t('components.AboutPod.header.features') }}
</h3>
<div class="features-container ui two column stackable grid">
<div class="column">
@ -184,7 +184,7 @@ const headerStyle = computed(() => {
<tbody>
<tr>
<td>
{{ $t('components.AboutPod.funkwhaleVersion') }}
{{ $t('components.AboutPod.feature.version') }}
</td>
<td
v-if="version"
@ -205,7 +205,7 @@ const headerStyle = computed(() => {
</tr>
<tr>
<td>
{{ $t('components.AboutPod.federation') }}
{{ $t('components.AboutPod.feature.federation') }}
</td>
<td
v-if="federationEnabled"
@ -213,7 +213,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
{{ $t('components.AboutPod.enabled') }}
{{ $t('components.AboutPod.feature.status.enabled') }}
</span>
</td>
<td
@ -222,13 +222,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
{{ $t('components.AboutPod.disabled') }}
{{ $t('components.AboutPod.feature.status.disabled') }}
</span>
</td>
</tr>
<tr>
<td>
{{ $t('components.AboutPod.allowList') }}
{{ $t('components.AboutPod.feature.allowList') }}
</td>
<td
v-if="allowListEnabled"
@ -236,7 +236,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
{{ $t('components.AboutPod.enabled') }}
{{ $t('components.AboutPod.feature.status.enabled') }}
</span>
</td>
<td
@ -245,7 +245,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
{{ $t('components.AboutPod.disabled') }}
{{ $t('components.AboutPod.feature.status.disabled') }}
</span>
</td>
</tr>
@ -257,7 +257,7 @@ const headerStyle = computed(() => {
<tbody>
<tr>
<td>
{{ $t('components.AboutPod.anonymousAccess') }}
{{ $t('components.AboutPod.feature.anonymousAccess') }}
</td>
<td
v-if="anonymousCanListen"
@ -265,7 +265,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
{{ $t('components.AboutPod.enabled') }}
{{ $t('components.AboutPod.feature.status.enabled') }}
</span>
</td>
<td
@ -274,13 +274,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
{{ $t('components.AboutPod.disabled') }}
{{ $t('components.AboutPod.feature.status.disabled') }}
</span>
</td>
</tr>
<tr>
<td>
{{ $t('components.AboutPod.registrations') }}
{{ $t('components.AboutPod.feature.registrations') }}
</td>
<td
v-if="openRegistrations"
@ -288,7 +288,7 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="check icon" />
{{ $t('components.AboutPod.open') }}
{{ $t('components.AboutPod.feature.status.open') }}
</span>
</td>
<td
@ -297,13 +297,13 @@ const headerStyle = computed(() => {
>
<span class="features-status ui text">
<i class="x icon" />
{{ $t('components.AboutPod.closed') }}
{{ $t('components.AboutPod.feature.status.closed') }}
</span>
</td>
</tr>
<tr>
<td>
{{ $t('components.AboutPod.uploadQuota') }}
{{ $t('components.AboutPod.feature.quota') }}
</td>
<td
v-if="defaultUploadQuota"
@ -332,7 +332,7 @@ const headerStyle = computed(() => {
id="statistics"
class="header"
>
{{ $t('components.AboutPod.statistics') }}
{{ $t('components.AboutPod.header.statistics') }}
</h3>
<div class="statistics-container">
<div
@ -342,7 +342,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.hoursOfMusic', {hours: stats.hours}) }}
{{ $t('components.AboutPod.stat.hoursOfMusic', {hours: stats.hours}) }}
</span>
</div>
<div
@ -352,7 +352,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.artists.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.artistsCount', {artists: stats.artists}) }}
{{ $t('components.AboutPod.stat.artistsCount', {artists: stats.artists}) }}
</span>
</div>
<div
@ -362,7 +362,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.albums.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.albumsCount', {albums: stats.albums}) }}
{{ $t('components.AboutPod.stat.albumsCount', {albums: stats.albums}) }}
</span>
</div>
<div
@ -372,7 +372,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.tracks.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.tracksCount', {tracks: stats.tracks}) }}
{{ $t('components.AboutPod.stat.tracksCount', {tracks: stats.tracks}) }}
</span>
</div>
<div
@ -382,7 +382,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.activeUsers', {users: stats.users}) }}
{{ $t('components.AboutPod.stat.activeUsers', {users: stats.users}) }}
</span>
</div>
<div
@ -392,7 +392,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.listenings.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br>
{{ $t('components.AboutPod.listeningsCount', {listenings: stats.listenings}) }}
{{ $t('components.AboutPod.stat.listeningsCount', {listenings: stats.listenings}) }}
</span>
</div>
</div>
@ -403,13 +403,13 @@ const headerStyle = computed(() => {
id="contact"
class="ui header"
>
{{ $t('components.AboutPod.contactHeader') }}
{{ $t('components.AboutPod.header.contact') }}
</h3>
<a
v-if="contactEmail"
:href="`mailto:${contactEmail}`"
>
{{ $t('components.AboutPod.contactEmail', {contactEmail: contactEmail}) }}
{{ $t('components.AboutPod.message.contact', {contactEmail: contactEmail}) }}
</a>
</template>
@ -420,7 +420,7 @@ const headerStyle = computed(() => {
class="ui left floated basic secondary button"
>
<i class="icon arrow left" />
{{ $t('components.AboutPod.introductionLink') }}
{{ $t('components.AboutPod.link.introduction') }}
</router-link>
</div>
</div>

View File

@ -71,7 +71,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="segment-content">
<h1 class="ui center aligned large header">
<span>
{{ $t('components.Home.welcomeMessage', {podName: podName}) }}
{{ $t('components.Home.header.welcome', {podName: podName}) }}
</span>
<div
v-if="shortDescription"
@ -86,7 +86,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="ten wide column">
<h2 class="header">
{{ $t('components.Home.aboutPod') }}
{{ $t('components.Home.header.about') }}
</h2>
<div
id="pod"
@ -95,7 +95,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="eight wide column">
<p v-if="!longDescription">
{{ $t('components.Home.noDescription') }}
{{ $t('components.Home.placeholder.noDescription') }}
</p>
<template v-if="longDescription || rules">
<sanitized-html
@ -118,7 +118,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link"
:to="{name: 'about'}"
>
{{ $t('components.Home.learnMore') }}
{{ $t('components.Home.link.learnMore') }}
</router-link>
</div>
</div>
@ -133,7 +133,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link"
:to="{name: 'about', hash: '#rules'}"
>
{{ $t('components.Home.serverRules') }}
{{ $t('components.Home.link.rules') }}
</router-link>
</div>
</div>
@ -143,20 +143,20 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="eight wide column">
<template v-if="stats">
<h3 class="sub header">
{{ $t('components.Home.statistics') }}
{{ $t('components.Home.header.statistics') }}
</h3>
<p>
<i class="user icon" />
{{ $t('components.Home.activeUsers', {users: stats.users}) }}
{{ $t('components.Home.stat.activeUsers', {users: stats.users}) }}
</p>
<p>
<i class="music icon" />
{{ $t('components.Home.hoursOfMusic', {hours: stats.hours}) }}
{{ $t('components.Home.stat.hoursOfMusic', {hours: stats.hours}) }}
</p>
</template>
<template v-if="contactEmail">
<h3 class="sub header">
{{ $t('components.Home.contactHeader') }}
{{ $t('components.Home.header.contact') }}
</h3>
<i class="at icon" />
<a :href="`mailto:${contactEmail}`">{{ contactEmail }}</a>
@ -179,13 +179,13 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid">
<div class="four wide column">
<h3 class="header">
{{ $t('components.Home.aboutFunkwhale') }}
{{ $t('components.Home.header.aboutFunkwhale') }}
</h3>
<p>
{{ $t('components.Home.funkwhaleDescription') }}
{{ $t('components.Home.description.funkwhale.paragraph1') }}
</p>
<p>
{{ $t('components.Home.funkwhaleAddendum') }}
{{ $t('components.Home.description.funkwhale.paragraph2') }}
</p>
<a
target="_blank"
@ -193,12 +193,12 @@ whenever(() => store.state.auth.authenticated, () => {
href="https://funkwhale.audio"
>
<i class="external alternate icon" />
{{ $t('components.Home.websiteLink') }}
{{ $t('components.Home.link.funkwhale') }}
</a>
</div>
<div class="four wide column">
<h3 class="header">
{{ $t('components.Home.loginHeader') }}
{{ $t('components.Home.header.login') }}
</h3>
<login-form
button-classes="success"
@ -208,14 +208,14 @@ whenever(() => store.state.auth.authenticated, () => {
</div>
<div class="four wide column">
<h3 class="header">
{{ $t('components.Home.signupHeader') }}
{{ $t('components.Home.header.signup') }}
</h3>
<template v-if="openRegistrations">
<p>
{{ $t('components.Home.signupDescription') }}
{{ $t('components.Home.description.signup') }}
</p>
<p v-if="defaultUploadQuota">
{{ $t('components.Home.uploadQuota', { quota: humanSize(defaultUploadQuota * 1000 * 1000) }) }}
{{ $t('components.Home.description.quota', { quota: humanSize(defaultUploadQuota * 1000 * 1000) }) }}
</p>
<signup-form
button-classes="success"
@ -224,7 +224,7 @@ whenever(() => store.state.auth.authenticated, () => {
</template>
<div v-else>
<p>
{{ $t('components.Home.registrationsClosed') }}
{{ $t('components.Home.help.registrationsClosed') }}
</p>
<a
target="_blank"
@ -232,14 +232,14 @@ whenever(() => store.state.auth.authenticated, () => {
href="https://funkwhale.audio/#get-started"
>
<i class="external alternate icon" />
{{ $t('components.Home.findOtherPod') }}
{{ $t('components.Home.link.findOtherPod') }}
</a>
</div>
</div>
<div class="four wide column">
<h3 class="header">
{{ $t('components.Home.usefulLinks') }}
{{ $t('components.Home.header.links') }}
</h3>
<div class="ui relaxed list">
<div class="item">
@ -250,10 +250,10 @@ whenever(() => store.state.auth.authenticated, () => {
class="header"
to="/library"
>
{{ $t('components.Home.browsePublicContent') }}
{{ $t('components.Home.link.publicContent.label') }}
</router-link>
<div class="description">
{{ $t('components.Home.publicContentDescription') }}
{{ $t('components.Home.link.publicContent.description') }}
</div>
</div>
</div>
@ -266,10 +266,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank"
rel="noopener"
>
{{ $t('components.Home.mobileApps') }}
{{ $t('components.Home.link.mobileApps.label') }}
</a>
<div class="description">
{{ $t('components.Home.mobileAppsDescription') }}
{{ $t('components.Home.link.mobileApps.description') }}
</div>
</div>
</div>
@ -282,10 +282,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank"
rel="noopener"
>
{{ $t('components.Home.userGuides') }}
{{ $t('components.Home.link.userGuides.label') }}
</a>
<div class="description">
{{ $t('components.Home.userGuidesDescription') }}
{{ $t('components.Home.link.userGuides.description') }}
</div>
</div>
</div>
@ -302,16 +302,16 @@ whenever(() => store.state.auth.authenticated, () => {
:limit="10"
>
<template #title>
{{ $t('components.Home.recentlyAddedLabel') }}
{{ $t('components.Home.header.newAlbums') }}
</template>
<router-link to="/library">
{{ $t('components.Home.viewMore') }}
{{ $t('components.Home.link.viewMore') }}
<div class="ui hidden divider" />
</router-link>
</album-widget>
<div class="ui hidden section divider" />
<h3 class="ui header">
{{ $t('components.Home.newChannelsLabel') }}
{{ $t('components.Home.header.newChannels') }}
</h3>
<channels-widget
:show-modification-date="true"

View File

@ -20,11 +20,11 @@ const labels = computed(() => ({
<h1 class="ui huge header">
<i class="warning icon" />
<div class="content">
{{ $t('components.PageNotFound.pageNotFound') }}
{{ $t('components.PageNotFound.header.pageNotFound') }}
</div>
</h1>
<p>
{{ $t('components.PageNotFound.pageNotFoundMessage') }}
{{ $t('components.PageNotFound.message.pageNotFound') }}
</p>
<a :href="path">{{ path }}</a>
<div class="ui hidden divider" />
@ -32,7 +32,7 @@ const labels = computed(() => ({
class="ui icon labeled right button"
to="/"
>
{{ $t('components.PageNotFound.homeLink') }}
{{ $t('components.PageNotFound.link.home') }}
<i class="right arrow icon" />
</router-link>
</div>

View File

@ -55,19 +55,19 @@ const scrollLock = useScrollLock(document.body)
const store = useStore()
const labels = computed(() => ({
queue: t('components.Queue.queue'),
populating: t('components.Queue.queue.populatingRadio'),
duration: t('components.Queue.duration'),
addArtistContentFilter: t('components.Queue.addArtistContentFilter'),
restart: t('components.Queue.restart'),
previous: t('components.Queue.previous'),
next: t('components.Queue.next'),
pause: t('components.Queue.pause'),
play: t('components.Queue.play'),
fullscreen: t('components.Queue.enterFullscreen'),
exitFullscreen: t('components.Queue.exitFullscreen'),
showCoverArt: t('components.Queue.showCoverArt'),
showVisualizer: t('components.Queue.showVisualizer')
queue: t('components.Queue.label.queue'),
populating: t('components.Queue.queue.label.populatingRadio'),
duration: t('components.Queue.label.duration'),
addArtistContentFilter: t('components.Queue.label.addArtistContentFilter'),
restart: t('components.Queue.label.restart'),
previous: t('components.Queue.label.previous'),
next: t('components.Queue.label.next'),
pause: t('components.Queue.label.pause'),
play: t('components.Queue.label.play'),
fullscreen: t('components.Queue.label.enterFullscreen'),
exitFullscreen: t('components.Queue.label.exitFullscreen'),
showCoverArt: t('components.Queue.label.showCoverArt'),
showVisualizer: t('components.Queue.label.showVisualizer')
}))
watchEffect(async () => {
@ -126,9 +126,9 @@ const queueItems = computed(() => queue.value.map((track, index) => ({
...track,
key: `${index}-${track.id}`,
labels: {
remove: t('components.Queue.remove'),
selectTrack: t('components.Queue.selectTrack'),
favorite: t('components.Queue.favorite')
remove: t('components.Queue.label.remove'),
selectTrack: t('components.Queue.label.selectTrack'),
favorite: t('components.Queue.label.favorite')
},
duration: time.durationFormatted(track.uploads[0]?.duration ?? 0) ?? ''
}) as QueueItemSource))
@ -298,14 +298,14 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui small warning message"
>
<h3 class="header">
{{ $t('components.Queue.trackLoadFailure') }}
{{ $t('components.Queue.header.failure') }}
</h3>
<p v-if="hasNext && isPlaying">
{{ $t('components.Queue.automaticPlay') }}
{{ $t('components.Queue.message.automaticPlay') }}
<i class="loading spinner icon" />
</p>
<p>
{{ $t('components.Queue.connectivityWarning') }}
{{ $t('components.Queue.warning.connectivity') }}
</p>
</div>
<div
@ -404,18 +404,18 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui right floated basic button"
@click="$store.commit('ui/queueFocused', null)"
>
{{ $t('components.Queue.closeButton') }}
{{ $t('components.Queue.button.close') }}
</button>
<button
class="ui right floated basic button danger"
@click="clear"
>
{{ $t('components.Queue.clearButton') }}
{{ $t('components.Queue.button.clear') }}
</button>
{{ labels.queue }}
<div class="sub header">
<div>
{{ $t('components.Queue.queuePosition', {index: currentIndex +1, length: queue.length}) }}
{{ $t('components.Queue.display.queuePosition', {index: currentIndex +1, length: queue.length}) }}
<template v-if="!$store.state.radios.running">
<span class="middle ellipses symbol" />
<span :title="labels.duration">
@ -461,10 +461,10 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
<div class="content">
<h3 class="header">
<i class="feed icon" />
{{ $t('components.Queue.radioPlaying') }}
{{ $t('components.Queue.header.radio') }}
</h3>
<p>
{{ $t('components.Queue.appendTracks') }}
{{ $t('components.Queue.message.radio') }}
</p>
<button
class="ui basic primary button"

View File

@ -40,14 +40,14 @@ const errors = ref([] as string[])
const { t } = useI18n()
const labels = computed(() => ({
title: type.value === 'rss'
? t('components.RemoteSearchForm.subscribeRss')
: t('components.RemoteSearchForm.subscribeFediverse'),
? t('components.RemoteSearchForm.label.rss.title')
: t('components.RemoteSearchForm.label.fediverse.title'),
fieldLabel: type.value === 'rss'
? t('components.RemoteSearchForm.rssLocation')
: t('components.RemoteSearchForm.fediverseObject'),
? t('components.RemoteSearchForm.label.rss.fieldLabel')
: t('components.RemoteSearchForm.label.fediverse.fieldLabel'),
fieldPlaceholder: type.value === 'rss'
? t('components.RemoteSearchForm.rssPlaceholder')
: t('components.RemoteSearchForm.fediversePlaceholder')
? t('components.RemoteSearchForm.label.rss.fieldPlaceholder')
: t('components.RemoteSearchForm.label.fediverse.fieldPlaceholder')
}))
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(t('components.RemoteSearchForm.fetchFailureMessage'))
errors.value.push(t('components.RemoteSearchForm.error.fetchFailed'))
}
} catch (error) {
errors.value = (error as BackendError).backendErrors
@ -172,7 +172,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'rss'"
>
<i class="feed icon" />
{{ $t('components.RemoteSearchForm.rss') }}
{{ $t('components.RemoteSearchForm.button.rss') }}
</button>
<div class="or" />
<button
@ -180,7 +180,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'artists'"
>
<i class="globe icon" />
{{ $t('components.RemoteSearchForm.fediverse') }}
{{ $t('components.RemoteSearchForm.button.fediverse') }}
</button>
</div>
<div v-else>
@ -195,7 +195,7 @@ watch(() => props.initialId, () => {
class="ui negative message"
>
<h3 class="header">
{{ $t('components.RemoteSearchForm.objectFetchError') }}
{{ $t('components.RemoteSearchForm.header.fetchFailed') }}
</h3>
<ul class="list">
<li
@ -211,10 +211,10 @@ watch(() => props.initialId, () => {
{{ labels.fieldLabel }}
</label>
<p v-if="type === 'rss'">
{{ $t('components.RemoteSearchForm.rssDescription') }}
{{ $t('components.RemoteSearchForm.description.rss') }}
</p>
<p v-else-if="type === 'artists'">
{{ $t('components.RemoteSearchForm.fediverseDescription') }}
{{ $t('components.RemoteSearchForm.description.fediverse') }}
</p>
<input
id="object-id"
@ -231,7 +231,7 @@ watch(() => props.initialId, () => {
:class="['ui', 'primary', {loading: isLoading}, 'button']"
:disabled="isLoading || !id || id.length === 0"
>
{{ $t('components.RemoteSearchForm.searchButton') }}
{{ $t('components.RemoteSearchForm.button.search') }}
</button>
</form>
<div
@ -240,7 +240,7 @@ watch(() => props.initialId, () => {
class="ui warning message"
>
<p>
{{ $t('components.RemoteSearchForm.unsupportedObject') }}
{{ $t('components.RemoteSearchForm.warning.unsupported') }}
</p>
</div>
</div>

View File

@ -51,7 +51,7 @@ const checkAndSwitch = async (url: string) => {
show.value = false
store.commit('ui/addMessage', {
content: t('components.SetInstanceModal.currentUrl', { url: instanceUrl }),
content: t('components.SetInstanceModal.message.newUrl', { url: instanceUrl }),
date: new Date()
})
@ -71,7 +71,7 @@ const checkAndSwitch = async (url: string) => {
@update:show="isError = false"
>
<h3 class="header">
{{ $t('components.SetInstanceModal.chooseInstance') }}
{{ $t('components.SetInstanceModal.header.chooseInstance') }}
</h3>
<div class="scrolling content">
<div
@ -80,14 +80,14 @@ const checkAndSwitch = async (url: string) => {
class="ui negative message"
>
<h4 class="header">
{{ $t('components.SetInstanceModal.connectionFailure') }}
{{ $t('components.SetInstanceModal.header.failure') }}
</h4>
<ul class="list">
<li>
{{ $t('components.SetInstanceModal.serverDown') }}
{{ $t('components.SetInstanceModal.help.serverDown') }}
</li>
<li>
{{ $t('components.SetInstanceModal.notFunkwhaleServer') }}
{{ $t('components.SetInstanceModal.help.notFunkwhaleServer') }}
</li>
</ul>
</div>
@ -99,13 +99,13 @@ const checkAndSwitch = async (url: string) => {
v-if="$store.state.instance.instanceUrl"
class="description"
>
{{ $t('components.SetInstanceModal.currentConnection', {url: $store.state.instance.instanceUrl, hostname: $store.getters['instance/domain']}) }}
{{ $t('components.SetInstanceModal.message.currentConnection', {url: $store.state.instance.instanceUrl, hostname: $store.getters['instance/domain']}) }}
</p>
<p v-else>
{{ $t('components.SetInstanceModal.selectFunkwhalePod') }}
{{ $t('components.SetInstanceModal.help.selectPod') }}
</p>
<div class="field">
<label for="instance-picker">{{ $t('components.SetInstanceModal.instanceUrl') }}</label>
<label for="instance-picker">{{ $t('components.SetInstanceModal.label.url') }}</label>
<div class="ui action input">
<input
id="instance-picker"
@ -117,7 +117,7 @@ const checkAndSwitch = async (url: string) => {
type="submit"
:class="['ui', 'icon', {loading: isLoading}, 'button']"
>
{{ $t('components.SetInstanceModal.submitButton') }}
{{ $t('components.SetInstanceModal.button.submit') }}
</button>
</div>
</div>
@ -129,7 +129,7 @@ const checkAndSwitch = async (url: string) => {
>
<div class="field">
<h4>
{{ $t('components.SetInstanceModal.suggestions') }}
{{ $t('components.SetInstanceModal.header.suggestions') }}
</h4>
<button
v-for="(url, key) in suggestedInstances"
@ -144,7 +144,7 @@ const checkAndSwitch = async (url: string) => {
</div>
<div class="actions">
<button class="ui basic cancel button">
{{ $t('components.SetInstanceModal.cancelButton') }}
{{ $t('components.SetInstanceModal.button.cancel') }}
</button>
</div>
</semantic-modal>

View File

@ -20,19 +20,19 @@ const showRef = useVModel(props, 'show', emit)
const { t } = useI18n()
const general = computed(() => [
{
title: t('components.ShortcutsModal.generalShortcuts'),
title: t('components.ShortcutsModal.shortcut.general.label'),
shortcuts: [
{
key: 'h',
summary: t('components.ShortcutsModal.showShortcuts')
summary: t('components.ShortcutsModal.shortcut.general.show')
},
{
key: 'shift + f',
summary: t('components.ShortcutsModal.focusSearch')
summary: t('components.ShortcutsModal.shortcut.general.focus')
},
{
key: 'esc',
summary: t('components.ShortcutsModal.unfocusSearch')
summary: t('components.ShortcutsModal.shortcut.general.unfocus')
}
]
}
@ -40,67 +40,67 @@ const general = computed(() => [
const player = computed(() => [
{
title: t('components.ShortcutsModal.audioShortcuts'),
title: t('components.ShortcutsModal.shortcut.audio.label'),
shortcuts: [
{
key: 'p',
summary: t('components.ShortcutsModal.playPause')
summary: t('components.ShortcutsModal.shortcut.audio.playPause')
},
{
key: 'left',
summary: t('components.ShortcutsModal.seekBack5')
summary: t('components.ShortcutsModal.shortcut.audio.seekBack5')
},
{
key: 'right',
summary: t('components.ShortcutsModal.seekForward5')
summary: t('components.ShortcutsModal.shortcut.audio.seekForward5')
},
{
key: 'shift + left',
summary: t('components.ShortcutsModal.seekBack30')
summary: t('components.ShortcutsModal.shortcut.audio.seekBack30')
},
{
key: 'shift + right',
summary: t('components.ShortcutsModal.seekForward30')
summary: t('components.ShortcutsModal.shortcut.audio.seekForward30')
},
{
key: 'ctrl + shift + left',
summary: t('components.ShortcutsModal.playPrevious')
summary: t('components.ShortcutsModal.shortcut.audio.playPrevious')
},
{
key: 'ctrl + shift + right',
summary: t('components.ShortcutsModal.playNext')
summary: t('components.ShortcutsModal.shortcut.audio.playNext')
},
{
key: 'shift + up',
summary: t('components.ShortcutsModal.increaseVolume')
summary: t('components.ShortcutsModal.shortcut.audio.increaseVolume')
},
{
key: 'shift + down',
summary: t('components.ShortcutsModal.decreaseVolume')
summary: t('components.ShortcutsModal.shortcut.audio.decreaseVolume')
},
{
key: 'm',
summary: t('components.ShortcutsModal.toggleMute')
summary: t('components.ShortcutsModal.shortcut.audio.toggleMute')
},
{
key: 'e',
summary: t('components.ShortcutsModal.expandQueue')
summary: t('components.ShortcutsModal.shortcut.audio.expandQueue')
},
{
key: 'l',
summary: t('components.ShortcutsModal.toggleLoop')
summary: t('components.ShortcutsModal.shortcut.audio.toggleLoop')
},
{
key: 's',
summary: t('components.ShortcutsModal.shuffleQueue')
summary: t('components.ShortcutsModal.shortcut.audio.shuffleQueue')
},
{
key: 'q',
summary: t('components.ShortcutsModal.clearQueue')
summary: t('components.ShortcutsModal.shortcut.audio.clearQueue')
},
{
key: 'f',
summary: t('components.ShortcutsModal.toggleFavorite')
summary: t('components.ShortcutsModal.shortcut.audio.toggleFavorite')
}
]
}
@ -110,7 +110,7 @@ const player = computed(() => [
<template>
<semantic-modal v-model:show="showRef">
<header class="header">
{{ $t('components.ShortcutsModal.modalHeader') }}
{{ $t('components.ShortcutsModal.header.modal') }}
</header>
<section class="scrolling content">
<div class="ui stackable two column grid">
@ -154,7 +154,7 @@ const player = computed(() => [
</section>
<footer class="actions">
<button class="ui basic cancel button">
{{ $t('components.ShortcutsModal.closeButton') }}
{{ $t('components.ShortcutsModal.button.close') }}
</button>
</footer>
</semantic-modal>

View File

@ -42,15 +42,15 @@ const additionalNotifications = computed(() => store.getters['ui/additionalNotif
const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index' : 'index')
const labels = computed(() => ({
mainMenu: t('components.Sidebar.mainMenu'),
selectTrack: t('components.Sidebar.selectTrack'),
pendingFollows: t('components.Sidebar.pendingFollows'),
pendingReviewEdits: t('components.Sidebar.pendingReviewEdits'),
pendingReviewReports: t('components.Sidebar.pendingReviewReports'),
language: t('components.Sidebar.language'),
theme: t('components.Sidebar.theme'),
addContent: t('components.Sidebar.addContent'),
administration: t('components.Sidebar.administration')
mainMenu: t('components.Sidebar.label.main'),
selectTrack: t('components.Sidebar.label.play'),
pendingFollows: t('components.Sidebar.label.follows'),
pendingReviewEdits: t('components.Sidebar.label.edits'),
pendingReviewReports: t('components.Sidebar.label.reports'),
language: t('components.Sidebar.label.language'),
theme: t('components.Sidebar.label.theme'),
addContent: t('components.Sidebar.label.add'),
administration: t('components.Sidebar.label.administration')
}))
type SidebarMenuTabs = 'explore' | 'myLibrary'
@ -129,7 +129,7 @@ onMounted(() => {
>
<i class="logo bordered inverted vibrant big icon">
<logo class="logo" />
<span class="visually-hidden">{{ $t('components.Sidebar.home') }}</span>
<span class="visually-hidden">{{ $t('components.Sidebar.link.home') }}</span>
</i>
</router-link>
<nav class="top ui compact right aligned inverted text menu">
@ -149,7 +149,7 @@ onMounted(() => {
</div>
<div class="menu">
<h3 class="header">
{{ $t('components.Sidebar.administration') }}
{{ $t('components.Sidebar.header.administration') }}
</h3>
<div class="divider" />
<router-link
@ -164,7 +164,7 @@ onMounted(() => {
>
{{ $store.state.ui.notifications.pendingReviewEdits }}
</div>
{{ $t('components.Sidebar.library') }}
{{ $t('components.Sidebar.link.library') }}
</router-link>
<router-link
v-if="$store.state.auth.availablePermissions['moderation']"
@ -178,21 +178,21 @@ onMounted(() => {
>
{{ $store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests }}
</div>
{{ $t('components.Sidebar.moderation') }}
{{ $t('components.Sidebar.link.moderation') }}
</router-link>
<router-link
v-if="$store.state.auth.availablePermissions['settings']"
class="item"
:to="{name: 'manage.users.users.list'}"
>
{{ $t('components.Sidebar.users') }}
{{ $t('components.Sidebar.link.users') }}
</router-link>
<router-link
v-if="$store.state.auth.availablePermissions['settings']"
class="item"
:to="{path: '/manage/settings'}"
>
{{ $t('components.Sidebar.settings') }}
{{ $t('components.Sidebar.link.settings') }}
</router-link>
</div>
</div>
@ -352,14 +352,14 @@ onMounted(() => {
class="ui fluid tiny primary button"
:to="{name: 'login'}"
>
{{ $t('components.Sidebar.login') }}
{{ $t('components.Sidebar.link.login') }}
</router-link>
<div class="ui small hidden divider" />
<router-link
class="ui fluid tiny button"
:to="{path: '/signup'}"
>
{{ $t('components.Sidebar.createAccount') }}
{{ $t('components.Sidebar.link.createAccount') }}
</router-link>
</div>
<nav
@ -371,7 +371,7 @@ onMounted(() => {
id="navigation-label"
class="visually-hidden"
>
{{ $t('components.Sidebar.mainNavigation') }}
{{ $t('components.Sidebar.header.main') }}
</h1>
<div class="ui small hidden divider" />
<section
@ -391,7 +391,7 @@ onMounted(() => {
@click="expanded = 'explore'"
@focus="expanded = 'explore'"
>
{{ $t('components.Sidebar.explore') }}
{{ $t('components.Sidebar.header.explore') }}
<i
v-if="expanded !== 'explore'"
class="angle right icon"
@ -403,7 +403,7 @@ onMounted(() => {
:to="{name: 'search'}"
>
<i class="search icon" />
{{ $t('components.Sidebar.search') }}
{{ $t('components.Sidebar.link.search') }}
</router-link>
<router-link
class="item"
@ -411,42 +411,42 @@ onMounted(() => {
active-class="_active"
>
<i class="music icon" />
{{ $t('components.Sidebar.browse') }}
{{ $t('components.Sidebar.link.browse') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.podcasts.browse'}"
>
<i class="podcast icon" />
{{ $t('components.Sidebar.podcasts') }}
{{ $t('components.Sidebar.link.podcasts') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.albums.browse'}"
>
<i class="compact disc icon" />
{{ $t('components.Sidebar.albums') }}
{{ $t('components.Sidebar.link.albums') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.artists.browse'}"
>
<i class="user icon" />
{{ $t('components.Sidebar.artists') }}
{{ $t('components.Sidebar.link.artists') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.playlists.browse'}"
>
<i class="list icon" />
{{ $t('components.Sidebar.playlists') }}
{{ $t('components.Sidebar.link.playlists') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.radios.browse'}"
>
<i class="feed icon" />
{{ $t('components.Sidebar.radios') }}
{{ $t('components.Sidebar.link.radios') }}
</router-link>
</div>
</div>
@ -461,7 +461,7 @@ onMounted(() => {
@click="expanded = 'myLibrary'"
@focus="expanded = 'myLibrary'"
>
{{ $t('components.Sidebar.myLibrary') }}
{{ $t('components.Sidebar.header.library') }}
<i
v-if="expanded !== 'myLibrary'"
class="angle right icon"
@ -473,42 +473,42 @@ onMounted(() => {
:to="{name: 'library.me'}"
>
<i class="music icon" />
{{ $t('components.Sidebar.browse') }}
{{ $t('components.Sidebar.link.browse') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.albums.me'}"
>
<i class="compact disc icon" />
{{ $t('components.Sidebar.albums') }}
{{ $t('components.Sidebar.link.albums') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.artists.me'}"
>
<i class="user icon" />
{{ $t('components.Sidebar.artists') }}
{{ $t('components.Sidebar.link.artists') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.playlists.me'}"
>
<i class="list icon" />
{{ $t('components.Sidebar.playlists') }}
{{ $t('components.Sidebar.link.playlists') }}
</router-link>
<router-link
class="item"
:to="{name: 'library.radios.me'}"
>
<i class="feed icon" />
{{ $t('components.Sidebar.radios') }}
{{ $t('components.Sidebar.link.radios') }}
</router-link>
<router-link
class="item"
:to="{name: 'favorites'}"
>
<i class="heart icon" />
{{ $t('components.Sidebar.favorites') }}
{{ $t('components.Sidebar.link.favorites') }}
</router-link>
</div>
</div>
@ -517,11 +517,11 @@ onMounted(() => {
class="header item"
:to="{name: 'subscriptions'}"
>
{{ $t('components.Sidebar.channels') }}
{{ $t('components.Sidebar.link.channels') }}
</router-link>
<div class="item">
<h3 class="header">
{{ $t('components.Sidebar.more') }}
{{ $t('components.Sidebar.header.more') }}
</h3>
<div class="menu">
<router-link
@ -530,7 +530,7 @@ onMounted(() => {
active-class="router-link-exact-active active"
>
<i class="info icon" />
{{ $t('components.Sidebar.aboutPod') }}
{{ $t('components.Sidebar.link.about') }}
</router-link>
</div>
</div>
@ -543,7 +543,7 @@ onMounted(() => {
href=""
class="link item"
@click.prevent="emit('show:set-instance-modal')"
>{{ $t('components.Sidebar.switchInstance') }}</a>
>{{ $t('components.Sidebar.link.switchInstance') }}</a>
</div>
</nav>
</section>

View File

@ -110,7 +110,7 @@ const save = async () => {
class="ui negative message"
>
<h4 class="header">
{{ $t('components.admin.SettingsGroup.errorMessage') }}
{{ $t('components.admin.SettingsGroup.header.error') }}
</h4>
<ul class="list">
<li
@ -125,7 +125,7 @@ const save = async () => {
v-if="result"
class="ui positive message"
>
{{ $t('components.admin.SettingsGroup.successMessage') }}
{{ $t('components.admin.SettingsGroup.message.success') }}
</div>
<div
v-for="(setting, key) in settings"
@ -225,7 +225,7 @@ const save = async () => {
<div v-if="values[setting.identifier]">
<div class="ui hidden divider" />
<h3 class="ui header">
{{ $t('components.admin.SettingsGroup.currentImage') }}
{{ $t('components.admin.SettingsGroup.header.image') }}
</h3>
<img
v-if="values[setting.identifier]"
@ -240,7 +240,7 @@ const save = async () => {
type="submit"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']"
>
{{ $t('components.admin.SettingsGroup.saveButton') }}
{{ $t('components.admin.SettingsGroup.button.save') }}
</button>
</form>
</template>

View File

@ -28,9 +28,9 @@ const isPreviewing = ref(false)
const { t } = useI18n()
const labels = computed(() => ({
delete: t('components.admin.SignupFormBuilder.deleteLabel'),
up: t('components.admin.SignupFormBuilder.moveUpLabel'),
down: t('components.admin.SignupFormBuilder.moveDownLabel')
delete: t('components.admin.SignupFormBuilder.label.delete'),
up: t('components.admin.SignupFormBuilder.label.moveUp'),
down: t('components.admin.SignupFormBuilder.label.moveDown')
}))
if (!value.value?.fields) {
@ -45,7 +45,7 @@ if (!value.value?.fields) {
const addField = () => {
value.value.fields.push({
label: t('components.admin.SignupFormBuilder.additionalFieldInput') + ' ' + (value.value.fields.length + 1),
label: t('components.admin.SignupFormBuilder.label.additionalField') + ' ' + (value.value.fields.length + 1),
required: true,
input_type: 'short_text'
})
@ -69,13 +69,13 @@ const move = (idx: number, increment: number) => {
:class="[{active: !isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = false"
>
{{ $t('components.admin.SignupFormBuilder.editForm') }}
{{ $t('components.admin.SignupFormBuilder.button.edit') }}
</button>
<button
:class="[{active: isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = true"
>
{{ $t('components.admin.SignupFormBuilder.previewForm') }}
{{ $t('components.admin.SignupFormBuilder.button.preview') }}
</button>
</div>
<div
@ -95,10 +95,10 @@ const move = (idx: number, increment: number) => {
>
<div class="field">
<label for="help-text">
{{ $t('components.admin.SignupFormBuilder.helpTextLabel') }}
{{ $t('components.admin.SignupFormBuilder.label.helpText') }}
</label>
<p>
{{ $t('components.admin.SignupFormBuilder.helpTextMessage') }}
{{ $t('components.admin.SignupFormBuilder.help.helpText') }}
</p>
<content-form
v-if="value.help_text"
@ -109,24 +109,24 @@ const move = (idx: number, increment: number) => {
</div>
<div class="field">
<label>
{{ $t('components.admin.SignupFormBuilder.additionalFieldsLabel') }}
{{ $t('components.admin.SignupFormBuilder.label.additionalFields') }}
</label>
<p>
{{ $t('components.admin.SignupFormBuilder.additionalFieldsMessage') }}
{{ $t('components.admin.SignupFormBuilder.help.additionalFields') }}
</p>
<table v-if="value.fields?.length > 0">
<thead>
<tr>
<th>
{{ $t('components.admin.SignupFormBuilder.fieldLabelTableHeader') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.header.label') }}
</th>
<th>
{{ $t('components.admin.SignupFormBuilder.fieldTypeTableHeader') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.header.type') }}
</th>
<th>
{{ $t('components.admin.SignupFormBuilder.requiredTableHeader') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.header.required') }}
</th>
<th><span class="visually-hidden">{{ $t('components.admin.SignupFormBuilder.actionsTableHeader') }}</span></th>
<th><span class="visually-hidden">{{ $t('components.admin.SignupFormBuilder.table.additionalFields.header.actions') }}</span></th>
</tr>
</thead>
<tbody>
@ -144,20 +144,20 @@ const move = (idx: number, increment: number) => {
<td>
<select v-model="field.input_type">
<option value="short_text">
{{ $t('components.admin.SignupFormBuilder.shortTextInput') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.type.short') }}
</option>
<option value="long_text">
{{ $t('components.admin.SignupFormBuilder.longTextInput') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.type.long') }}
</option>
</select>
</td>
<td>
<select v-model="field.required">
<option :value="true">
{{ $t('components.admin.SignupFormBuilder.requiredTrue') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.required.true') }}
</option>
<option :value="false">
{{ $t('components.admin.SignupFormBuilder.requiredFalse') }}
{{ $t('components.admin.SignupFormBuilder.table.additionalFields.required.false') }}
</option>
</select>
</td>
@ -192,7 +192,7 @@ const move = (idx: number, increment: number) => {
class="ui basic button"
@click.stop.prevent="addField"
>
{{ $t('components.admin.SignupFormBuilder.addFieldButton') }}
{{ $t('components.admin.SignupFormBuilder.button.add') }}
</button>
</div>
</div>

View File

@ -33,7 +33,7 @@ const urlId = computed(() => props.object.actor?.is_local
const { t } = useI18n()
const updatedTitle = computed(() => {
const date = momentFormat(new Date(props.object.artist?.modification_date ?? '1970-01-01'))
return t('components.audio.ChannelCard.updatedOn', { date })
return t('components.audio.ChannelCard.title', { date })
})
// TODO (wvffle): Use time ago
@ -68,12 +68,12 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
v-if="object.artist?.content_category === 'podcast'"
class="meta ellipsis"
>
{{ $t('components.audio.ChannelCard.episodeCount', {episode_count: object.artist.tracks_count}) }}
{{ $t('components.audio.ChannelCard.meta.episodes', {episode_count: object.artist.tracks_count}) }}
</span>
<span
v-else
>
{{ $t('components.audio.ChannelCard.trackCount', {tracks_count: object.artist?.tracks_count}) }}
{{ $t('components.audio.ChannelCard.meta.tracks', {tracks_count: object.artist?.tracks_count}) }}
</span>
<tags-list
label-classes="tiny"

View File

@ -103,7 +103,7 @@ watch(page, fetchData, { immediate: true })
@refresh="fetchData()"
>
<p>
{{ $t('components.audio.ChannelEntries.emptyMessage') }}
{{ $t('components.audio.ChannelEntries.help.subscribe') }}
</p>
</empty-state>
</template>

View File

@ -45,13 +45,13 @@ const creating = computed(() => props.object === null)
const categoryChoices = computed(() => [
{
value: 'podcast',
label: t('components.audio.ChannelForm.podcastsLabel'),
helpText: t('components.audio.ChannelForm.podcastsHelpText')
label: t('components.audio.ChannelForm.label.podcast'),
helpText: t('components.audio.ChannelForm.help.podcast')
},
{
value: 'music',
label: t('components.audio.ChannelForm.discographyLabel'),
helpText: t('components.audio.ChannelForm.discographyHelpText')
label: t('components.audio.ChannelForm.label.discography'),
helpText: t('components.audio.ChannelForm.help.discography')
}
])
@ -81,8 +81,8 @@ const itunesSubcategories = computed(() => {
})
const labels = computed(() => ({
namePlaceholder: t('components.audio.ChannelForm.namePlaceholder'),
usernamePlaceholder: t('components.audio.ChannelForm.usernamePlaceholder')
namePlaceholder: t('components.audio.ChannelForm.placeholder.name'),
usernamePlaceholder: t('components.audio.ChannelForm.placeholder.username')
}))
const submittable = computed(() => !!(
@ -165,7 +165,7 @@ defineExpose({
class="ui negative message"
>
<h4 class="header">
{{ $t('components.audio.ChannelForm.errorHeader') }}
{{ $t('components.audio.ChannelForm.header.error') }}
</h4>
<ul class="list">
<li
@ -182,7 +182,7 @@ defineExpose({
class="ui grouped channel-type required field"
>
<legend>
{{ $t('components.audio.ChannelForm.channelPurposeLegend') }}
{{ $t('components.audio.ChannelForm.legend.purpose') }}
</legend>
<div class="ui hidden divider" />
<div class="field">
@ -210,7 +210,7 @@ defineExpose({
<template v-if="!creating || step === 2">
<div class="ui required field">
<label for="channel-name">
{{ $t('components.audio.ChannelForm.channelNameLabel') }}
{{ $t('components.audio.ChannelForm.label.name') }}
</label>
<input
v-model="newValues.name"
@ -221,7 +221,7 @@ defineExpose({
</div>
<div class="ui required field">
<label for="channel-username">
{{ $t('components.audio.ChannelForm.channelUsernameLabel') }}
{{ $t('components.audio.ChannelForm.label.username') }}
</label>
<div class="ui left labeled input">
<div class="ui basic label">
@ -238,7 +238,7 @@ defineExpose({
<template v-if="creating">
<div class="ui small hidden divider" />
<p>
{{ $t('components.audio.ChannelForm.channelUsernameDescription') }}
{{ $t('components.audio.ChannelForm.help.username') }}
</p>
</template>
</div>
@ -248,7 +248,7 @@ defineExpose({
:image-class="newValues.content_category === 'podcast' ? '' : 'circular'"
@delete="newValues.cover = null"
>
{{ $t('components.audio.ChannelForm.channelImageLabel') }}
{{ $t('components.audio.ChannelForm.label.image') }}
</attachment-input>
</div>
<div class="ui small hidden divider" />
@ -256,7 +256,7 @@ defineExpose({
<div class="ten wide column">
<div class="ui field">
<label for="channel-tags">
{{ $t('components.audio.ChannelForm.channelTagsLabel') }}
{{ $t('components.audio.ChannelForm.label.tags') }}
</label>
<tags-selector
id="channel-tags"
@ -271,7 +271,7 @@ defineExpose({
>
<div class="ui required field">
<label for="channel-language">
{{ $t('components.audio.ChannelForm.channelLanguageLabel') }}
{{ $t('components.audio.ChannelForm.label.language') }}
</label>
<select
id="channel-language"
@ -294,7 +294,7 @@ defineExpose({
<div class="ui small hidden divider" />
<div class="ui field">
<label for="channel-name">
{{ $t('components.audio.ChannelForm.channelDescriptionLabel') }}
{{ $t('components.audio.ChannelForm.label.description') }}
</label>
<content-form v-model="newValues.description" />
</div>
@ -304,7 +304,7 @@ defineExpose({
>
<div class="ui required field">
<label for="channel-itunes-category">
{{ $t('components.audio.ChannelForm.channelCategoryLabel') }}
{{ $t('components.audio.ChannelForm.label.category') }}
</label>
<select
id="itunes-category"
@ -324,7 +324,7 @@ defineExpose({
</div>
<div class="ui field">
<label for="channel-itunes-category">
{{ $t('components.audio.ChannelForm.channelSubcategoryLabel') }}
{{ $t('components.audio.ChannelForm.label.subcategory') }}
</label>
<select
id="itunes-category"
@ -349,7 +349,7 @@ defineExpose({
>
<div class="ui field">
<label for="channel-itunes-email">
{{ $t('components.audio.ChannelForm.channelEmailLabel') }}
{{ $t('components.audio.ChannelForm.label.email') }}
</label>
<input
id="channel-itunes-email"
@ -360,7 +360,7 @@ defineExpose({
</div>
<div class="ui field">
<label for="channel-itunes-name">
{{ $t('components.audio.ChannelForm.channelOwnerLabel') }}
{{ $t('components.audio.ChannelForm.label.owner') }}
</label>
<input
id="channel-itunes-name"
@ -371,7 +371,7 @@ defineExpose({
</div>
</div>
<p>
{{ $t('components.audio.ChannelForm.channelPodcastFieldsHelp') }}
{{ $t('components.audio.ChannelForm.help.podcastFields') }}
</p>
</template>
</template>
@ -380,7 +380,7 @@ defineExpose({
class="ui active inverted dimmer"
>
<div class="ui text loader">
{{ $t('components.audio.ChannelForm.loadingMessage') }}
{{ $t('components.audio.ChannelForm.message.loading') }}
</div>
</div>
</form>

View File

@ -56,7 +56,7 @@ const cover = computed(() => props.serie?.cover ?? null)
</strong>
<div class="description">
<span>
{{ $t('components.audio.ChannelSerieCard.episodeCount', serie.tracks_count) }}
{{ $t('components.audio.ChannelSerieCard.meta.episodes', serie.tracks_count) }}
</span>
</div>
</div>

View File

@ -84,7 +84,7 @@ fetchData()
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
{{ $t('components.audio.ChannelSeries.showMore') }}
{{ $t('components.audio.ChannelSeries.button.showMore') }}
</button>
</template>
<template v-if="!isLoading && albums.length === 0">
@ -93,7 +93,7 @@ fetchData()
@refresh="fetchData()"
>
<p>
{{ $t('components.audio.ChannelSeries.emptyMessage') }}
{{ $t('components.audio.ChannelSeries.help.subscribe') }}
</p>
</empty-state>
</template>

View File

@ -77,7 +77,7 @@ fetchData()
:class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)"
>
{{ $t('components.audio.ChannelsWidget.showMore') }}
{{ $t('components.audio.ChannelsWidget.button.showMore') }}
</button>
</template>
<template v-if="!isLoading && channels.length === 0">

View File

@ -52,20 +52,20 @@ const { copy, copied } = useClipboard({ source: textarea })
>
<p>
<strong>
{{ $t('components.audio.EmbedWizard.anonymousAccessWarning') }}
{{ $t('components.audio.EmbedWizard.warning.anonymous') }}
</strong>
</p>
<p>
{{ $t('components.audio.EmbedWizard.anonymousAccessHelp') }}
{{ $t('components.audio.EmbedWizard.help.anonymous') }}
</p>
</div>
<div class="ui form">
<div class="two fields">
<div class="field">
<div class="field">
<label for="embed-width">{{ $t('components.audio.EmbedWizard.widgetWidthLabel') }}</label>
<label for="embed-width">{{ $t('components.audio.EmbedWizard.label.width') }}</label>
<p>
{{ $t('components.audio.EmbedWizard.widgetWidthHelp') }}
{{ $t('components.audio.EmbedWizard.help.width') }}
</p>
<input
id="embed-width"
@ -78,7 +78,7 @@ const { copy, copied } = useClipboard({ source: textarea })
<template v-if="type != 'track'">
<br>
<div class="field">
<label for="embed-height">{{ $t('components.audio.EmbedWizard.widgetHeightLabel') }}</label>
<label for="embed-height">{{ $t('components.audio.EmbedWizard.label.height') }}</label>
<input
id="embed-height"
v-model="height"
@ -96,11 +96,11 @@ const { copy, copied } = useClipboard({ source: textarea })
@click="copy()"
>
<i class="copy icon" />
{{ $t('components.audio.EmbedWizard.copyButton') }}
{{ $t('components.audio.EmbedWizard.button.copy') }}
</button>
<label for="embed-width">{{ $t('components.audio.EmbedWizard.embedCodeLabel') }}</label>
<label for="embed-width">{{ $t('components.audio.EmbedWizard.label.embed') }}</label>
<p>
{{ $t('components.audio.EmbedWizard.embedCodeHelp') }}
{{ $t('components.audio.EmbedWizard.help.embed') }}
</p>
<textarea
ref="textarea"
@ -113,7 +113,7 @@ const { copy, copied } = useClipboard({ source: textarea })
v-if="copied"
class="message"
>
{{ $t('components.audio.EmbedWizard.copyButtonSuccessMessage') }}
{{ $t('components.audio.EmbedWizard.message.copy') }}
</p>
</div>
</div>
@ -125,7 +125,7 @@ const { copy, copied } = useClipboard({ source: textarea })
:href="iframeSrc"
target="_blank"
>
{{ $t('components.audio.EmbedWizard.previewHeader') }}
{{ $t('components.audio.EmbedWizard.header.preview') }}
</a>
</h3>
<iframe

View File

@ -41,17 +41,17 @@ const toggle = () => {
<span
v-if="isApproved"
>
{{ $t('components.audio.LibraryFollowButton.unfollowLabel') }}
{{ $t('components.audio.LibraryFollowButton.button.unfollow') }}
</span>
<span
v-else-if="isPending"
>
{{ $t('components.audio.LibraryFollowButton.cancelLabel') }}
{{ $t('components.audio.LibraryFollowButton.button.cancel') }}
</span>
<span
v-else
>
{{ $t('components.audio.LibraryFollowButton.followLabel') }}
{{ $t('components.audio.LibraryFollowButton.button.follow') }}
</span>
</button>
</template>

View File

@ -65,31 +65,31 @@ const { report, getReportableObjects } = useReport()
const { t } = useI18n()
const labels = computed(() => ({
playNow: t('components.audio.PlayButton.playNowLabel'),
addToQueue: t('components.audio.PlayButton.addToQueueLabel'),
playNext: t('components.audio.PlayButton.playNextLabel'),
startRadio: t('components.audio.PlayButton.startRadioLabel'),
report: t('components.audio.PlayButton.reportLabel'),
addToPlaylist: t('components.audio.PlayButton.addToPlaylistLabel'),
hideArtist: t('components.audio.PlayButton.hideArtistLabel'),
playNow: t('components.audio.PlayButton.button.playNow'),
addToQueue: t('components.audio.PlayButton.button.addToQueue'),
playNext: t('components.audio.PlayButton.button.playNext'),
startRadio: t('components.audio.PlayButton.button.startRadio'),
report: t('components.audio.PlayButton.button.report'),
addToPlaylist: t('components.audio.PlayButton.button.addToPlaylist'),
hideArtist: t('components.audio.PlayButton.button.hideArtist'),
replacePlay: props.track
? t('components.audio.PlayButton.playTrackLabel')
? t('components.audio.PlayButton.button.playTrack')
: props.album
? t('components.audio.PlayButton.playAlbumLabel')
? t('components.audio.PlayButton.button.playAlbum')
: props.artist
? t('components.audio.PlayButton.playArtistLabel')
? t('components.audio.PlayButton.button.playArtist')
: props.playlist
? t('components.audio.PlayButton.playPlaylistLabel')
: t('components.audio.PlayButton.playTracksLabel')
? t('components.audio.PlayButton.button.playPlaylist')
: t('components.audio.PlayButton.button.playTracks')
}))
const title = computed(() => {
if (playable.value) {
return t('components.audio.PlayButton.moreTitle')
return t('components.audio.PlayButton.title.more')
}
if (props.track) {
return t('components.audio.PlayButton.notAvailableTitle')
return t('components.audio.PlayButton.title.unavailable')
}
return ''
@ -138,7 +138,7 @@ const openMenu = () => {
v-else
:class="[playIconClass, 'icon']"
/>
<template v-if="!discrete && !iconOnly">&nbsp;<slot>{{ $t('components.audio.PlayButton.discretePlayButton') }}</slot></template>
<template v-if="!discrete && !iconOnly">&nbsp;<slot>{{ $t('components.audio.PlayButton.button.discretePlay') }}</slot></template>
</button>
<button
v-if="!discrete && !iconOnly"
@ -200,10 +200,10 @@ const openMenu = () => {
<i class="info icon" />
<span
v-if="track.artist?.content_category === 'podcast'"
>{{ $t('components.audio.PlayButton.episodeDetailsButton') }}</span>
>{{ $t('components.audio.PlayButton.button.episodeDetails') }}</span>
<span
v-else
>{{ $t('components.audio.PlayButton.trackDetailsButton') }}</span>
>{{ $t('components.audio.PlayButton.button.trackDetails') }}</span>
</button>
<div class="divider" />
<button

View File

@ -72,17 +72,17 @@ onKeyboardShortcut(['ctrl', 'shift', 'left'], playPrevious, true)
onKeyboardShortcut(['ctrl', 'shift', 'right'], playNext, true)
const labels = computed(() => ({
audioPlayer: t('components.audio.Player.audioPlayerLabel'),
previous: t('components.audio.Player.previousTrackLabel'),
play: t('components.audio.Player.playLabel'),
pause: t('components.audio.Player.pauseLabel'),
next: t('components.audio.Player.nextTrackLabel'),
unmute: t('components.audio.Player.unmuteLabel'),
mute: t('components.audio.Player.muteLabel'),
expandQueue: t('components.audio.Player.expandQueueLabel'),
shuffle: t('components.audio.Player.shuffleQueueLabel'),
clear: t('components.audio.Player.clearQueueLabel'),
addArtistContentFilter: t('components.audio.Player.addArtistContentFilterLabel')
audioPlayer: t('components.audio.Player.label.audioPlayer'),
previous: t('components.audio.Player.label.previousTrack'),
play: t('components.audio.Player.label.play'),
pause: t('components.audio.Player.label.pause'),
next: t('components.audio.Player.label.nextTrack'),
unmute: t('components.audio.Player.label.unmute'),
mute: t('components.audio.Player.label.mute'),
expandQueue: t('components.audio.Player.label.expandQueue'),
shuffle: t('components.audio.Player.label.shuffleQueue'),
clear: t('components.audio.Player.label.clearQueue'),
addArtistContentFilter: t('components.audio.Player.label.addArtistContentFilter')
}))
const switchTab = () => {
@ -103,10 +103,10 @@ initializeFirstTrack()
const loopingTitle = computed(() => {
const mode = looping.value
return mode === LoopingMode.None
? t('components.audio.Player.loopingDisabledLabel')
? t('components.audio.Player.label.loopingDisabledLabel')
: mode === LoopingMode.LoopTrack
? t('components.audio.Player.loopingSingleLabel')
: t('components.audio.Player.loopingWholeQueueLabel')
? t('components.audio.Player.label.loopingSingleLabel')
: t('components.audio.Player.label.loopingWholeQueueLabel')
})
const hideArtist = () => {
@ -133,7 +133,7 @@ const hideArtist = () => {
id="player-label"
class="visually-hidden"
>
{{ $t('components.audio.Player.playerHeader') }}
{{ $t('components.audio.Player.header.player') }}
</h1>
<div
class="ui inverted segment fixed-controls"
@ -301,7 +301,7 @@ const hideArtist = () => {
>
<i class="stream icon" />
<span>
{{ $t('components.audio.Player.queuePosition', { index: currentIndex + 1, length: queue.length }) }}
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
</span>
</button>
<button
@ -310,7 +310,7 @@ const hideArtist = () => {
>
<i class="stream icon" />
<span>
{{ $t('components.audio.Player.queuePosition', { index: currentIndex + 1, length: queue.length }) }}
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
</span>
</button>

View File

@ -65,7 +65,7 @@ onMounted(() => {
})
const labels = computed(() => ({
searchPlaceholder: t('components.audio.Search.searchPlaceHolderLabel')
searchPlaceholder: t('components.audio.Search.placeholder.search')
}))
</script>
@ -73,7 +73,7 @@ const labels = computed(() => ({
<template>
<div>
<h2>
{{ $t('components.audio.Search.searchHeader') }}
{{ $t('components.audio.Search.header.search') }}
</h2>
<div :class="['ui', {'loading': isLoading }, 'search']">
<div class="ui icon big input">
@ -89,7 +89,7 @@ const labels = computed(() => ({
</div>
<template v-if="query.length > 0">
<h3 class="ui title">
{{ $t('components.audio.Search.artistsHeader') }}
{{ $t('components.audio.Search.header.artists') }}
</h3>
<div v-if="results.artists.length > 0">
<div class="ui cards">
@ -101,12 +101,12 @@ const labels = computed(() => ({
</div>
</div>
<p v-else>
{{ $t('components.audio.Search.noArtistsMessage') }}
{{ $t('components.audio.Search.message.noArtists') }}
</p>
</template>
<template v-if="query.length > 0">
<h3 class="ui title">
{{ $t('components.audio.Search.albumsHeader') }}
{{ $t('components.audio.Search.header.albums') }}
</h3>
<div
v-if="results.albums.length > 0"
@ -124,7 +124,7 @@ const labels = computed(() => ({
</div>
</div>
<p v-else>
{{ $t('components.audio.Search.noAlbumsMessage') }}
{{ $t('components.audio.Search.message.noAlbums') }}
</p>
</template>
</div>

View File

@ -5,334 +5,524 @@
"components": {
"About": {
"title": "About",
"funkwhaleHeader": "A social platform to enjoy and share music",
"funkwhaleDescription": "Funkwhale is a community-driven project that lets you listen and share music and audio within a decentralized, open network.",
"signupHeader": "Sign up",
"signupDescription": "Sign up now to keep a track of your favorites, create playlists, discover new content and much more!",
"quotaDescription": "Users on this pod also get {quota} of free storage to upload their own content!",
"registrationsClosedHelp": "Registrations are closed on this pod. You can signup on another pod using the link below.",
"findOtherPod": "Find another pod",
"alreadyLoggedIn": "You're already signed in!",
"greetingMessage": "Hello {username}",
"aboutPodHeader": "About this pod",
"noDescription": "No description available",
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music",
"learnMoreLink": "Learn More",
"publicContentHeader": "Browse public content",
"publicContentDescription": "Listen to public albums and playlists shared on this pod.",
"findAppHeader": "Find an app",
"findAppDescription": "Use Funkwhale on other devices with our apps."
"header": {
"funkwhale": "A social platform to enjoy and share music",
"signup": "Sign up",
"publicContent": "Browse public content",
"findApp": "Find an app",
"aboutPod": "About this pod"
},
"description": {
"funkwhale": "Funkwhale is a community-driven project that lets you listen and share music and audio within a decentralized, open network.",
"signup": "Sign up now to keep a track of your favorites, create playlists, discover new content and much more!",
"quota": "Users on this pod also get {quota} of free storage to upload their own content!",
"publicContent": "Listen to public albums and playlists shared on this pod.",
"findApp": "Use Funkwhale on other devices with our apps."
},
"placeholder": {
"noDescription": "No description available"
},
"message": {
"greeting": "Hello {username}",
"loggedIn": "You're already signed in!"
},
"link": {
"learnMore": "Learn more",
"findOtherPod": "Find another pod"
},
"stat": {
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music"
},
"help": {
"closedRegistrations": "Registrations are closed on this pod. You can signup on another pod using the link below."
}
},
"AboutPod": {
"title": "About",
"aboutPod": "About this pod",
"rules": "Rules",
"termsAndPrivacy": "Terms and privacy policy",
"features": "Features",
"statistics": "Statistics",
"noDescription": "No description available",
"noRules": "No rules available",
"noTerms": "No terms available",
"funkwhaleVersion": "Funkwhale version",
"notApplicable": "N/A",
"federation": "Federation",
"enabled": "Enabled",
"disabled": "Disabled",
"allowList": "Allow-list",
"anonymousAccess": "Anonymous access",
"registrations": "Registrations",
"open": "Open",
"closed": "Closed",
"uploadQuota": "Upload quota",
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music",
"artistsCount": "{artists} artist | {artists} artists",
"albumsCount": "{albums} album | {albums} albums",
"tracksCount": "{tracks} track | {tracks} tracks",
"listeningsCount": "{listenings} listening | {listenings} listenings",
"contactHeader": "Contact",
"contactEmail": "Send us an email: {contactEmail}",
"introductionLink": "Introdution"
"header": {
"about":"About this pod",
"rules": "Rules",
"terms": "Terms and privacy policy",
"features": "Features",
"contact": "Contact",
"statistics": "Statistics"
},
"placeholder": {
"noDescription": "No description available",
"noRules": "No rules available",
"noTerms": "No terms available"
},
"message": {
"contact": "Send us an email: {contactEmail}"
},
"link": {
"about": "About this pod",
"rules": "Rules",
"terms": "Terms and privacy policy",
"features": "Features",
"statistics": "Statistics",
"introduction": "Introduction"
},
"feature": {
"version": "Funkwhale version",
"federation": "Federation",
"allowList": "Allow-list",
"anonymousAccess": "Anonymous access",
"registrations": "Registrations",
"quota": "Upload quota",
"status": {
"enabled": "Enabled",
"disabled": "Disabled",
"open": "Open",
"closed": "Closed"
}
},
"stat": {
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music",
"artistsCount": "{artists} artist | {artists} artists",
"albumsCount": "{albums} album | {albums} albums",
"tracksCount": "{tracks} track | {tracks} tracks",
"listeningsCount": "{listenings} listening | {listenings} listenings"
}
},
"Home": {
"title": "Home",
"welcomeMessage": "Welcome to {podName}!",
"aboutPod": "About this Funkwhale pod",
"noDescription": "No description available",
"learnMore": "Learn more",
"serverRules": "Server rules",
"statistics": "Statistics",
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music",
"contactHeader": "Contact",
"aboutFunkwhale": "About Funkwhale",
"funkwhaleDescription": "This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network.",
"funkwhaleAddendum": "Funkwhale is free and developed by a friendly community of volunteers.",
"websiteLink": "Visit funkwhale.audio",
"loginHeader": "Log In",
"signupHeader": "Sign up",
"signupDescription": "Sign up now to keep a track of your favorites, create playlists, discover new content and much more!",
"uploadQuota": "Users on this pod also get {quota} of free storage to upload their own content!",
"registrationsClosed": "Registrations are closed on this pod. You can signup on another pod using the link below.",
"findOtherPod": "Find another pod",
"usefulLinks": "Useful links",
"browsePublicContent": "Browse public content",
"publicContentDescription": "Listen to public albums and playlists shared on this pod.",
"mobileApps": "Mobile apps",
"mobileAppsDescription": "Use Funkwhale on other devices with our apps",
"userGuides": "User guides",
"userGuidesDescription": "Discover everything you need to know about Funkwhale and its features",
"recentlyAddedLabel": "Recently added albums",
"viewMore": "View more…",
"newChannelsLabel": "New channels"
"header": {
"welcome": "Welcome to {podName}!",
"about":"About this Funkwhale pod",
"statistics": "Statistics",
"contact": "Contact",
"aboutFunkwhale": "About Funkwhale",
"login": "Log in",
"signup": "Sign up",
"links": "Useful links",
"newAlbums": "Recently added albums",
"newChannels": "New channels"
},
"description": {
"signup": "Sign up now to keep a track of your favorites, create playlists, discover new content and much more!",
"quota": "Users on this pod also get {quota} of free storage to upload their own content!",
"funkwhale": {
"paragraph1": "This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network.",
"paragraph2": "Funkwhale is free and developed by a friendly community of volunteers."
}
},
"placeholder": {
"noDescription": "No description available"
},
"link": {
"learnMore": "Learn more",
"rules": "Server rules",
"funkwhale": "Visit funkwhale.audio",
"findOtherPod": "Find another pod",
"viewMore": "View more…",
"publicContent": {
"label": "Browse public content",
"description": "Listen to public albums and playlists shared on this pod."
},
"mobileApps": {
"label": "Mobile apps",
"description": "Use Funkwhale on other devices with our apps"
},
"userGuides": {
"label": "User guides",
"description": "Discover everything you need to know about Funkwhale and its features"
}
},
"stat": {
"activeUsers": "{users} active user | {users} active users",
"hoursOfMusic": "{hours} hour of music | {hours} hours of music"
},
"help": {
"registrationsClosed": "Registrations are closed on this pod. You can signup on another pod using the link below."
}
},
"PageNotFound": {
"title": "Page not found",
"pageNotFound": "Page not found!",
"pageNotFoundMessage": "Sorry, the page you asked for does not exist:",
"homeLink": "Go to home page"
"header": {
"pageNotFound": "Page not found!"
},
"message": {
"pageNotFound": "Sorry, the page you asked for does not exist:"
},
"link": {
"home": "Go to home page"
}
},
"Queue": {
"queue": "Queue",
"duration": "Duration",
"addArtistContentFilter": "Hide content from this artist…",
"restart": "Restart track",
"previous": "Previous track",
"next": "Next track",
"pause": "Pause",
"play": "Play",
"remove": "Remove",
"selectTrack": "Select track",
"favorite": "Favorite track",
"trackLoadFailure": "The track cannot be loaded",
"automaticPlay": "The next track will play automatically in a few seconds…",
"connectivityWarning": "You may have a connectivity issue.",
"closeButton": "Close",
"clearButton": "Clear",
"queuePosition":"Track {index} of {length}",
"radioPlaying": "You have a radio playing",
"appendTracks": "New tracks will be appended here automatically.",
"stopRadio": "Stop radio"
"label": {
"queue": "Queue",
"duration": "Duration",
"addArtistContentFilter": "Hide content from this artist…",
"restart": "Restart track",
"previous": "Previous track",
"next": "Next track",
"pause": "Pause",
"play": "Play",
"remove": "Remove",
"selectTrack": "Select track",
"favorite": "Favorite track"
},
"header": {
"failure": "The track cannot be loaded",
"radio": "You have a radio playing"
},
"message": {
"automaticPlay": "The next track will play automatically in a few seconds…",
"radio": "New tracks will be appended here automatically."
},
"warning": {
"connectivity": "You may have a connectivity issue."
},
"button": {
"close": "Close",
"clear": "Clear",
"stopRadio": "Stop radio"
},
"display": {
"queuePosition":"Track {index} of {length}"
}
},
"RemoteSearchForm": {
"subscribeRss": "Subscribe to a podcast RSS feed",
"subscribeFediverse": "Subscribe to a podcast hosted on the Fediverse",
"rssLocation": "RSS feed location",
"fediverseObject": "Fediverse object",
"rssPlaceholder": "https://website.example.com/rss.xml",
"fediversePlaceholder": "@username@example.com",
"fetchFailureMessage": "This object cannot be retrieved",
"rss": "RSS",
"fediverse": "Fediverse",
"objectFetchError": "Error while fetching object",
"rssDescription": "Use this form to subscribe to an RSS feed from its URL.",
"fediverseDescription": "Use this form to subscribe to a channel hosted somewhere else on the Fediverse.",
"searchButton": "Search",
"unsupportedObject": "This kind of object isn't supported yet"
"label": {
"rss": {
"title": "Subscribe to a podcast RSS feed",
"fieldLabel": "RSS feed location",
"fieldPlaceholder": "https://website.example.com/rss.xml"
},
"fediverse": {
"title": "Fediverse object",
"fieldLabel": "Fediverse object",
"fieldPlaceholder": "@username@example.com"
}
},
"header": {
"fetchFailed": "Error while fetching object"
},
"description": {
"rss": "Use this form to subscribe to an RSS feed from its URL.",
"fediverse": "Use this form to subscribe to a channel hosted somewhere else on the Fediverse."
},
"button": {
"search": "Search",
"rss": "RSS",
"fediverse": "Fediverse"
},
"error": {
"fetchFailed": "This object cannot be retrieved"
},
"warning": {
"unsupported": "This kind of object isn't supported yet"
}
},
"SetInstanceModal": {
"currentUrl": "You are now using the Funkwhale instance at {url}",
"chooseInstance": "Choose your instance",
"connectionFailure": "It is not possible to connect to the given URL",
"serverDown": "The server might be down",
"notFunkwhaleServer": "The given address is not a Funkwhale server",
"currentConnection": "You are currently connected to <a href=\"{url}\" target=\"_blank\">{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.",
"selectFunkwhalePod": "To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices.",
"instanceUrl": "Instance URL",
"submitButton": "Submit",
"suggestions": "Suggested choices",
"cancelButton": "Cancel"
"header": {
"chooseInstance": "Choose your instance",
"failure": "It is not possible to connect to the given URL",
"suggestions": "Suggested choices"
},
"message": {
"newUrl": "You are now using the Funkwhale instance at {url}",
"currentConnection": "You are currently connected to <a href=\"{url}\" target=\"_blank\">{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."
},
"help": {
"serverDown": "The server might be down",
"notFunkwhaleServer": "The given address is not a Funkwhale server",
"selectPod": "To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices."
},
"button": {
"submit": "Submit",
"cancel": "Cancel"
},
"label": {
"url": "Instance URL"
}
},
"ShortcutsModal": {
"generalShortcuts": "General shortcuts",
"showShortcuts": "Show available keyboard shortcuts",
"focusSearch": "Focus searchbar",
"unfocusSearch": "Unfocus searchbar",
"audioShortcuts": "Audio player shortcuts",
"playPause": "Pause/play the current track",
"seekBack5": "Seek backwards 5s",
"seekForward5": "Seek forwards 5s",
"seekBack30": "Seek backwards 30s",
"seekForward30": "Seek forwards 30s",
"playPrevious": "Play previous track",
"playNext":"Play next track",
"increaseVolume": "Increase volume",
"decreaseVolume": "Decrease volume",
"toggleMute": "Toggle mute",
"expandQueue": "Expand queue/player view",
"toggleLoop": "Toggle queue looping",
"shuffleQueue": "Shuffle queue",
"clearQueue": "Clear queue",
"toggleFavorite": "Toggle favorite",
"modalHeader": "Keyboard shortcuts",
"closeButton": "Close"
"header": {
"modal": "Keyboard shortcuts"
},
"button": {
"close": "Close"
},
"shortcut": {
"general": {
"label": "General shortcuts",
"show": "Show available keyboard shortcuts",
"focus": "Focus searchbar",
"unfocus": "Unfocus searchbar"
},
"audio": {
"label": "Audio player shortcuts",
"playPause": "Pause/play the current track",
"seekBack5": "Seek backwards 5s",
"seekForward5": "Seek forwards 5s",
"seekBack30": "Seek backwards 30s",
"seekForward30": "Seek forwards 30s",
"playPrevious": "Play previous track",
"playNext":"Play next track",
"increaseVolume": "Increase volume",
"decreaseVolume": "Decrease volume",
"toggleMute": "Toggle mute",
"expandQueue": "Expand queue/player view",
"toggleLoop": "Toggle queue looping",
"shuffleQueue": "Shuffle queue",
"clearQueue": "Clear queue",
"toggleFavorite": "Toggle favorite"
}
}
},
"Sidebar": {
"mainMenu": "Main menu",
"selectTrack": "Play this track",
"pendingFollows": "Pending follow requests",
"pendingReviewEdits": "Pending review edits",
"pendingReviewReports": "Pending review reports",
"language": "Language",
"theme": "Theme",
"addContent": "Add content",
"administration": "Administration",
"home": "Home",
"library": "Library",
"moderation": "Moderation",
"users": "Users",
"settings": "Settings",
"login": "Login",
"createAccount": "Create an account",
"mainNavigation": "Main navigation",
"explore": "Explore",
"search": "Search",
"browse": "Browse",
"podcasts": "Podcasts",
"albums": "Albums",
"artists": "Artists",
"playlists": "Playlists",
"radios": "Radios",
"favorites": "Favorites",
"channels": "Channels",
"myLibrary": "My library",
"more": "More",
"aboutPod": "About this pod",
"switchInstance": "Switch instance"
"label": {
"main": "Main menu",
"play": "Play this track",
"follows": "Pending follow requests",
"edits": "Pending review edits",
"reports": "Pending review reports",
"language": "Language",
"theme": "Theme",
"add": "Add content",
"administration": "Administration"
},
"header": {
"main": "Main navigation",
"explore": "Explore",
"more": "More",
"library": "My library",
"administration": "Administration"
},
"link": {
"home": "Home",
"search": "Search",
"browse": "Browse",
"podcasts": "Podcasts",
"albums": "Albums",
"artists": "Artists",
"playlists": "Playlists",
"radios": "Radios",
"favorites": "Favorites",
"channels": "Channels",
"about": "About this pod",
"switchInstance": "Switch instance",
"moderation": "Moderation",
"users": "Users",
"library": "Library",
"settings": "Settings",
"login": "Login",
"createAccount": "Create account"
}
},
"admin": {
"SettingsGroup": {
"errorMessage": "Error while saving settings.",
"successMessage": "Settings updated successfully.",
"currentImage": "Current image",
"saveButton": "Save"
"header": {
"error": "Error while saving settings.",
"image": "Current image"
},
"message": {
"success": "Settings updated successfully."
},
"button": {
"save": "Save"
}
},
"SignupFormBuilder": {
"deleteLabel": "Delete",
"moveUpLabel": "Move up",
"moveDownLabel": "Move down",
"additionalFieldInput": "Additional field",
"editForm": "Edit form",
"previewForm": "Preview Form",
"helpTextLabel": "Help text",
"helpTextMessage": "An optional text to be displayed at the start of the sign-up form.",
"additionalFieldsLabel": "Additional fields",
"additionalFieldsMessage": "Additional form fields to be displayed in the form. Only shown if manual sign-up validation is enabled.",
"fieldLabelTableHeader": "Field label",
"fieldTypeTableHeader": "Field type",
"requiredTableHeader": "Required",
"actionsTableHeader": "Actions",
"shortTextInput": "Short text",
"longTextInput": "Long text",
"requiredTrue": "Yes",
"requiredFalse": "No",
"addFieldButton": "Add a new field"
"label": {
"delete": "Delete",
"moveUp": "Move up",
"moveDown": "Move down",
"additionalField": "Additional field",
"helpText": "Help text",
"additionalFields": "Additional fields"
},
"button": {
"edit": "Edit form",
"preview": "Preview Form",
"add": "Add a new field"
},
"help": {
"helpText": "An optional text to be displayed at the start of the sign-up form.",
"additionalFields": "Additional form fields to be displayed in the form. Only shown if manual sign-up validation is enabled."
},
"table": {
"additionalFields": {
"header": {
"label": "Field label",
"type": "Field type",
"required": "Required",
"actions": "Actions"
},
"type": {
"short": "Short text",
"long": "Long text"
},
"required": {
"true": "True",
"false": "False"
}
}
}
}
},
"audio": {
"ChannelCard": {
"updatedOn": "Updated on {date}",
"episodeCount": "No episodes | {episode_count} episode | {episode_count} episodes",
"trackCount": "No tracks | {tracks_count} track | {tracks_count} tracks"
"title": "Updated on {date}",
"meta": {
"episodes": "No episodes | {episode_count} episode | {episode_count} episodes",
"tracks": "No tracks | {tracks_count} track | {tracks_count} tracks"
}
},
"ChannelEntries": {
"emptyMessage": "You may need to subscribe to this channel to see its content."
"help": {
"subscribe": "You may need to subscribe to this channel to see its content."
}
},
"ChannelForm": {
"podcastsLabel": "Podcasts",
"podcastsHelpText": "Host your episodes and keep your community updated.",
"discographyLabel": "Artist Discography",
"discographyHelpText": "Publish music you make as a nice discography of albums and singles.",
"namePlaceholder": "Awesome channel name",
"usernamePlaceholder": "awesomechannelname",
"errorHeader": "Error while saving channel",
"channelPurposeLegend": "What will this channel be used for?",
"channelNameLabel": "Name",
"channelUsernameLabel": "Fediverse handle",
"channelUsernameDescription": "Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.",
"channelImageLabel": "Channel Picture",
"channelTagsLabel": "Tags",
"channelLanguageLabel": "Language",
"channelDescriptionLabel": "Description",
"channelCategoryLabel": "Category",
"channelSubcategoryLabel": "Subcategory",
"channelEmailLabel": "Owner e-mail address",
"channelOwnerLabel": "Owner name",
"channelPodcastFieldsHelp": "Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.",
"loadingMessage": "Loading"
"label": {
"podcast": "Podcasts",
"discography": "Artist Discography",
"name": "Name",
"username": "Fediverse handle",
"image": "Channel Picture",
"tags": "Tags",
"language": "Language",
"description": "Description",
"category": "Category",
"subcategory": "Subcategory",
"email": "Owner e-mail address",
"owner": "Owner name"
},
"help": {
"podcast": "Host your episodes and keep your community updated.",
"discography": "Publish music you make as a nice discography of albums and singles.",
"username": "Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.",
"podcastFields": "Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes."
},
"placeholder": {
"name": "Awesome channel name",
"username": "awesomechannelname"
},
"header": {
"error": "Error while saving channel."
},
"legend": {
"purpose": "What will this channel be used for?"
},
"message": {
"loading": "Loading"
}
},
"ChannelSerieCard": {
"episodeCount": "No episodes | {episode_count} episode | {episode_count} episodes"
"meta": {
"episodes": "No episodes | {episode_count} episode | {episode_count} episodes"
}
},
"ChannelSeries": {
"showMore": "Show more",
"emptyMessage": "You may need to subscribe to this channel to see its contents."
"button": {
"showMore": "Show more"
},
"help": {
"subscribe": "You may need to subscribe to this channel to see its contents."
}
},
"ChannelsWidget": {
"showMore": "Show more"
"button": {
"showMore": "Show more"
}
},
"EmbedWizard": {
"anonymousAccessWarning": "Sharing will not work because this pod doesn't allow anonymous users to access content.",
"anonymousAccessHelp": "Please contact your admins and ask them to update the corresponding setting.",
"widgetWidthLabel": "Widget width",
"widgetWidthHelp": "Leave empty for a responsive widget",
"widgetHeightLabel": "Widget height",
"copyButton": "Copy",
"embedCodeLabel": "Embed code",
"embedCodeHelp": "Copy/paste this code in your website HTML",
"copyButtonSuccessMessage": "Text copied to clipboard!",
"previewHeader": "Preview"
"warning": {
"anonymous": "Sharing will not work because this pod doesn't allow anonymous users to access content."
},
"help": {
"anonymous": "Please contact your admins and ask them to update the corresponding setting.",
"embed": "Copy/paste this code in your website HTML",
"width": "Leave empty for a responsive widget"
},
"label": {
"width": "Widget width",
"height": "Widget height",
"embed": "Embed code"
},
"button": {
"copy": "Copy"
},
"header": {
"preview": "Preview"
},
"message": {
"copy": "Text copied to clipboard!"
}
},
"LibraryFollowButton": {
"unfollowLabel": "Unfollow",
"cancelLabel": "Cancel follow request",
"followLabel": "Follow"
"button": {
"unfollow": "Unfollow",
"cancel": "Cancel follow request",
"follow": "Follow"
}
},
"PlayButton": {
"playNowLabel": "Play now",
"addToQueueLabel": "Add to current queue",
"playNextLabel": "Play next",
"startRadioLabel": "Play similar songs",
"reportLabel": "Report…",
"addToPlaylistLabel": "Add to playlist",
"hideArtistLabel": "Hide content from this artist",
"playTrackLabel": "Play track",
"playAlbumLabel": "Play album",
"playArtistLabel": "Play artist",
"playPlaylistLabel": "Play playlist",
"playTracksLabel": "Play tracks",
"moreTitle": "More…",
"notAvailableTitle": "This track is not available in any library you have access to",
"episodeDetailsButton": "Episode details",
"trackDetailsButton": "Track details",
"discretePlayButton": "Play"
"button": {
"playNow": "Play now",
"addToQueue": "Add to current queue",
"playNext": "Play next",
"startRadio": "Play similar songs",
"report": "Report…",
"addToPlaylist": "Add to playlist",
"hideArtist": "Hide content from this artist",
"playTrack": "Play track",
"playAlbum": "Play album",
"playArtist": "Play artist",
"playPlaylist": "Play playlist",
"playTracks": "Play tracks",
"episodeDetails": "Episode details",
"trackDetails": "Track details",
"discretePlay": "Play"
},
"title": {
"more": "More…",
"unavailable": "This track is not available in any library you have access to"
}
},
"Player": {
"audioPlayerLabel": "Media player",
"previousTrackLabel": "Previous track",
"playLabel": "Play",
"pauseLabel": "Pause",
"nextTrackLabel": "Next track",
"unmuteLabel": "Unmute",
"muteLabel": "Mute",
"expandQueueLabel": "Expand queue",
"loopingDisabledLabel": "Looping disabled. Click to switch to single-track looping.",
"loopingSingleLabel": "Looping on a single track. Click to switch to whole queue looping.",
"loopingWholeQueueLabel": "Looping on whole queue. Click to disable looping.",
"shuffleQueueLabel": "Shuffle your queue",
"clearQueueLabel": "Clear your queue",
"addArtistContentFilterLabel": "Hide content from this artist…",
"playerHeader": "Audio player and controls",
"queuePosition": "{index} of {length}"
"label": {
"audioPlayer": "Media player",
"previousTrack": "Previous track",
"play": "Play",
"pause": "Pause",
"nextTrack": "Next track",
"unmute": "Unmute",
"mute": "Mute",
"expandQueue": "Expand queue",
"loopingDisabled": "Looping disabled. Click to switch to single-track looping.",
"loopingSingle": "Looping on a single track. Click to switch to whole queue looping.",
"loopingWholeQueue": "Looping on whole queue. Click to disable looping.",
"shuffleQueue": "Shuffle your queue",
"clearQueue": "Clear your queue",
"addArtistContentFilter": "Hide content from this artist…"
},
"header": {
"player": "Audio player and controls"
},
"meta": {
"position": "{index} of {length}"
}
},
"Search": {
"searchPlaceHolderLabel": "Artist, album, track…",
"searchHeader": "Search for some music",
"artistsHeader": "Artists",
"albumsHeader": "Albums",
"noArtistsMessage": "No artist matched your query",
"noAlbumsMessage": "No album matched your query"
"header": {
"search": "Search for some music",
"artists": "Artists",
"albums": "Albums"
},
"message": {
"noArtists": "No artist matched your query",
"noAlbums": "No album matched your query"
},
"placeholder": {
"search": "Artist, album, track…"
}
},
"SearchBar": {
"placeHolderLabel": "Search for artists, albums, tracks…",