Add eslint rules for i18n

This commit is contained in:
wvffle 2022-09-10 16:31:48 +00:00 committed by Kasper Seweryn
parent ebea32faf9
commit edfbf94313
200 changed files with 1738 additions and 295018 deletions

15
.vscode/settings.json vendored
View File

@ -2,9 +2,20 @@
"python.defaultInterpreterPath": "/workspace/funkwhale/api/.venv/bin/python", "python.defaultInterpreterPath": "/workspace/funkwhale/api/.venv/bin/python",
"python.testing.cwd": "/workspace/funkwhale/api", "python.testing.cwd": "/workspace/funkwhale/api",
"python.envFile": "/workspace/funkwhale/.gitpod/.env", "python.envFile": "/workspace/funkwhale/.gitpod/.env",
"python.testing.pytestArgs": ["--cov=funkwhale_api", "tests/"], "python.testing.pytestArgs": [
"--cov=funkwhale_api",
"tests/"
],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"vitest.enable": true, "vitest.enable": true,
"vitest.commandLine": "yarn vitest" "vitest.commandLine": "yarn vitest",
"i18n-ally.localesPaths": [
"front/src/locales"
],
"i18n-ally.pathMatcher": "*.json",
"i18n-ally.enabledFrameworks": [
"vue"
],
"i18n-ally.keystyle": "nested"
} }

View File

@ -5,6 +5,7 @@ module.exports = {
}, },
extends: [ extends: [
'plugin:vue/vue3-recommended', 'plugin:vue/vue3-recommended',
'plugin:@intlify/vue-i18n/recommended',
'@vue/typescript/recommended', '@vue/typescript/recommended',
'@vue/standard' '@vue/standard'
], ],
@ -32,6 +33,11 @@ module.exports = {
'no-redeclare': 'off', 'no-redeclare': 'off',
'no-undef': 'off', 'no-undef': 'off',
// NOTE: i18n
'@intlify/vue-i18n/valid-message-syntax': 'error',
'@intlify/vue-i18n/no-missing-keys': 'error',
'@intlify/vue-i18n/no-dynamic-keys': 'error',
// TODO (wvffle): Remove after VUI and #1618 // TODO (wvffle): Remove after VUI and #1618
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'import/extensions': 'off', 'import/extensions': 'off',
@ -51,5 +57,11 @@ module.exports = {
'vue/comment-directive': 'off' 'vue/comment-directive': 'off'
} }
} }
] ],
settings: {
'vue-i18n': {
localeDir: 'src/locales/*.{json,json5,yaml,yml}',
messageSyntaxVersion: '^9.0.0'
}
}
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -58,6 +58,8 @@
"vuex-router-sync": "5.0.0" "vuex-router-sync": "5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@intlify/eslint-plugin-vue-i18n": "^2.0.0",
"@intlify/vite-plugin-vue-i18n": "^6.0.1",
"@types/diff": "5.0.2", "@types/diff": "5.0.2",
"@types/dompurify": "2.4.0", "@types/dompurify": "2.4.0",
"@types/howler": "2.2.7", "@types/howler": "2.2.7",

View File

@ -67,14 +67,10 @@ const headerStyle = computed(() => {
<div class="column" /> <div class="column" />
</div> </div>
<h2 class="header"> <h2 class="header">
<translate > A social platform to enjoy and share music
A social platform to enjoy and share music
</translate>
</h2> </h2>
<p> <p>
<translate > Funkwhale is a community-driven project that lets you listen and share music and audio within a decentralized, open network.
Funkwhale is a community-driven project that lets you listen and share music and audio within a decentralized, open network.
</translate>
</p> </p>
</div> </div>
</div> </div>
@ -88,23 +84,14 @@ const headerStyle = computed(() => {
class="signup-form content" class="signup-form content"
> >
<h3 class="header"> <h3 class="header">
<translate > Sign up
Sign up
</translate>
</h3> </h3>
<template v-if="openRegistrations"> <template v-if="openRegistrations">
<p> <p>
<translate > Sign up now to keep a track of your favorites, create playlists, discover new content and much more!
Sign up now to keep a track of your favorites, create playlists, discover new content and much more!
</translate>
</p> </p>
<p v-if="defaultUploadQuota"> <p v-if="defaultUploadQuota">
<translate Users on this pod also get %{ quota } of free storage to upload their own content!
:translate-params="{quota: defaultUploadQuota}"
>
Users on this pod also get %{ quota } of free storage to upload their own content!
</translate>
</p> </p>
<signup-form <signup-form
button-classes="success" button-classes="success"
@ -112,7 +99,7 @@ const headerStyle = computed(() => {
/> />
</template> </template>
<div v-else> <div v-else>
<p > <p>
Registrations are closed on this pod. You can signup on another pod using the link below. Registrations are closed on this pod. You can signup on another pod using the link below.
</p> </p>
@ -121,7 +108,7 @@ const headerStyle = computed(() => {
rel="noopener" rel="noopener"
href="https://funkwhale.audio/#get-started" href="https://funkwhale.audio/#get-started"
> >
<translate >Find another pod</translate> Find another pod
&nbsp;<i class="external alternate icon" /> &nbsp;<i class="external alternate icon" />
</a> </a>
</div> </div>
@ -131,19 +118,13 @@ const headerStyle = computed(() => {
class="signup-form content" class="signup-form content"
> >
<h3 class="header"> <h3 class="header">
<translate > Sign up
Sign up
</translate>
<div class="ui positive message"> <div class="ui positive message">
<div class="header"> <div class="header">
<translate > You're already signed in!
You're already signed in!
</translate>
</div> </div>
<p> <p>
<translate translate-contect="Content/About/Hello"> Hello {{ $store.state.auth.username }}
Hello
</translate> {{ $store.state.auth.username }}
</p> </p>
</div> </div>
</h3> </h3>
@ -164,9 +145,7 @@ const headerStyle = computed(() => {
id="description" id="description"
class="ui header" class="ui header"
> >
<translate > About this pod
About this pod
</translate>
</h3> </h3>
<div <div
v-if="shortDescription" v-if="shortDescription"
@ -175,9 +154,7 @@ const headerStyle = computed(() => {
{{ shortDescription }} {{ shortDescription }}
</div> </div>
<p v-else> <p v-else>
<translate > No description available.
No description available.
</translate>
</p> </p>
<template v-if="stats"> <template v-if="stats">
@ -187,22 +164,14 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('active user | active users', stats.users) }}
:translate-n="stats.users"
translate-plural="active users"
>active user</translate>
</span> </span>
</div> </div>
<div class="column"> <div class="column">
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('hour of music | hours of music', stats.hours) }}
translate-context="Content/About/*"
:translate-n="stats.hours"
translate-plural="hours of music"
>hour of music</translate>
</span> </span>
</div> </div>
</div> </div>
@ -213,9 +182,7 @@ const headerStyle = computed(() => {
to="/about/pod" to="/about/pod"
class="ui fluid basic secondary button" class="ui fluid basic secondary button"
> >
<translate > Learn More
Learn More
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -234,14 +201,10 @@ const headerStyle = computed(() => {
id="description" id="description"
class="ui header" class="ui header"
> >
<translate > Browse public content
Browse public content
</translate>
</h3> </h3>
<p> <p>
<translate > Listen to public albums and playlists shared on this pod.
Listen to public albums and playlists shared on this pod.
</translate>
</p> </p>
</div> </div>
</router-link> </router-link>
@ -255,11 +218,11 @@ const headerStyle = computed(() => {
id="description" id="description"
class="ui header" class="ui header"
> >
<translate >Find another pod</translate> Find another pod
&nbsp;<i class="external alternate icon" /> &nbsp;<i class="external alternate icon" />
</h3> </h3>
<p> <p>
<translate >Listen to public albums and playlists shared on this pod.</translate> Listen to public albums and playlists shared on this pod.
</p> </p>
</div> </div>
</a> </a>
@ -273,11 +236,11 @@ const headerStyle = computed(() => {
id="description" id="description"
class="ui header" class="ui header"
> >
<translate >Find an app</translate> Find an app
&nbsp;<i class="external alternate icon" /> &nbsp;<i class="external alternate icon" />
</h3> </h3>
<p> <p>
<translate >Use Funkwhale on other devices with our apps.</translate> Use Funkwhale on other devices with our apps.
</p> </p>
</div> </div>
</a> </a>
@ -287,9 +250,7 @@ const headerStyle = computed(() => {
to="/about/pod" to="/about/pod"
class="ui right floated basic secondary button" class="ui right floated basic secondary button"
> >
<translate > About this pod
About this pod
</translate>
<i class="icon arrow right" /> <i class="icon arrow right" />
</router-link> </router-link>
</div> </div>

View File

@ -99,42 +99,32 @@ const headerStyle = computed(() => {
to="/about/pod" to="/about/pod"
class="item" class="item"
> >
<translate > About this pod
About this pod
</translate>
</router-link> </router-link>
<router-link <router-link
to="/about/pod#rules" to="/about/pod#rules"
class="item" class="item"
> >
<translate > Rules
Rules
</translate>
</router-link> </router-link>
<router-link <router-link
to="/about/pod#terms" to="/about/pod#terms"
class="item" class="item"
> >
<translate > Terms and privacy policy
Terms and privacy policy
</translate>
</router-link> </router-link>
<router-link <router-link
to="/about/pod#features" to="/about/pod#features"
class="item" class="item"
> >
<translate > Features
Features
</translate>
</router-link> </router-link>
<router-link <router-link
v-if="stats" v-if="stats"
to="/about/pod#statistics" to="/about/pod#statistics"
class="item" class="item"
> >
<translate > Statistics
Statistics
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -144,63 +134,49 @@ const headerStyle = computed(() => {
id="description about-this-pod" id="description about-this-pod"
class="ui header" class="ui header"
> >
<translate > About this pod
About this pod
</translate>
</h2> </h2>
<sanitized-html <sanitized-html
v-if="longDescription" v-if="longDescription"
:html="longDescription" :html="longDescription"
/> />
<p v-else> <p v-else>
<translate > No description available.
No description available.
</translate>
</p> </p>
<h3 <h3
id="rules" id="rules"
class="ui header" class="ui header"
> >
<translate > Rules
Rules
</translate>
</h3> </h3>
<sanitized-html <sanitized-html
v-if="rules" v-if="rules"
:html="rules" :html="rules"
/> />
<p v-else> <p v-else>
<translate > No rules available.
No rules available.
</translate>
</p> </p>
<h3 <h3
id="terms" id="terms"
class="ui header" class="ui header"
> >
<translate > Terms and privacy policy
Terms and privacy policy
</translate>
</h3> </h3>
<sanitized-html <sanitized-html
v-if="terms" v-if="terms"
:html="terms" :html="terms"
/> />
<p v-else> <p v-else>
<translate > No terms available.
No terms available.
</translate>
</p> </p>
<h3 <h3
id="features" id="features"
class="header" class="header"
> >
<translate > Features
Features
</translate>
</h3> </h3>
<div class="features-container ui two column stackable grid"> <div class="features-container ui two column stackable grid">
<div class="column"> <div class="column">
@ -208,9 +184,7 @@ const headerStyle = computed(() => {
<tbody> <tbody>
<tr> <tr>
<td> <td>
<translate > Funkwhale version
Funkwhale version
</translate>
</td> </td>
<td <td
v-if="version" v-if="version"
@ -225,15 +199,13 @@ const headerStyle = computed(() => {
class="right aligned" class="right aligned"
> >
<span class="features-status ui text"> <span class="features-status ui text">
<translate >N/A</translate> N/A
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<translate > Federation
Federation
</translate>
</td> </td>
<td <td
v-if="federationEnabled" v-if="federationEnabled"
@ -241,7 +213,7 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="check icon" /> <i class="check icon" />
<translate >Enabled</translate> Enabled
</span> </span>
</td> </td>
<td <td
@ -250,15 +222,13 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="x icon" /> <i class="x icon" />
<translate >Disabled</translate> Disabled
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<translate > Allow-list
Allow-list
</translate>
</td> </td>
<td <td
v-if="allowListEnabled" v-if="allowListEnabled"
@ -266,7 +236,7 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="check icon" /> <i class="check icon" />
<translate >Enabled</translate> Enabled
</span> </span>
</td> </td>
<td <td
@ -275,7 +245,7 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="x icon" /> <i class="x icon" />
<translate >Disabled</translate> Disabled
</span> </span>
</td> </td>
</tr> </tr>
@ -287,9 +257,7 @@ const headerStyle = computed(() => {
<tbody> <tbody>
<tr> <tr>
<td> <td>
<translate > Anonymous access
Anonymous access
</translate>
</td> </td>
<td <td
v-if="anonymousCanListen" v-if="anonymousCanListen"
@ -297,7 +265,7 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="check icon" /> <i class="check icon" />
<translate >Enabled</translate> Enabled
</span> </span>
</td> </td>
<td <td
@ -306,15 +274,13 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="x icon" /> <i class="x icon" />
<translate >Disabled</translate> Disabled
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<translate > Registrations
Registrations
</translate>
</td> </td>
<td <td
v-if="openRegistrations" v-if="openRegistrations"
@ -322,7 +288,7 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="check icon" /> <i class="check icon" />
<translate >Open</translate> Open
</span> </span>
</td> </td>
<td <td
@ -331,15 +297,13 @@ const headerStyle = computed(() => {
> >
<span class="features-status ui text"> <span class="features-status ui text">
<i class="x icon" /> <i class="x icon" />
<translate >Closed</translate> Closed
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<translate > Upload quota
Upload quota
</translate>
</td> </td>
<td <td
v-if="defaultUploadQuota" v-if="defaultUploadQuota"
@ -354,7 +318,7 @@ const headerStyle = computed(() => {
class="right aligned" class="right aligned"
> >
<span class="features-status ui text"> <span class="features-status ui text">
<translate >N/A</translate> N/A
</span> </span>
</td> </td>
</tr> </tr>
@ -368,9 +332,7 @@ const headerStyle = computed(() => {
id="statistics" id="statistics"
class="header" class="header"
> >
<translate > Statistics
Statistics
</translate>
</h3> </h3>
<div class="statistics-container"> <div class="statistics-container">
<div <div
@ -380,11 +342,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.hours.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('hour of music | hours of music', stats.hours) }}
translate-context="Content/About/*"
:translate-n="stats.hours"
translate-plural="hours of music"
>hour of music</translate>
</span> </span>
</div> </div>
<div <div
@ -394,11 +352,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.artists.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.artists.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('artist | artists', stats.artists) }}
:translate-n="stats.artists"
translate-plural="artists"
>artist</translate>
</span> </span>
</div> </div>
<div <div
@ -408,11 +362,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.albums.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.albums.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('album | albums', stats.albums) }}
:translate-n="stats.albums"
translate-plural="albums"
>album</translate>
</span> </span>
</div> </div>
<div <div
@ -422,11 +372,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.tracks.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.tracks.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('track | tracks', stats.tracks) }}
:translate-n="stats.tracks"
translate-plural="tracks"
>track</translate>
</span> </span>
</div> </div>
<div <div
@ -436,11 +382,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.users.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('active user | active users', stats.users) }}
:translate-n="stats.users"
translate-plural="active users"
>active user</translate>
</span> </span>
</div> </div>
<div <div
@ -450,11 +392,7 @@ const headerStyle = computed(() => {
<span class="statistics-figure ui text"> <span class="statistics-figure ui text">
<span class="ui big text"><strong>{{ stats.listenings.toLocaleString($store.state.ui.momentLocale) }}</strong></span> <span class="ui big text"><strong>{{ stats.listenings.toLocaleString($store.state.ui.momentLocale) }}</strong></span>
<br> <br>
<translate {{ $t('listening | listenings', stats.listenings) }}
:translate-n="stats.listenings"
translate-plural="listenings"
>listening</translate>
</span> </span>
</div> </div>
</div> </div>
@ -465,18 +403,13 @@ const headerStyle = computed(() => {
id="contact" id="contact"
class="ui header" class="ui header"
> >
<translate > Contact
Contact
</translate>
</h3> </h3>
<a <a
v-if="contactEmail" v-if="contactEmail"
:href="`mailto:${contactEmail}`" :href="`mailto:${contactEmail}`"
> >
<translate {{ $t('Send us an email: %{ contactEmail }', { contactEmail }) }}
:translate-params="{ email: contactEmail }"
>Send us an email: {{ contactEmail }}</translate>
</a> </a>
</template> </template>
@ -487,9 +420,7 @@ const headerStyle = computed(() => {
class="ui left floated basic secondary button" class="ui left floated basic secondary button"
> >
<i class="icon arrow left" /> <i class="icon arrow left" />
<translate > Introduction
Introduction
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>

View File

@ -70,12 +70,9 @@ whenever(() => store.state.auth.authenticated, () => {
> >
<div class="segment-content"> <div class="segment-content">
<h1 class="ui center aligned large header"> <h1 class="ui center aligned large header">
<translate <span>
:translate-params="{podName: podName}" {{ $t('Welcome to %{ podName }!', { podName }) }}
tag="span" </span>
>
Welcome to %{ podName }!
</translate>
<div <div
v-if="shortDescription" v-if="shortDescription"
class="sub header" class="sub header"
@ -89,9 +86,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="ten wide column"> <div class="ten wide column">
<h2 class="header"> <h2 class="header">
<translate > About this Funkwhale pod
About this Funkwhale pod
</translate>
</h2> </h2>
<div <div
id="pod" id="pod"
@ -100,9 +95,7 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="eight wide column"> <div class="eight wide column">
<p v-if="!longDescription"> <p v-if="!longDescription">
<translate > No description available.
No description available.
</translate>
</p> </p>
<template v-if="longDescription || rules"> <template v-if="longDescription || rules">
<sanitized-html <sanitized-html
@ -125,9 +118,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link" class="ui link"
:to="{name: 'about'}" :to="{name: 'about'}"
> >
<translate > Learn more
Learn more
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -142,9 +133,7 @@ whenever(() => store.state.auth.authenticated, () => {
class="ui link" class="ui link"
:to="{name: 'about', hash: '#rules'}" :to="{name: 'about', hash: '#rules'}"
> >
<translate > Server rules
Server rules
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -154,36 +143,20 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="eight wide column"> <div class="eight wide column">
<template v-if="stats"> <template v-if="stats">
<h3 class="sub header"> <h3 class="sub header">
<translate > Statistics
Statistics
</translate>
</h3> </h3>
<p> <p>
<i class="user icon" /><translate <i class="user icon" />
{{ $t('%{ users } active user | %{ users } active users', stats, stats.users) }}
:translate-params="{count: stats.users.toLocaleString($store.state.ui.momentLocale) }"
:translate-n="stats.users"
translate-plural="%{ count } active users"
>
%{ count } active user
</translate>
</p> </p>
<p> <p>
<i class="music icon" /><translate <i class="music icon" />
translate-context="Content/Home/Stat" {{ $t('%{ hours } hour of music | %{ hours } hours of music', stats, stats.hours) }}
:translate-params="{count: stats.hours.toLocaleString($store.state.ui.momentLocale)}"
:translate-n="stats.hours"
translate-plural="%{ count } hours of music"
>
%{ count } hour of music
</translate>
</p> </p>
</template> </template>
<template v-if="contactEmail"> <template v-if="contactEmail">
<h3 class="sub header"> <h3 class="sub header">
<translate > Contact
Contact
</translate>
</h3> </h3>
<i class="at icon" /> <i class="at icon" />
<a :href="`mailto:${contactEmail}`">{{ contactEmail }}</a> <a :href="`mailto:${contactEmail}`">{{ contactEmail }}</a>
@ -206,30 +179,26 @@ whenever(() => store.state.auth.authenticated, () => {
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="four wide column"> <div class="four wide column">
<h3 class="header"> <h3 class="header">
<translate > About Funkwhale
About Funkwhale
</translate>
</h3> </h3>
<translate tag="p"> <p>
This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network. This pod runs Funkwhale, a community-driven project that lets you listen and share music and audio within a decentralized, open network.
</translate> </p>
<translate tag="p"> <p>
Funkwhale is free and developed by a friendly community of volunteers. Funkwhale is free and developed by a friendly community of volunteers.
</translate> </p>
<a <a
target="_blank" target="_blank"
rel="noopener" rel="noopener"
href="https://funkwhale.audio" href="https://funkwhale.audio"
> >
<i class="external alternate icon" /> <i class="external alternate icon" />
<translate >Visit funkwhale.audio</translate> Visit funkwhale.audio
</a> </a>
</div> </div>
<div class="four wide column"> <div class="four wide column">
<h3 class="header"> <h3 class="header">
<translate > Log In
Log In
</translate>
</h3> </h3>
<login-form <login-form
button-classes="success" button-classes="success"
@ -239,23 +208,14 @@ whenever(() => store.state.auth.authenticated, () => {
</div> </div>
<div class="four wide column"> <div class="four wide column">
<h3 class="header"> <h3 class="header">
<translate > Sign up
Sign up
</translate>
</h3> </h3>
<template v-if="openRegistrations"> <template v-if="openRegistrations">
<p> <p>
<translate > Sign up now to keep track of your favorites, create playlists, discover new content and much more!
Sign up now to keep track of your favorites, create playlists, discover new content and much more!
</translate>
</p> </p>
<p v-if="defaultUploadQuota"> <p v-if="defaultUploadQuota">
<translate {{ $t('Users on this pod also get %{ quota } of free storage to upload their own content!', { quota: humanSize(defaultUploadQuota * 1000 * 1000) }) }}
:translate-params="{quota: humanSize(defaultUploadQuota * 1000 * 1000)}"
>
Users on this pod also get %{ quota } of free storage to upload their own content!
</translate>
</p> </p>
<signup-form <signup-form
button-classes="success" button-classes="success"
@ -263,7 +223,7 @@ whenever(() => store.state.auth.authenticated, () => {
/> />
</template> </template>
<div v-else> <div v-else>
<p > <p>
Registrations are closed on this pod. You can signup on another pod using the link below. Registrations are closed on this pod. You can signup on another pod using the link below.
</p> </p>
<a <a
@ -272,16 +232,14 @@ whenever(() => store.state.auth.authenticated, () => {
href="https://funkwhale.audio/#get-started" href="https://funkwhale.audio/#get-started"
> >
<i class="external alternate icon" /> <i class="external alternate icon" />
<translate >Find another pod</translate> Find another pod
</a> </a>
</div> </div>
</div> </div>
<div class="four wide column"> <div class="four wide column">
<h3 class="header"> <h3 class="header">
<translate > Useful links
Useful links
</translate>
</h3> </h3>
<div class="ui relaxed list"> <div class="ui relaxed list">
<div class="item"> <div class="item">
@ -292,14 +250,10 @@ whenever(() => store.state.auth.authenticated, () => {
class="header" class="header"
to="/library" to="/library"
> >
<translate > Browse public content
Browse public content
</translate>
</router-link> </router-link>
<div class="description"> <div class="description">
<translate > Listen to public albums and playlists shared on this pod
Listen to public albums and playlists shared on this pod
</translate>
</div> </div>
</div> </div>
</div> </div>
@ -312,12 +266,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank" target="_blank"
rel="noopener" rel="noopener"
> >
<translate >Mobile apps</translate> Mobile apps
</a> </a>
<div class="description"> <div class="description">
<translate > Use Funkwhale on other devices with our apps
Use Funkwhale on other devices with our apps
</translate>
</div> </div>
</div> </div>
</div> </div>
@ -330,12 +282,10 @@ whenever(() => store.state.auth.authenticated, () => {
target="_blank" target="_blank"
rel="noopener" rel="noopener"
> >
<translate >User guides</translate> User guides
</a> </a>
<div class="description"> <div class="description">
<translate > Discover everything you need to know about Funkwhale and its features
Discover everything you need to know about Funkwhale and its features
</translate>
</div> </div>
</div> </div>
</div> </div>
@ -352,22 +302,16 @@ whenever(() => store.state.auth.authenticated, () => {
:limit="10" :limit="10"
> >
<template #title> <template #title>
<translate > Recently added albums
Recently added albums
</translate>
</template> </template>
<router-link to="/library"> <router-link to="/library">
<translate > View more
View more
</translate>
<div class="ui hidden divider" /> <div class="ui hidden divider" />
</router-link> </router-link>
</album-widget> </album-widget>
<div class="ui hidden section divider" /> <div class="ui hidden section divider" />
<h3 class="ui header"> <h3 class="ui header">
<translate > New channels
New channels
</translate>
</h3> </h3>
<channels-widget <channels-widget
:show-modification-date="true" :show-modification-date="true"

View File

@ -20,15 +20,11 @@ const labels = computed(() => ({
<h1 class="ui huge header"> <h1 class="ui huge header">
<i class="warning icon" /> <i class="warning icon" />
<div class="content"> <div class="content">
<translate > Page not found!
Page not found!
</translate>
</div> </div>
</h1> </h1>
<p> <p>
<translate > Sorry, the page you asked for does not exist:
Sorry, the page you asked for does not exist:
</translate>
</p> </p>
<a :href="path">{{ path }}</a> <a :href="path">{{ path }}</a>
<div class="ui hidden divider" /> <div class="ui hidden divider" />
@ -36,9 +32,7 @@ const labels = computed(() => ({
class="ui icon labeled right button" class="ui icon labeled right button"
to="/" to="/"
> >
<translate > Go to home page
Go to home page
</translate>
<i class="right arrow icon" /> <i class="right arrow icon" />
</router-link> </router-link>
</div> </div>

View File

@ -5,6 +5,7 @@ import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscre
import { nextTick, ref, computed, watchEffect, onMounted } from 'vue' import { nextTick, ref, computed, watchEffect, onMounted } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap' import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useStore } from '~/store' import { useStore } from '~/store'
import { usePlayer } from '~/composables/audio/player' import { usePlayer } from '~/composables/audio/player'
@ -17,10 +18,6 @@ import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue' import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import PlayerControls from '~/components/audio/PlayerControls.vue' import PlayerControls from '~/components/audio/PlayerControls.vue'
import MilkDrop from '~/components/audio/visualizer/MilkDrop.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 VirtualList from '~/components/vui/list/VirtualList.vue'
import QueueItem from '~/components/QueueItem.vue' import QueueItem from '~/components/QueueItem.vue'
@ -301,20 +298,14 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui small warning message" class="ui small warning message"
> >
<h3 class="header"> <h3 class="header">
<translate > The track cannot be loaded
The track cannot be loaded
</translate>
</h3> </h3>
<p v-if="hasNext && isPlaying"> <p v-if="hasNext && isPlaying">
<translate > The next track will play automatically in a few seconds
The next track will play automatically in a few seconds
</translate>
<i class="loading spinner icon" /> <i class="loading spinner icon" />
</p> </p>
<p> <p>
<translate > You may have a connectivity issue.
You may have a connectivity issue.
</translate>
</p> </p>
</div> </div>
<div <div
@ -413,17 +404,13 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
class="ui right floated basic button" class="ui right floated basic button"
@click="$store.commit('ui/queueFocused', null)" @click="$store.commit('ui/queueFocused', null)"
> >
<translate > Close
Close
</translate>
</button> </button>
<button <button
class="ui right floated basic button danger" class="ui right floated basic button danger"
@click="clear" @click="clear"
> >
<translate > Clear
Clear
</translate>
</button> </button>
{{ labels.queue }} {{ labels.queue }}
<div class="sub header"> <div class="sub header">
@ -477,22 +464,17 @@ const coverType = useStorage('queue:cover-type', CoverType.COVER_ART)
> >
<div class="content"> <div class="content">
<h3 class="header"> <h3 class="header">
<i class="feed icon" /> <translate > <i class="feed icon" />
You have a radio playing You have a radio playing
</translate>
</h3> </h3>
<p> <p>
<translate > New tracks will be appended here automatically.
New tracks will be appended here automatically.
</translate>
</p> </p>
<button <button
class="ui basic primary button" class="ui basic primary button"
@click="$store.dispatch('radios/stop')" @click="$store.dispatch('radios/stop')"
> >
<translate > Stop radio
Stop radio
</translate>
</button> </button>
</div> </div>
</div> </div>

View File

@ -172,9 +172,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'rss'" @click.prevent="type = 'rss'"
> >
<i class="feed icon" /> <i class="feed icon" />
<translate > RSS
RSS
</translate>
</button> </button>
<div class="or" /> <div class="or" />
<button <button
@ -182,9 +180,7 @@ watch(() => props.initialId, () => {
@click.prevent="type = 'artists'" @click.prevent="type = 'artists'"
> >
<i class="globe icon" /> <i class="globe icon" />
<translate > Fediverse
Fediverse
</translate>
</button> </button>
</div> </div>
<div v-else> <div v-else>
@ -199,9 +195,7 @@ watch(() => props.initialId, () => {
class="ui negative message" class="ui negative message"
> >
<h3 class="header"> <h3 class="header">
<translate > Error while fetching object
Error while fetching object
</translate>
</h3> </h3>
<ul class="list"> <ul class="list">
<li <li
@ -217,14 +211,10 @@ watch(() => props.initialId, () => {
{{ labels.fieldLabel }} {{ labels.fieldLabel }}
</label> </label>
<p v-if="type === 'rss'"> <p v-if="type === 'rss'">
<translate > Use this form to subscribe to an RSS feed from its URL.
Use this form to subscribe to an RSS feed from its URL.
</translate>
</p> </p>
<p v-else-if="type === 'artists'"> <p v-else-if="type === 'artists'">
<translate > Use this form to subscribe to a channel hosted somewhere else on the Fediverse.
Use this form to subscribe to a channel hosted somewhere else on the Fediverse.
</translate>
</p> </p>
<input <input
id="object-id" id="object-id"
@ -241,9 +231,7 @@ watch(() => props.initialId, () => {
:class="['ui', 'primary', {loading: isLoading}, 'button']" :class="['ui', 'primary', {loading: isLoading}, 'button']"
:disabled="isLoading || !id || id.length === 0" :disabled="isLoading || !id || id.length === 0"
> >
<translate > Search
Search
</translate>
</button> </button>
</form> </form>
<div <div
@ -252,9 +240,7 @@ watch(() => props.initialId, () => {
class="ui warning message" class="ui warning message"
> >
<p> <p>
<translate > This kind of object isn't supported yet
This kind of object isn't supported yet
</translate>
</p> </p>
</div> </div>
</div> </div>

View File

@ -71,9 +71,7 @@ const checkAndSwitch = async (url: string) => {
@update:show="isError = false" @update:show="isError = false"
> >
<h3 class="header"> <h3 class="header">
<translate > Choose your instance
Choose your instance
</translate>
</h3> </h3>
<div class="scrolling content"> <div class="scrolling content">
<div <div
@ -82,20 +80,14 @@ const checkAndSwitch = async (url: string) => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > It is not possible to connect to the given URL
It is not possible to connect to the given URL
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li> <li>
<translate > The server might be down
The server might be down
</translate>
</li> </li>
<li> <li>
<translate > The given address is not a Funkwhale server
The given address is not a Funkwhale server
</translate>
</li> </li>
</ul> </ul>
</div> </div>
@ -107,7 +99,6 @@ const checkAndSwitch = async (url: string) => {
v-if="$store.state.instance.instanceUrl" v-if="$store.state.instance.instanceUrl"
v-translate="{url: $store.state.instance.instanceUrl, hostname: $store.getters['instance/domain'] }" v-translate="{url: $store.state.instance.instanceUrl, hostname: $store.getters['instance/domain'] }"
class="description" class="description"
> >
You are currently connected to <a You are currently connected to <a
href="%{ url }" href="%{ url }"
@ -115,12 +106,10 @@ 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. >%{ 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>
<p v-else> <p v-else>
<translate > To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices.
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> </p>
<div class="field"> <div class="field">
<label for="instance-picker"><translate >Instance URL</translate></label> <label for="instance-picker">Instance URL</label>
<div class="ui action input"> <div class="ui action input">
<input <input
id="instance-picker" id="instance-picker"
@ -132,9 +121,7 @@ const checkAndSwitch = async (url: string) => {
type="submit" type="submit"
:class="['ui', 'icon', {loading: isLoading}, 'button']" :class="['ui', 'icon', {loading: isLoading}, 'button']"
> >
<translate > Submit
Submit
</translate>
</button> </button>
</div> </div>
</div> </div>
@ -146,9 +133,7 @@ const checkAndSwitch = async (url: string) => {
> >
<div class="field"> <div class="field">
<h4> <h4>
<translate > Suggested choices
Suggested choices
</translate>
</h4> </h4>
<button <button
v-for="(url, key) in suggestedInstances" v-for="(url, key) in suggestedInstances"
@ -163,9 +148,7 @@ const checkAndSwitch = async (url: string) => {
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui basic cancel button"> <button class="ui basic cancel button">
<translate > Cancel
Cancel
</translate>
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>

View File

@ -110,9 +110,7 @@ const player = computed(() => [
<template> <template>
<semantic-modal v-model:show="showRef"> <semantic-modal v-model:show="showRef">
<header class="header"> <header class="header">
<translate > Keyboard shortcuts
Keyboard shortcuts
</translate>
</header> </header>
<section class="scrolling content"> <section class="scrolling content">
<div class="ui stackable two column grid"> <div class="ui stackable two column grid">
@ -156,9 +154,7 @@ const player = computed(() => [
</section> </section>
<footer class="actions"> <footer class="actions">
<button class="ui basic cancel button"> <button class="ui basic cancel button">
<translate > Close
Close
</translate>
</button> </button>
</footer> </footer>
</semantic-modal> </semantic-modal>

View File

@ -149,9 +149,7 @@ onMounted(() => {
</div> </div>
<div class="menu"> <div class="menu">
<h3 class="header"> <h3 class="header">
<translate > Administration
Administration
</translate>
</h3> </h3>
<div class="divider" /> <div class="divider" />
<router-link <router-link
@ -166,9 +164,7 @@ onMounted(() => {
> >
{{ $store.state.ui.notifications.pendingReviewEdits }} {{ $store.state.ui.notifications.pendingReviewEdits }}
</div> </div>
<translate > Library
Library
</translate>
</router-link> </router-link>
<router-link <router-link
v-if="$store.state.auth.availablePermissions['moderation']" v-if="$store.state.auth.availablePermissions['moderation']"
@ -182,27 +178,21 @@ onMounted(() => {
> >
{{ $store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests }} {{ $store.state.ui.notifications.pendingReviewReports + $store.state.ui.notifications.pendingReviewRequests }}
</div> </div>
<translate > Moderation
Moderation
</translate>
</router-link> </router-link>
<router-link <router-link
v-if="$store.state.auth.availablePermissions['settings']" v-if="$store.state.auth.availablePermissions['settings']"
class="item" class="item"
:to="{name: 'manage.users.users.list'}" :to="{name: 'manage.users.users.list'}"
> >
<translate > Users
Users
</translate>
</router-link> </router-link>
<router-link <router-link
v-if="$store.state.auth.availablePermissions['settings']" v-if="$store.state.auth.availablePermissions['settings']"
class="item" class="item"
:to="{path: '/manage/settings'}" :to="{path: '/manage/settings'}"
> >
<translate > Settings
Settings
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -327,17 +317,17 @@ onMounted(() => {
</div> </div>
<div class="content"> <div class="content">
<fieldset <fieldset
v-for="t in themes" v-for="th in themes"
:key="t.key" :key="th.key"
> >
<input <input
:id="t.key" :id="th.key"
v-model="theme" v-model="theme"
type="radio" type="radio"
name="theme" name="theme"
:value="t.key" :value="th.key"
> >
<label :for="t.key">{{ t.name }}</label> <label :for="th.key">{{ th.name }}</label>
</fieldset> </fieldset>
</div> </div>
</semantic-modal> </semantic-modal>
@ -362,18 +352,14 @@ onMounted(() => {
class="ui fluid tiny primary button" class="ui fluid tiny primary button"
:to="{name: 'login'}" :to="{name: 'login'}"
> >
<translate > Login
Login
</translate>
</router-link> </router-link>
<div class="ui small hidden divider" /> <div class="ui small hidden divider" />
<router-link <router-link
class="ui fluid tiny button" class="ui fluid tiny button"
:to="{path: '/signup'}" :to="{path: '/signup'}"
> >
<translate > Create an account
Create an account
</translate>
</router-link> </router-link>
</div> </div>
<nav <nav
@ -385,9 +371,7 @@ onMounted(() => {
id="navigation-label" id="navigation-label"
class="visually-hidden" class="visually-hidden"
> >
<translate > Main navigation
Main navigation
</translate>
</h1> </h1>
<div class="ui small hidden divider" /> <div class="ui small hidden divider" />
<section <section
@ -407,9 +391,7 @@ onMounted(() => {
@click="expanded = 'explore'" @click="expanded = 'explore'"
@focus="expanded = 'explore'" @focus="expanded = 'explore'"
> >
<translate > Explore
Explore
</translate>
<i <i
v-if="expanded !== 'explore'" v-if="expanded !== 'explore'"
class="angle right icon" class="angle right icon"
@ -420,58 +402,44 @@ onMounted(() => {
class="item" class="item"
:to="{name: 'search'}" :to="{name: 'search'}"
> >
<i class="search icon" /><translate > <i class="search icon" /> Search
Search
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.index'}" :to="{name: 'library.index'}"
active-class="_active" active-class="_active"
> >
<i class="music icon" /><translate > <i class="music icon" /> Browse
Browse
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.podcasts.browse'}" :to="{name: 'library.podcasts.browse'}"
> >
<i class="podcast icon" /><translate > <i class="podcast icon" /> Podcasts
Podcasts
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.albums.browse'}" :to="{name: 'library.albums.browse'}"
> >
<i class="compact disc icon" /><translate > <i class="compact disc icon" /> Albums
Albums
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.artists.browse'}" :to="{name: 'library.artists.browse'}"
> >
<i class="user icon" /><translate > <i class="user icon" /> Artists
Artists
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.playlists.browse'}" :to="{name: 'library.playlists.browse'}"
> >
<i class="list icon" /><translate > <i class="list icon" /> Playlists
Playlists
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.radios.browse'}" :to="{name: 'library.radios.browse'}"
> >
<i class="feed icon" /><translate > <i class="feed icon" /> Radios
Radios
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -486,9 +454,7 @@ onMounted(() => {
@click="expanded = 'myLibrary'" @click="expanded = 'myLibrary'"
@focus="expanded = 'myLibrary'" @focus="expanded = 'myLibrary'"
> >
<translate > My Library
My Library
</translate>
<i <i
v-if="expanded !== 'myLibrary'" v-if="expanded !== 'myLibrary'"
class="angle right icon" class="angle right icon"
@ -499,49 +465,37 @@ onMounted(() => {
class="item" class="item"
:to="{name: 'library.me'}" :to="{name: 'library.me'}"
> >
<i class="music icon" /><translate > <i class="music icon" /> Browse
Browse
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.albums.me'}" :to="{name: 'library.albums.me'}"
> >
<i class="compact disc icon" /><translate > <i class="compact disc icon" /> Albums
Albums
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.artists.me'}" :to="{name: 'library.artists.me'}"
> >
<i class="user icon" /><translate > <i class="user icon" /> Artists
Artists
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.playlists.me'}" :to="{name: 'library.playlists.me'}"
> >
<i class="list icon" /><translate > <i class="list icon" /> Playlists
Playlists
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'library.radios.me'}" :to="{name: 'library.radios.me'}"
> >
<i class="feed icon" /><translate > <i class="feed icon" /> Radios
Radios
</translate>
</router-link> </router-link>
<router-link <router-link
class="item" class="item"
:to="{name: 'favorites'}" :to="{name: 'favorites'}"
> >
<i class="heart icon" /><translate > <i class="heart icon" /> Favorites
Favorites
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -550,15 +504,11 @@ onMounted(() => {
class="header item" class="header item"
:to="{name: 'subscriptions'}" :to="{name: 'subscriptions'}"
> >
<translate > Channels
Channels
</translate>
</router-link> </router-link>
<div class="item"> <div class="item">
<h3 class="header"> <h3 class="header">
<translate > More
More
</translate>
</h3> </h3>
<div class="menu"> <div class="menu">
<router-link <router-link
@ -566,9 +516,7 @@ onMounted(() => {
to="/about" to="/about"
active-class="router-link-exact-active active" active-class="router-link-exact-active active"
> >
<i class="info icon" /><translate > <i class="info icon" /> About this pod
About this pod
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>

View File

@ -110,9 +110,7 @@ const save = async () => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while saving settings
Error while saving settings
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -127,9 +125,7 @@ const save = async () => {
v-if="result" v-if="result"
class="ui positive message" class="ui positive message"
> >
<translate > Settings updated successfully.
Settings updated successfully.
</translate>
</div> </div>
<div <div
v-for="(setting, key) in settings" v-for="(setting, key) in settings"
@ -229,9 +225,7 @@ const save = async () => {
<div v-if="values[setting.identifier]"> <div v-if="values[setting.identifier]">
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<h3 class="ui header"> <h3 class="ui header">
<translate > Current image
Current image
</translate>
</h3> </h3>
<img <img
v-if="values[setting.identifier]" v-if="values[setting.identifier]"
@ -246,9 +240,7 @@ const save = async () => {
type="submit" type="submit"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'success', 'button']"
> >
<translate > Save
Save
</translate>
</button> </button>
</form> </form>
</template> </template>

View File

@ -69,17 +69,13 @@ const move = (idx: number, increment: number) => {
:class="[{active: !isPreviewing}, 'item']" :class="[{active: !isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = false" @click.stop.prevent="isPreviewing = false"
> >
<translate > Edit form
Edit form
</translate>
</button> </button>
<button <button
:class="[{active: isPreviewing}, 'item']" :class="[{active: isPreviewing}, 'item']"
@click.stop.prevent="isPreviewing = true" @click.stop.prevent="isPreviewing = true"
> >
<translate > Preview form
Preview form
</translate>
</button> </button>
</div> </div>
<div <div
@ -99,12 +95,10 @@ const move = (idx: number, increment: number) => {
> >
<div class="field"> <div class="field">
<label for="help-text"> <label for="help-text">
<translate >Help text</translate> Help text
</label> </label>
<p> <p>
<translate > An optional text to be displayed at the start of the sign-up form.
An optional text to be displayed at the start of the sign-up form.
</translate>
</p> </p>
<content-form <content-form
v-if="value.help_text" v-if="value.help_text"
@ -115,32 +109,24 @@ const move = (idx: number, increment: number) => {
</div> </div>
<div class="field"> <div class="field">
<label> <label>
<translate >Additional fields</translate> Additional fields
</label> </label>
<p> <p>
<translate > Additional form fields to be displayed in the form. Only shown if manual sign-up validation is enabled.
Additional form fields to be displayed in the form. Only shown if manual sign-up validation is enabled.
</translate>
</p> </p>
<table v-if="value.fields?.length > 0"> <table v-if="value.fields?.length > 0">
<thead> <thead>
<tr> <tr>
<th> <th>
<translate > Field label
Field label
</translate>
</th> </th>
<th> <th>
<translate > Field type
Field type
</translate>
</th> </th>
<th> <th>
<translate > Required
Required
</translate>
</th> </th>
<th><span class="visually-hidden"><translate >Actions</translate></span></th> <th><span class="visually-hidden">Actions</span></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -158,28 +144,20 @@ const move = (idx: number, increment: number) => {
<td> <td>
<select v-model="field.input_type"> <select v-model="field.input_type">
<option value="short_text"> <option value="short_text">
<translate > Short text
Short text
</translate>
</option> </option>
<option value="long_text"> <option value="long_text">
<translate > Long text
Long text
</translate>
</option> </option>
</select> </select>
</td> </td>
<td> <td>
<select v-model="field.required"> <select v-model="field.required">
<option :value="true"> <option :value="true">
<translate > Yes
Yes
</translate>
</option> </option>
<option :value="false"> <option :value="false">
<translate > No
No
</translate>
</option> </option>
</select> </select>
</td> </td>
@ -214,9 +192,7 @@ const move = (idx: number, increment: number) => {
class="ui basic button" class="ui basic button"
@click.stop.prevent="addField" @click.stop.prevent="addField"
> >
<translate > Add a new field
Add a new field
</translate>
</button> </button>
</div> </div>
</div> </div>

View File

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

View File

@ -165,9 +165,7 @@ defineExpose({
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while saving channel
Error while saving channel
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -184,9 +182,7 @@ defineExpose({
class="ui grouped channel-type required field" class="ui grouped channel-type required field"
> >
<legend> <legend>
<translate > What will this channel be used for?
What will this channel be used for?
</translate>
</legend> </legend>
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<div class="field"> <div class="field">
@ -214,7 +210,7 @@ defineExpose({
<template v-if="!creating || step === 2"> <template v-if="!creating || step === 2">
<div class="ui required field"> <div class="ui required field">
<label for="channel-name"> <label for="channel-name">
<translate >Name</translate> Name
</label> </label>
<input <input
v-model="newValues.name" v-model="newValues.name"
@ -225,7 +221,7 @@ defineExpose({
</div> </div>
<div class="ui required field"> <div class="ui required field">
<label for="channel-username"> <label for="channel-username">
<translate >Fediverse handle</translate> Fediverse handle
</label> </label>
<div class="ui left labeled input"> <div class="ui left labeled input">
<div class="ui basic label"> <div class="ui basic label">
@ -242,9 +238,7 @@ defineExpose({
<template v-if="creating"> <template v-if="creating">
<div class="ui small hidden divider" /> <div class="ui small hidden divider" />
<p> <p>
<translate > Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.
Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.
</translate>
</p> </p>
</template> </template>
</div> </div>
@ -254,9 +248,7 @@ defineExpose({
:image-class="newValues.content_category === 'podcast' ? '' : 'circular'" :image-class="newValues.content_category === 'podcast' ? '' : 'circular'"
@delete="newValues.cover = null" @delete="newValues.cover = null"
> >
<translate > Channel Picture
Channel Picture
</translate>
</attachment-input> </attachment-input>
</div> </div>
<div class="ui small hidden divider" /> <div class="ui small hidden divider" />
@ -264,7 +256,7 @@ defineExpose({
<div class="ten wide column"> <div class="ten wide column">
<div class="ui field"> <div class="ui field">
<label for="channel-tags"> <label for="channel-tags">
<translate >Tags</translate> Tags
</label> </label>
<tags-selector <tags-selector
id="channel-tags" id="channel-tags"
@ -279,7 +271,7 @@ defineExpose({
> >
<div class="ui required field"> <div class="ui required field">
<label for="channel-language"> <label for="channel-language">
<translate >Language</translate> Language
</label> </label>
<select <select
id="channel-language" id="channel-language"
@ -302,7 +294,7 @@ defineExpose({
<div class="ui small hidden divider" /> <div class="ui small hidden divider" />
<div class="ui field"> <div class="ui field">
<label for="channel-name"> <label for="channel-name">
<translate >Description</translate> Description
</label> </label>
<content-form v-model="newValues.description" /> <content-form v-model="newValues.description" />
</div> </div>
@ -312,7 +304,7 @@ defineExpose({
> >
<div class="ui required field"> <div class="ui required field">
<label for="channel-itunes-category"> <label for="channel-itunes-category">
<translate >Category</translate> Category
</label> </label>
<select <select
id="itunes-category" id="itunes-category"
@ -332,7 +324,7 @@ defineExpose({
</div> </div>
<div class="ui field"> <div class="ui field">
<label for="channel-itunes-category"> <label for="channel-itunes-category">
<translate >Subcategory</translate> Subcategory
</label> </label>
<select <select
id="itunes-category" id="itunes-category"
@ -357,7 +349,7 @@ defineExpose({
> >
<div class="ui field"> <div class="ui field">
<label for="channel-itunes-email"> <label for="channel-itunes-email">
<translate >Owner e-mail address</translate> Owner e-mail address
</label> </label>
<input <input
id="channel-itunes-email" id="channel-itunes-email"
@ -368,7 +360,7 @@ defineExpose({
</div> </div>
<div class="ui field"> <div class="ui field">
<label for="channel-itunes-name"> <label for="channel-itunes-name">
<translate >Owner name</translate> Owner name
</label> </label>
<input <input
id="channel-itunes-name" id="channel-itunes-name"
@ -379,9 +371,7 @@ defineExpose({
</div> </div>
</div> </div>
<p> <p>
<translate > Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.
Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.
</translate>
</p> </p>
</template> </template>
</template> </template>
@ -390,9 +380,7 @@ defineExpose({
class="ui active inverted dimmer" class="ui active inverted dimmer"
> >
<div class="ui text loader"> <div class="ui text loader">
<translate > Loading
Loading
</translate>
</div> </div>
</div> </div>
</form> </form>

View File

@ -84,9 +84,7 @@ fetchData()
:class="['ui', 'basic', 'button']" :class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)" @click="fetchData(nextPage)"
> >
<translate > Show more
Show more
</translate>
</button> </button>
</template> </template>
<template v-if="!isLoading && albums.length === 0"> <template v-if="!isLoading && albums.length === 0">
@ -95,9 +93,7 @@ fetchData()
@refresh="fetchData()" @refresh="fetchData()"
> >
<p> <p>
<translate > You may need to subscribe to this channel to see its contents.
You may need to subscribe to this channel to see its contents.
</translate>
</p> </p>
</empty-state> </empty-state>
</template> </template>

View File

@ -77,9 +77,7 @@ fetchData()
:class="['ui', 'basic', 'button']" :class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)" @click="fetchData(nextPage)"
> >
<translate > Show more
Show more
</translate>
</button> </button>
</template> </template>
<template v-if="!isLoading && channels.length === 0"> <template v-if="!isLoading && channels.length === 0">

View File

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

View File

@ -40,19 +40,16 @@ const toggle = () => {
<i class="heart icon" /> <i class="heart icon" />
<translate <translate
v-if="isApproved" v-if="isApproved"
> >
Unfollow Unfollow
</translate> </translate>
<translate <translate
v-else-if="isPending" v-else-if="isPending"
> >
Cancel follow request Cancel follow request
</translate> </translate>
<translate <translate
v-else v-else
> >
Follow Follow
</translate> </translate>

View File

@ -138,7 +138,7 @@ const openMenu = () => {
v-else v-else
:class="[playIconClass, 'icon']" :class="[playIconClass, 'icon']"
/> />
<template v-if="!discrete && !iconOnly">&nbsp;<slot><translate >Play</translate></slot></template> <template v-if="!discrete && !iconOnly">&nbsp;<slot>Play</slot></template>
</button> </button>
<button <button
v-if="!discrete && !iconOnly" v-if="!discrete && !iconOnly"
@ -156,7 +156,7 @@ const openMenu = () => {
:title="labels.addToQueue" :title="labels.addToQueue"
@click.stop.prevent="enqueue" @click.stop.prevent="enqueue"
> >
<i class="plus icon" /><translate >Add to queue</translate> <i class="plus icon" />Add to queue
</button> </button>
<button <button
class="item basic" class="item basic"
@ -181,7 +181,7 @@ const openMenu = () => {
:title="labels.startRadio" :title="labels.startRadio"
@click.stop.prevent="$store.dispatch('radios/start', {type: 'similar', objectId: track?.id})" @click.stop.prevent="$store.dispatch('radios/start', {type: 'similar', objectId: track?.id})"
> >
<i class="feed icon" /><translate >Play radio</translate> <i class="feed icon" />Play radio
</button> </button>
<button <button
v-if="track" v-if="track"
@ -190,7 +190,7 @@ const openMenu = () => {
@click.stop="$store.commit('playlists/chooseTrack', track)" @click.stop="$store.commit('playlists/chooseTrack', track)"
> >
<i class="list icon" /> <i class="list icon" />
<translate >Add to playlist</translate> Add to playlist
</button> </button>
<button <button
v-if="track && $route.name !== 'library.tracks.detail'" v-if="track && $route.name !== 'library.tracks.detail'"
@ -200,11 +200,9 @@ const openMenu = () => {
<i class="info icon" /> <i class="info icon" />
<translate <translate
v-if="track.artist?.content_category === 'podcast'" v-if="track.artist?.content_category === 'podcast'"
>Episode details</translate> >Episode details</translate>
<translate <translate
v-else v-else
>Track details</translate> >Track details</translate>
</button> </button>
<div class="divider" /> <div class="divider" />

View File

@ -133,9 +133,7 @@ const hideArtist = () => {
id="player-label" id="player-label"
class="visually-hidden" class="visually-hidden"
> >
<translate > Audio player and controls
Audio player and controls
</translate>
</h1> </h1>
<div <div
class="ui inverted segment fixed-controls" class="ui inverted segment fixed-controls"

View File

@ -73,9 +73,7 @@ const labels = computed(() => ({
<template> <template>
<div> <div>
<h2> <h2>
<translate > Search for some music
Search for some music
</translate>
</h2> </h2>
<div :class="['ui', {'loading': isLoading }, 'search']"> <div :class="['ui', {'loading': isLoading }, 'search']">
<div class="ui icon big input"> <div class="ui icon big input">
@ -91,9 +89,7 @@ const labels = computed(() => ({
</div> </div>
<template v-if="query.length > 0"> <template v-if="query.length > 0">
<h3 class="ui title"> <h3 class="ui title">
<translate > Artists
Artists
</translate>
</h3> </h3>
<div v-if="results.artists.length > 0"> <div v-if="results.artists.length > 0">
<div class="ui cards"> <div class="ui cards">
@ -105,16 +101,12 @@ const labels = computed(() => ({
</div> </div>
</div> </div>
<p v-else> <p v-else>
<translate > No artist matched your query
No artist matched your query
</translate>
</p> </p>
</template> </template>
<template v-if="query.length > 0"> <template v-if="query.length > 0">
<h3 class="ui title"> <h3 class="ui title">
<translate > Albums
Albums
</translate>
</h3> </h3>
<div <div
v-if="results.albums.length > 0" v-if="results.albums.length > 0"
@ -132,9 +124,7 @@ const labels = computed(() => ({
</div> </div>
</div> </div>
<p v-else> <p v-else>
<translate > No album matched your query
No album matched your query
</translate>
</p> </p>
</template> </template>
</div> </div>

View File

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

View File

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

View File

@ -200,9 +200,7 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
> >
<div class="ui icon header"> <div class="ui icon header">
<i class="music icon" /> <i class="music icon" />
<translate > Nothing found
Nothing found
</translate>
</div> </div>
<div <div
v-if="isLoading" v-if="isLoading"
@ -217,9 +215,7 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
:class="['ui', 'basic', 'button']" :class="['ui', 'basic', 'button']"
@click="fetchData(nextPage as string)" @click="fetchData(nextPage as string)"
> >
<translate > Show more
Show more
</translate>
</button> </button>
</template> </template>
</div> </div>

View File

@ -72,23 +72,17 @@ store.state.auth.applicationSecret = undefined
</div> </div>
<template v-else> <template v-else>
<router-link :to="{name: 'settings'}"> <router-link :to="{name: 'settings'}">
<translate > Back to settings
Back to settings
</translate>
</router-link> </router-link>
<h2 class="ui header"> <h2 class="ui header">
<translate > Application details
Application details
</translate>
</h2> </h2>
<div class="ui form"> <div class="ui form">
<p> <p>
<translate > Application ID and secret are really sensitive values and must be treated like passwords. Do not share those with anyone else.
Application ID and secret are really sensitive values and must be treated like passwords. Do not share those with anyone else.
</translate>
</p> </p>
<div class="field"> <div class="field">
<label for="copy-id"><translate >Application ID</translate></label> <label for="copy-id">Application ID</label>
<copy-input <copy-input
id="copy-id" id="copy-id"
:value="application.client_id" :value="application.client_id"
@ -100,18 +94,14 @@ store.state.auth.applicationSecret = undefined
> >
<div class="ui small warning message"> <div class="ui small warning message">
<h3 class="header"> <h3 class="header">
<translate > Keep a copy of this token in a safe place
Keep a copy of this token in a safe place
</translate>
</h3> </h3>
<p> <p>
<translate > You won't be able to see it again once you leave this screen.
You won't be able to see it again once you leave this screen.
</translate>
</p> </p>
</div> </div>
<label for="copy-secret"><translate >Application secret</translate></label> <label for="copy-secret">Application secret</label>
<copy-input <copy-input
id="copy-secret" id="copy-secret"
:value="secret" :value="secret"
@ -121,7 +111,7 @@ store.state.auth.applicationSecret = undefined
v-if="application.token != undefined" v-if="application.token != undefined"
class="field" class="field"
> >
<label for="copy-secret"><translate >Access token</translate></label> <label for="copy-secret">Access token</label>
<copy-input <copy-input
id="copy-secret" id="copy-secret"
:value="application.token" :value="application.token"
@ -131,14 +121,12 @@ store.state.auth.applicationSecret = undefined
@click.prevent="refreshToken" @click.prevent="refreshToken"
> >
<i class="refresh icon" /> <i class="refresh icon" />
<translate >Regenerate token</translate> Regenerate token
</a> </a>
</div> </div>
</div> </div>
<h2 class="ui header"> <h2 class="ui header">
<translate > Edit application
Edit application
</translate>
</h2> </h2>
<application-form <application-form
:app="application" :app="application"

View File

@ -120,9 +120,7 @@ const allScopes = computed(() => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > We cannot save your changes
We cannot save your changes
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -134,7 +132,7 @@ const allScopes = computed(() => {
</ul> </ul>
</div> </div>
<div class="ui field"> <div class="ui field">
<label for="application-name"><translate >Name</translate></label> <label for="application-name">Name</label>
<input <input
id="application-name" id="application-name"
v-model="fields.name" v-model="fields.name"
@ -144,7 +142,7 @@ const allScopes = computed(() => {
> >
</div> </div>
<div class="ui field"> <div class="ui field">
<label for="redirect-uris"><translate >Redirect URI</translate></label> <label for="redirect-uris">Redirect URI</label>
<input <input
id="redirect-uris" id="redirect-uris"
v-model="fields.redirect_uris" v-model="fields.redirect_uris"
@ -152,17 +150,13 @@ const allScopes = computed(() => {
type="text" type="text"
> >
<p class="help"> <p class="help">
<translate > Use "urn:ietf:wg:oauth:2.0:oob" as a redirect URI if your application is not served on the web.
Use "urn:ietf:wg:oauth:2.0:oob" as a redirect URI if your application is not served on the web.
</translate>
</p> </p>
</div> </div>
<div class="ui field"> <div class="ui field">
<label><translate >Scopes</translate></label> <label>Scopes</label>
<p> <p>
<translate > Checking the parent "Read" or "Write" scopes implies access to all the corresponding children scopes.
Checking the parent "Read" or "Write" scopes implies access to all the corresponding children scopes.
</translate>
</p> </p>
<div class="ui stackable two column grid"> <div class="ui stackable two column grid">
<div <div
@ -210,13 +204,11 @@ const allScopes = computed(() => {
> >
<translate <translate
v-if="app !== null" v-if="app !== null"
> >
Update application Update application
</translate> </translate>
<translate <translate
v-else v-else
> >
Create application Create application
</translate> </translate>

View File

@ -55,14 +55,10 @@ const created = (application: Application) => {
<div class="ui vertical stripe segment"> <div class="ui vertical stripe segment">
<section class="ui text container"> <section class="ui text container">
<router-link :to="{name: 'settings'}"> <router-link :to="{name: 'settings'}">
<translate > Back to settings
Back to settings
</translate>
</router-link> </router-link>
<h2 class="ui header"> <h2 class="ui header">
<translate > Create a new application
Create a new application
</translate>
</h2> </h2>
<application-form <application-form
:defaults="defaults" :defaults="defaults"

View File

@ -119,9 +119,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
<section class="ui vertical stripe segment"> <section class="ui vertical stripe segment">
<div class="ui small text container"> <div class="ui small text container">
<h2> <h2>
<i class="lock open icon" /><translate > <i class="lock open icon" /> Authorize third-party app
Authorize third-party app
</translate>
</h2> </h2>
<div <div
v-if="errors.length > 0" v-if="errors.length > 0"
@ -132,17 +130,13 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
v-if="application" v-if="application"
class="header" class="header"
> >
<translate > Error while authorizing application
Error while authorizing application
</translate>
</h4> </h4>
<h4 <h4
v-else v-else
class="header" class="header"
> >
<translate > Error while fetching application data
Error while fetching application data
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -183,20 +177,20 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
> >
<i class="pencil icon" /> <i class="pencil icon" />
<translate >Write-only</translate> Write-only
</span> </span>
<span <span
v-else-if="!topic.write && topic.read" v-else-if="!topic.write && topic.read"
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
> >
<translate >Read-only</translate> Read-only
</span> </span>
<span <span
v-else-if="topic.write && topic.read" v-else-if="topic.write && topic.read"
:class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']" :class="['ui', 'basic', 'right floated', 'tiny', 'vertically-spaced component-label label']"
> >
<i class="pencil icon" /> <i class="pencil icon" />
<translate >Full access</translate> Full access
</span> </span>
<i :class="[topic.icon, 'icon']" /> <i :class="[topic.icon, 'icon']" />
<div class="content"> <div class="content">
@ -207,7 +201,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
</div> </div>
</h4> </h4>
<div v-if="unknownRequestedScopes.length > 0"> <div v-if="unknownRequestedScopes.length > 0">
<p><strong><translate >The application is also requesting the following unknown permissions:</translate></strong></p> <p><strong>The application is also requesting the following unknown permissions:</strong></p>
<ul <ul
v-for="(unknownscope, key) in unknownRequestedScopes" v-for="(unknownscope, key) in unknownRequestedScopes"
:key="key" :key="key"
@ -230,7 +224,6 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
<p <p
v-if="redirectUri === 'urn:ietf:wg:oauth:2.0:oob'" v-if="redirectUri === 'urn:ietf:wg:oauth:2.0:oob'"
v-translate v-translate
> >
You will be shown a code to copy-paste in the application. You will be shown a code to copy-paste in the application.
</p> </p>
@ -244,7 +237,7 @@ whenever(() => props.clientId, fetchApplication, { immediate: true })
</p> </p>
</form> </form>
<div v-else-if="code"> <div v-else-if="code">
<p><strong><translate >Copy-paste the following code in the application:</translate></strong></p> <p><strong>Copy-paste the following code in the application:</strong></p>
<copy-input :value="code" /> <copy-input :value="code" />
</div> </div>
</div> </div>

View File

@ -75,20 +75,14 @@ const submit = async () => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > We cannot log you in
We cannot log you in
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li v-if="errors[0] == 'invalid_credentials' && $store.state.instance.settings.moderation.signup_approval_enabled.value"> <li v-if="errors[0] == 'invalid_credentials' && $store.state.instance.settings.moderation.signup_approval_enabled.value">
<translate > If you signed-up recently, you may need to wait before our moderation team review your account, or verify your e-mail address.
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>
<li v-else-if="errors[0] == 'invalid_credentials'"> <li v-else-if="errors[0] == 'invalid_credentials'">
<translate > Please double-check that your username and password combination is correct and make sure you verified your e-mail address.
Please double-check that your username and password combination is correct and make sure you verified your e-mail address.
</translate>
</li> </li>
<li v-else> <li v-else>
{{ errors[0] }} {{ errors[0] }}
@ -98,11 +92,11 @@ const submit = async () => {
<template v-if="domain === $store.getters['instance/domain']"> <template v-if="domain === $store.getters['instance/domain']">
<div class="field"> <div class="field">
<label for="username-field"> <label for="username-field">
<translate >Username or e-mail address</translate> Username or e-mail address
<template v-if="showSignup"> <template v-if="showSignup">
| |
<router-link :to="{path: '/signup'}"> <router-link :to="{path: '/signup'}">
<translate >Create an account</translate> Create an account
</router-link> </router-link>
</template> </template>
</label> </label>
@ -119,12 +113,12 @@ const submit = async () => {
</div> </div>
<div class="field"> <div class="field">
<label for="password-field"> <label for="password-field">
<translate >Password</translate> | Password |
<router-link <router-link
tabindex="1" tabindex="1"
:to="{name: 'auth.password-reset', query: {email: credentials.username}}" :to="{name: 'auth.password-reset', query: {email: credentials.username}}"
> >
<translate >Reset your password</translate> Reset your password
</router-link> </router-link>
</label> </label>
<password-input <password-input
@ -147,9 +141,7 @@ const submit = async () => {
:class="['ui', {'loading': isLoading}, 'right', 'floated', buttonClasses, 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', buttonClasses, 'button']"
type="submit" type="submit"
> >
<translate > Login
Login
</translate>
</button> </button>
</form> </form>
</template> </template>

View File

@ -19,13 +19,10 @@ const labels = computed(() => ({
class="ui small text container" class="ui small text container"
> >
<h2> <h2>
<translate > Are you sure you want to log out?
Are you sure you want to log out?
</translate>
</h2> </h2>
<p <p
v-translate="{username: $store.state.auth.username}" v-translate="{username: $store.state.auth.username}"
> >
You are currently logged in as %{ username } You are currently logged in as %{ username }
</p> </p>
@ -33,9 +30,7 @@ const labels = computed(() => ({
class="ui button" class="ui button"
@click="$store.dispatch('auth/logout')" @click="$store.dispatch('auth/logout')"
> >
<translate > Yes, log me out!
Yes, log me out!
</translate>
</button> </button>
</div> </div>
<div <div
@ -43,17 +38,13 @@ const labels = computed(() => ({
class="ui small text container" class="ui small text container"
> >
<h2> <h2>
<translate > You aren't currently logged in
You aren't currently logged in
</translate>
</h2> </h2>
<router-link <router-link
to="/login" to="/login"
class="ui button" class="ui button"
> >
<translate > Log in!
Log in!
</translate>
</router-link> </router-link>
</div> </div>
</section> </section>

View File

@ -69,7 +69,7 @@ const submitAndScan = async () => {
target="_blank" target="_blank"
> >
<i class="external icon" /> <i class="external icon" />
<translate >Documentation</translate> Documentation
</a> </a>
</template> </template>
<div class="ui clearing hidden divider" /> <div class="ui clearing hidden divider" />
@ -79,9 +79,7 @@ const submitAndScan = async () => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while saving plugin
Error while saving plugin
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -99,7 +97,7 @@ const submitAndScan = async () => {
v-model="enabled" v-model="enabled"
type="checkbox" type="checkbox"
> >
<label :for="`${plugin.name}-enabled`"><translate >Enabled</translate></label> <label :for="`${plugin.name}-enabled`">Enabled</label>
</div> </div>
</div> </div>
<div class="ui clearing hidden divider" /> <div class="ui clearing hidden divider" />
@ -107,7 +105,7 @@ const submitAndScan = async () => {
v-if="plugin.source" v-if="plugin.source"
class="field" class="field"
> >
<label for="plugin-library"><translate >Library</translate></label> <label for="plugin-library">Library</label>
<select <select
id="plugin-library" id="plugin-library"
v-model="values['library']" v-model="values['library']"
@ -121,9 +119,7 @@ const submitAndScan = async () => {
</option> </option>
</select> </select>
<div> <div>
<translate > Library where files should be imported.
Library where files should be imported.
</translate>
</div> </div>
</div> </div>
<template v-if="(plugin.conf?.length ?? 0) > 0"> <template v-if="(plugin.conf?.length ?? 0) > 0">
@ -198,18 +194,14 @@ const submitAndScan = async () => {
type="submit" type="submit"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
> >
<translate > Save
Save
</translate>
</button> </button>
<button <button
v-if="plugin.source" v-if="plugin.source"
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
@click.prevent="submitAndScan" @click.prevent="submitAndScan"
> >
<translate > Scan
Scan
</translate>
</button> </button>
<div class="ui clearing hidden divider" /> <div class="ui clearing hidden divider" />
</form> </form>

View File

@ -276,9 +276,7 @@ fetchOwnedApps()
<div class="ui vertical stripe segment"> <div class="ui vertical stripe segment">
<section class="ui text container"> <section class="ui text container">
<h2 class="ui header"> <h2 class="ui header">
<translate > Account settings
Account settings
</translate>
</h2> </h2>
<form <form
class="ui form" class="ui form"
@ -289,9 +287,7 @@ fetchOwnedApps()
class="ui positive message" class="ui positive message"
> >
<h4 class="header"> <h4 class="header">
<translate > Settings updated
Settings updated
</translate>
</h4> </h4>
</div> </div>
<div <div
@ -300,9 +296,7 @@ fetchOwnedApps()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Your settings can't be updated
Your settings can't be updated
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -346,18 +340,14 @@ fetchOwnedApps()
:class="['ui', { loading: isLoading }, 'button']" :class="['ui', { loading: isLoading }, 'button']"
type="submit" type="submit"
> >
<translate > Update settings
Update settings
</translate>
</button> </button>
</form> </form>
</section> </section>
<section class="ui text container"> <section class="ui text container">
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<h2 class="ui header"> <h2 class="ui header">
<translate > Avatar
Avatar
</translate>
</h2> </h2>
<div class="ui form"> <div class="ui form">
<div <div
@ -366,9 +356,7 @@ fetchOwnedApps()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Your avatar cannot be saved
Your avatar cannot be saved
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -385,9 +373,7 @@ fetchOwnedApps()
@update:model-value="submitAvatar($event)" @update:model-value="submitAvatar($event)"
@delete="avatar = {uuid: null}" @delete="avatar = {uuid: null}"
> >
<translate > Avatar
Avatar
</translate>
</attachment-input> </attachment-input>
</div> </div>
</section> </section>
@ -395,16 +381,10 @@ fetchOwnedApps()
<section class="ui text container"> <section class="ui text container">
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<h2 class="ui header"> <h2 class="ui header">
<translate > Change my password
Change my password
</translate>
</h2> </h2>
<div class="ui message"> <div class="ui message">
<translate > Changing your password will also change your Subsonic API password if you have requested one.&nbsp; You will have to update your password on your clients that use this password.
Changing your password will also change your Subsonic API password if you have requested one.
</translate>&nbsp;<translate >
You will have to update your password on your clients that use this password.
</translate>
</div> </div>
<form <form
class="ui form" class="ui form"
@ -416,20 +396,16 @@ fetchOwnedApps()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Your password cannot be changed
Your password cannot be changed
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li v-if="passwordError == 'invalid_credentials'"> <li v-if="passwordError == 'invalid_credentials'">
<translate > Please double-check your password is correct
Please double-check your password is correct
</translate>
</li> </li>
</ul> </ul>
</div> </div>
<div class="field"> <div class="field">
<label for="old-password-field"><translate >Current password</translate></label> <label for="old-password-field">Current password</label>
<password-input <password-input
v-model="credentials.oldPassword" v-model="credentials.oldPassword"
field-id="old-password-field" field-id="old-password-field"
@ -437,7 +413,7 @@ fetchOwnedApps()
/> />
</div> </div>
<div class="field"> <div class="field">
<label for="new-password-field"><translate >New password</translate></label> <label for="new-password-field">New password</label>
<password-input <password-input
v-model="credentials.newPassword" v-model="credentials.newPassword"
field-id="new-password-field" field-id="new-password-field"
@ -448,42 +424,30 @@ fetchOwnedApps()
:class="['ui', {'loading': isLoadingPassword}, {disabled: !credentials.newPassword || !credentials.oldPassword}, 'warning', 'button']" :class="['ui', {'loading': isLoadingPassword}, {disabled: !credentials.newPassword || !credentials.oldPassword}, 'warning', 'button']"
:action="submitPassword" :action="submitPassword"
> >
<translate > Change password
Change password
</translate>
<template #modal-header> <template #modal-header>
<p> <p>
<translate > Change your password?
Change your password?
</translate>
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<div> <div>
<p> <p>
<translate > Changing your password will have the following consequences:
Changing your password will have the following consequences:
</translate>
</p> </p>
<ul> <ul>
<li> <li>
<translate > You will be logged out from this session and have to log in with the new one
You will be logged out from this session and have to log in with the new one
</translate>
</li> </li>
<li> <li>
<translate > Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password
Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password
</translate>
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Disable access
Disable access
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>
@ -500,15 +464,11 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="eye slash outline icon" /> <i class="eye slash outline icon" />
<div class="content"> <div class="content">
<translate > Content filters
Content filters
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > Content filters help you hide content you don't want to see on the service.
Content filters help you hide content you don't want to see on the service.
</translate>
</p> </p>
<button <button
@ -516,27 +476,19 @@ fetchOwnedApps()
@click="$store.dispatch('moderation/fetchContentFilters')" @click="$store.dispatch('moderation/fetchContentFilters')"
> >
<i class="refresh icon" />&nbsp; <i class="refresh icon" />&nbsp;
<translate > Refresh
Refresh
</translate>
</button> </button>
<h3 class="ui header"> <h3 class="ui header">
<translate > Hidden artists
Hidden artists
</translate>
</h3> </h3>
<table class="ui compact very basic unstackable table"> <table class="ui compact very basic unstackable table">
<thead> <thead>
<tr> <tr>
<th> <th>
<translate > Name
Name
</translate>
</th> </th>
<th> <th>
<translate > Creation date
Creation date
</translate>
</th> </th>
<th /> <th />
</tr> </tr>
@ -559,9 +511,7 @@ fetchOwnedApps()
class="ui basic tiny button" class="ui basic tiny button"
@click="$store.dispatch('moderation/deleteContentFilter', filter.uuid)" @click="$store.dispatch('moderation/deleteContentFilter', filter.uuid)"
> >
<translate > Delete
Delete
</translate>
</button> </button>
</td> </td>
</tr> </tr>
@ -576,24 +526,18 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="open lock icon" /> <i class="open lock icon" />
<div class="content"> <div class="content">
<translate > Authorized apps
Authorized apps
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > This is the list of applications that have access to your account data.
This is the list of applications that have access to your account data.
</translate>
</p> </p>
<button <button
:class="['ui', 'icon', { loading: isLoadingApps }, 'button']" :class="['ui', 'icon', { loading: isLoadingApps }, 'button']"
@click="fetchApps()" @click="fetchApps()"
> >
<i class="refresh icon" />&nbsp; <i class="refresh icon" />&nbsp;
<translate > Refresh
Refresh
</translate>
</button> </button>
<table <table
v-if="apps.length > 0" v-if="apps.length > 0"
@ -602,14 +546,10 @@ fetchOwnedApps()
<thead> <thead>
<tr> <tr>
<th> <th>
<translate > Application
Application
</translate>
</th> </th>
<th> <th>
<translate > Permissions
Permissions
</translate>
</th> </th>
<th /> <th />
</tr> </tr>
@ -630,29 +570,22 @@ fetchOwnedApps()
:class="['ui', 'tiny', 'danger', { loading: isRevoking.has(app.client_id) }, 'button']" :class="['ui', 'tiny', 'danger', { loading: isRevoking.has(app.client_id) }, 'button']"
@confirm="revokeApp(app.client_id)" @confirm="revokeApp(app.client_id)"
> >
<translate > Revoke
Revoke
</translate>
<template #modal-header> <template #modal-header>
<p <p
v-translate="{application: app.name}" v-translate="{application: app.name}"
> >
Revoke access for application "%{ application }"? Revoke access for application "%{ application }"?
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<p> <p>
<translate > This will prevent this application from accessing the service on your behalf.
This will prevent this application from accessing the service on your behalf.
</translate>
</p> </p>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Revoke access
Revoke access
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>
@ -662,13 +595,9 @@ fetchOwnedApps()
</table> </table>
<empty-state v-else> <empty-state v-else>
<template #title> <template #title>
<translate > You don't have any application connected with your account.
You don't have any application connected with your account.
</translate>
</template> </template>
<translate > If you authorize third-party applications to access your data, those applications will be listed here.
If you authorize third-party applications to access your data, those applications will be listed here.
</translate>
</empty-state> </empty-state>
</section> </section>
<section <section
@ -679,23 +608,17 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="code icon" /> <i class="code icon" />
<div class="content"> <div class="content">
<translate > Your applications
Your applications
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > This is the list of applications that you have registered.
This is the list of applications that you have registered.
</translate>
</p> </p>
<router-link <router-link
class="ui success button" class="ui success button"
:to="{name: 'settings.applications.new'}" :to="{name: 'settings.applications.new'}"
> >
<translate > Register a new application
Register a new application
</translate>
</router-link> </router-link>
<table <table
v-if="ownedApps.length > 0" v-if="ownedApps.length > 0"
@ -704,19 +627,13 @@ fetchOwnedApps()
<thead> <thead>
<tr> <tr>
<th> <th>
<translate > Application
Application
</translate>
</th> </th>
<th> <th>
<translate > Scopes
Scopes
</translate>
</th> </th>
<th> <th>
<translate > Creation date
Creation date
</translate>
</th> </th>
<th /> <th />
</tr> </tr>
@ -742,37 +659,28 @@ fetchOwnedApps()
class="ui tiny success button" class="ui tiny success button"
:to="{name: 'settings.applications.edit', params: {id: app.client_id}}" :to="{name: 'settings.applications.edit', params: {id: app.client_id}}"
> >
<translate > Edit
Edit
</translate>
</router-link> </router-link>
<dangerous-button <dangerous-button
:class="['ui', 'tiny', 'danger', { loading: isDeleting.has(app.client_id) }, 'button']" :class="['ui', 'tiny', 'danger', { loading: isDeleting.has(app.client_id) }, 'button']"
@confirm="deleteApp(app.client_id)" @confirm="deleteApp(app.client_id)"
> >
<translate > Remove
Remove
</translate>
<template #modal-header> <template #modal-header>
<p <p
v-translate="{application: app.name}" v-translate="{application: app.name}"
> >
Remove application "%{ application }"? Remove application "%{ application }"?
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<p> <p>
<translate > This will permanently remove the application and all the associated tokens.
This will permanently remove the application and all the associated tokens.
</translate>
</p> </p>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Remove application
Remove application
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>
@ -782,13 +690,9 @@ fetchOwnedApps()
</table> </table>
<empty-state v-else> <empty-state v-else>
<template #title> <template #title>
<translate > You don't have registered any application yet.
You don't have registered any application yet.
</translate>
</template> </template>
<translate > Register one to integrate Funkwhale with third-party applications.
Register one to integrate Funkwhale with third-party applications.
</translate>
</empty-state> </empty-state>
</section> </section>
@ -800,23 +704,17 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="code icon" /> <i class="code icon" />
<div class="content"> <div class="content">
<translate > Plugins
Plugins
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > Use plugins to extend Funkwhale and get additional features.
Use plugins to extend Funkwhale and get additional features.
</translate>
</p> </p>
<router-link <router-link
class="ui success button" class="ui success button"
:to="{name: 'settings.plugins'}" :to="{name: 'settings.plugins'}"
> >
<translate > Manage plugins
Manage plugins
</translate>
</router-link> </router-link>
</section> </section>
<section class="ui text container"> <section class="ui text container">
@ -824,20 +722,15 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="comment icon" /> <i class="comment icon" />
<div class="content"> <div class="content">
<translate > Change my e-mail address
Change my e-mail address
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > Change the e-mail address associated with your account. We will send a confirmation to the new address.
Change the e-mail address associated with your account. We will send a confirmation to the new address.
</translate>
</p> </p>
<p> <p>
<translate <translate
:translate-params="{email: $store.state.auth.profile?.email}" :translate-params="{email: $store.state.auth.profile?.email}"
> >
Your current e-mail address is %{ email }. Your current e-mail address is %{ email }.
</translate> </translate>
@ -852,9 +745,7 @@ fetchOwnedApps()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > We cannot change your e-mail address
We cannot change your e-mail address
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -866,7 +757,7 @@ fetchOwnedApps()
</ul> </ul>
</div> </div>
<div class="field"> <div class="field">
<label for="new-email"><translate >New e-mail address</translate></label> <label for="new-email">New e-mail address</label>
<input <input
id="new-email" id="new-email"
v-model="newEmail" v-model="newEmail"
@ -875,7 +766,7 @@ fetchOwnedApps()
> >
</div> </div>
<div class="field"> <div class="field">
<label for="current-password-field-email"><translate >Password</translate></label> <label for="current-password-field-email">Password</label>
<password-input <password-input
v-model="emailPassword" v-model="emailPassword"
field-id="current-password-field-email" field-id="current-password-field-email"
@ -886,9 +777,7 @@ fetchOwnedApps()
type="submit" type="submit"
class="ui button" class="ui button"
> >
<translate > Update
Update
</translate>
</button> </button>
</form> </form>
</section> </section>
@ -897,23 +786,17 @@ fetchOwnedApps()
<h2 class="ui header"> <h2 class="ui header">
<i class="trash icon" /> <i class="trash icon" />
<div class="content"> <div class="content">
<translate > Delete my account
Delete my account
</translate>
</div> </div>
</h2> </h2>
<p> <p>
<translate > You can permanently and irreversibly delete your account and all the associated data using the form below. You will be asked for confirmation.
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> </p>
<div <div
role="alert" role="alert"
class="ui warning message" class="ui warning message"
> >
<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.
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> </div>
<div class="ui form"> <div class="ui form">
<div <div
@ -922,9 +805,7 @@ fetchOwnedApps()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > We cannot delete your account
We cannot delete your account
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -936,7 +817,7 @@ fetchOwnedApps()
</ul> </ul>
</div> </div>
<div class="field"> <div class="field">
<label for="current-password-field"><translate >Password</translate></label> <label for="current-password-field">Password</label>
<password-input <password-input
v-model="deleteAccountPassword" v-model="deleteAccountPassword"
field-id="current-password-field" field-id="current-password-field"
@ -947,30 +828,22 @@ fetchOwnedApps()
:class="['ui', {'loading': isDeletingAccount}, {disabled: !deleteAccountPassword}, {danger: deleteAccountPassword}, 'button']" :class="['ui', {'loading': isDeletingAccount}, {disabled: !deleteAccountPassword}, {danger: deleteAccountPassword}, 'button']"
:action="deleteAccount" :action="deleteAccount"
> >
<translate > Delete my account
Delete my account
</translate>
<template #modal-header> <template #modal-header>
<p> <p>
<translate > Do you want to delete your account?
Do you want to delete your account?
</translate>
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<div> <div>
<p> <p>
<translate > This is irreversible and will permanently remove your data from our servers. You will we immediately logged out.
This is irreversible and will permanently remove your data from our servers. You will we immediately logged out.
</translate>
</p> </p>
</div> </div>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Delete my account
Delete my account
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>

View File

@ -88,20 +88,14 @@ fetchInstanceSettings()
<div v-if="submitted"> <div v-if="submitted">
<div class="ui success message"> <div class="ui success message">
<p v-if="signupRequiresApproval"> <p v-if="signupRequiresApproval">
<translate > Your account request was successfully submitted. You will be notified by e-mail when our moderation team has reviewed your request.
Your account request was successfully submitted. You will be notified by e-mail when our moderation team has reviewed your request.
</translate>
</p> </p>
<p v-else> <p v-else>
<translate > Your account was successfully created. Please verify your e-mail address before trying to login.
Your account was successfully created. Please verify your e-mail address before trying to login.
</translate>
</p> </p>
</div> </div>
<h2> <h2>
<translate > Log in to your Funkwhale account
Log in to your Funkwhale account
</translate>
</h2> </h2>
<login-form <login-form
button-classes="basic success" button-classes="basic success"
@ -117,17 +111,13 @@ fetchInstanceSettings()
v-if="!$store.state.instance.settings.users.registration_enabled.value" v-if="!$store.state.instance.settings.users.registration_enabled.value"
class="ui message" class="ui message"
> >
<translate > Public registrations are not possible on this instance. You will need an invitation code to sign up.
Public registrations are not possible on this instance. You will need an invitation code to sign up.
</translate>
</p> </p>
<p <p
v-else-if="signupRequiresApproval" v-else-if="signupRequiresApproval"
class="ui message" class="ui message"
> >
<translate > Registrations on this pod are open, but reviewed by moderators before approval.
Registrations on this pod are open, but reviewed by moderators before approval.
</translate>
</p> </p>
<template v-if="formCustomization?.help_text"> <template v-if="formCustomization?.help_text">
<rendered-description <rendered-description
@ -143,9 +133,7 @@ fetchInstanceSettings()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Your account cannot be created.
Your account cannot be created.
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -157,7 +145,7 @@ fetchInstanceSettings()
</ul> </ul>
</div> </div>
<div class="required field"> <div class="required field">
<label for="username-field"><translate >Username</translate></label> <label for="username-field">Username</label>
<input <input
id="username-field" id="username-field"
ref="username" ref="username"
@ -170,7 +158,7 @@ fetchInstanceSettings()
> >
</div> </div>
<div class="required field"> <div class="required field">
<label for="email-field"><translate >E-mail address</translate></label> <label for="email-field">E-mail address</label>
<input <input
id="email-field" id="email-field"
ref="email" ref="email"
@ -182,7 +170,7 @@ fetchInstanceSettings()
> >
</div> </div>
<div class="required field"> <div class="required field">
<label for="password-field"><translate >Password</translate></label> <label for="password-field">Password</label>
<password-input <password-input
v-model="payload.password1" v-model="payload.password1"
field-id="password-field" field-id="password-field"
@ -192,7 +180,7 @@ fetchInstanceSettings()
v-if="!$store.state.instance.settings.users.registration_enabled.value" v-if="!$store.state.instance.settings.users.registration_enabled.value"
class="required field" class="required field"
> >
<label for="invitation-code"><translate >Invitation code</translate></label> <label for="invitation-code">Invitation code</label>
<input <input
id="invitation-code" id="invitation-code"
v-model="payload.invitation" v-model="payload.invitation"
@ -229,9 +217,7 @@ fetchInstanceSettings()
:class="['ui', buttonClasses, {'loading': isLoading}, ' right floated button']" :class="['ui', buttonClasses, {'loading': isLoading}, ' right floated button']"
type="submit" type="submit"
> >
<translate > Create my account
Create my account
</translate>
</button> </button>
</form> </form>
</template> </template>

View File

@ -82,36 +82,26 @@ fetchToken()
@submit.prevent="requestNewToken()" @submit.prevent="requestNewToken()"
> >
<h2> <h2>
<translate > Subsonic API password
Subsonic API password
</translate>
</h2> </h2>
<p <p
v-if="!subsonicEnabled" v-if="!subsonicEnabled"
class="ui message" class="ui message"
> >
<translate > The Subsonic API is not available on this Funkwhale instance.
The Subsonic API is not available on this Funkwhale instance.
</translate>
</p> </p>
<p> <p>
<translate > Funkwhale is compatible with other music players that support the Subsonic API.&nbsp; You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.
Funkwhale is compatible with other music players that support the Subsonic API.
</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>
<p> <p>
<translate > However, accessing Funkwhale from those clients requires a separate password you can set below.
However, accessing Funkwhale from those clients requires a separate password you can set below.
</translate>
</p> </p>
<p> <p>
<a <a
href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients" href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients"
target="_blank" target="_blank"
> >
<translate >Discover how to use Funkwhale from other apps</translate> Discover how to use Funkwhale from other apps
</a> </a>
</p> </p>
<div <div
@ -128,9 +118,7 @@ fetchToken()
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error
Error
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -164,28 +152,20 @@ fetchToken()
:class="['ui', {'loading': isLoading}, 'button']" :class="['ui', {'loading': isLoading}, 'button']"
:action="requestNewToken" :action="requestNewToken"
> >
<translate > Request a new password
Request a new password
</translate>
<template #modal-header> <template #modal-header>
<p> <p>
<translate > Request a new Subsonic API password?
Request a new Subsonic API password?
</translate>
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<p> <p>
<translate > This will log you out from existing devices that use the current password.
This will log you out from existing devices that use the current password.
</translate>
</p> </p>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Request a new password
Request a new password
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>
@ -195,37 +175,27 @@ fetchToken()
:class="['ui', {'loading': isLoading}, 'button']" :class="['ui', {'loading': isLoading}, 'button']"
@click="requestNewToken" @click="requestNewToken"
> >
<translate > Request a password
Request a password
</translate>
</button> </button>
<dangerous-button <dangerous-button
v-if="token" v-if="token"
:class="['ui', {'loading': isLoading}, 'warning', 'button']" :class="['ui', {'loading': isLoading}, 'warning', 'button']"
:action="disable" :action="disable"
> >
<translate > Disable Subsonic access
Disable Subsonic access
</translate>
<template #modal-header> <template #modal-header>
<p> <p>
<translate > Disable Subsonic API access?
Disable Subsonic API access?
</translate>
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<p> <p>
<translate > This will completely disable access to the Subsonic API using from account.
This will completely disable access to the Subsonic API using from account.
</translate>
</p> </p>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div> <div>
<translate > Disable access
Disable access
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>

View File

@ -59,9 +59,7 @@ defineExpose({
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while creating
Error while creating
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -74,7 +72,7 @@ defineExpose({
</div> </div>
<div class="ui required field"> <div class="ui required field">
<label for="album-title"> <label for="album-title">
<translate >Title</translate> Title
</label> </label>
<input <input
v-model="title" v-model="title"

View File

@ -35,13 +35,11 @@ const albumForm = ref()
<h4 class="header"> <h4 class="header">
<translate <translate
v-if="channel.content_category === 'podcast'" v-if="channel.content_category === 'podcast'"
> >
New series New series
</translate> </translate>
<translate <translate
v-else v-else
> >
New album New album
</translate> </translate>
@ -57,18 +55,14 @@ const albumForm = ref()
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui basic cancel button"> <button class="ui basic cancel button">
<translate > Cancel
Cancel
</translate>
</button> </button>
<button <button
:class="['ui', 'primary', {loading: isLoading}, 'button']" :class="['ui', 'primary', {loading: isLoading}, 'button']"
:disabled="!submittable" :disabled="!submittable"
@click.stop.prevent="albumForm.submit()" @click.stop.prevent="albumForm.submit()"
> >
<translate > Create
Create
</translate>
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>

View File

@ -49,11 +49,9 @@ watch(() => props.channel, fetchData, { immediate: true })
<label for="album-dropdown"> <label for="album-dropdown">
<translate <translate
v-if="channel && channel.artist && channel.artist.content_category === 'podcast'" v-if="channel && channel.artist && channel.artist.content_category === 'podcast'"
>Series</translate> >Series</translate>
<translate <translate
v-else v-else
>Album</translate> >Album</translate>
</label> </label>
<select <select
@ -62,9 +60,7 @@ watch(() => props.channel, fetchData, { immediate: true })
class="ui search normal dropdown" class="ui search normal dropdown"
> >
<option value=""> <option value="">
<translate > None
None
</translate>
</option> </option>
<option <option
v-for="album in albums" v-for="album in albums"

View File

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

View File

@ -411,9 +411,7 @@ const labels = computed(() => ({
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while publishing
Error while publishing
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -426,7 +424,7 @@ const labels = computed(() => ({
</div> </div>
<div :class="['ui', 'required', {hidden: step > 1}, 'field']"> <div :class="['ui', 'required', {hidden: step > 1}, 'field']">
<label for="channel-dropdown"> <label for="channel-dropdown">
<translate >Channel</translate> Channel
</label> </label>
<div <div
id="channel-dropdown" id="channel-dropdown"
@ -449,9 +447,7 @@ const labels = computed(() => ({
<div class="content"> <div class="content">
<p> <p>
<i class="copyright icon" /> <i class="copyright icon" />
<translate > Add a license to your upload to ensure some freedoms to your public.
Add a license to your upload to ensure some freedoms to your public.
</translate>
</p> </p>
</div> </div>
</div> </div>
@ -464,9 +460,7 @@ const labels = computed(() => ({
<div class="content"> <div class="content">
<p> <p>
<i class="warning icon" /> <i class="warning icon" />
<translate > You don't have any space left to upload your files. Please contact the moderators.
You don't have any space left to upload your files. Please contact the moderators.
</translate>
</p> </p>
</div> </div>
</div> </div>
@ -477,25 +471,19 @@ const labels = computed(() => ({
> >
<p> <p>
<i class="redo icon" /> <i class="redo icon" />
<translate > You have some draft uploads pending publication.
You have some draft uploads pending publication.
</translate>
</p> </p>
<button <button
class="ui basic button" class="ui basic button"
@click.stop.prevent="includeDraftUploads = false" @click.stop.prevent="includeDraftUploads = false"
> >
<translate > Ignore
Ignore
</translate>
</button> </button>
<button <button
class="ui basic button" class="ui basic button"
@click.stop.prevent="includeDraftUploads = true" @click.stop.prevent="includeDraftUploads = true"
> >
<translate > Resume
Resume
</translate>
</button> </button>
</div> </div>
<div <div
@ -547,19 +535,16 @@ const labels = computed(() => ({
<template v-else> <template v-else>
<translate <translate
v-if="file.active" v-if="file.active"
> >
Uploading Uploading
</translate> </translate>
<translate <translate
v-else-if="file.error" v-else-if="file.error"
> >
Errored Errored
</translate> </translate>
<translate <translate
v-else v-else
> >
Pending Pending
</translate> </translate>
@ -567,12 +552,12 @@ const labels = computed(() => ({
· {{ parseFloat(file.progress ?? '0') }}% · {{ parseFloat(file.progress ?? '0') }}%
</template> </template>
· <a @click.stop.prevent="remove(file)"> · <a @click.stop.prevent="remove(file)">
<translate >Remove</translate> Remove
</a> </a>
<template v-if="file.error"> <template v-if="file.error">
· ·
<a @click.stop.prevent="retry(file)"> <a @click.stop.prevent="retry(file)">
<translate >Retry</translate> Retry
</a> </a>
</template> </template>
</div> </div>
@ -615,15 +600,11 @@ const labels = computed(() => ({
> >
<div> <div>
<i class="upload icon" />&nbsp; <i class="upload icon" />&nbsp;
<translate > Drag and drop your files here or open the browser to upload your files
Drag and drop your files here or open the browser to upload your files
</translate>
</div> </div>
<div class="ui very small divider" /> <div class="ui very small divider" />
<div> <div>
<translate > Browse
Browse
</translate>
</div> </div>
</file-upload-widget> </file-upload-widget>
<div class="ui hidden divider" /> <div class="ui hidden divider" />

View File

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

View File

@ -56,25 +56,21 @@ const isLoading = ref(false)
<h4 class="header"> <h4 class="header">
<translate <translate
v-if="step === 1" v-if="step === 1"
> >
Publish audio Publish audio
</translate> </translate>
<translate <translate
v-else-if="step === 2" v-else-if="step === 2"
> >
Files to upload Files to upload
</translate> </translate>
<translate <translate
v-else-if="step === 3" v-else-if="step === 3"
> >
Upload details Upload details
</translate> </translate>
<translate <translate
v-else-if="step === 4" v-else-if="step === 4"
> >
Processing uploads Processing uploads
</translate> </translate>
@ -95,9 +91,7 @@ const isLoading = ref(false)
</template> </template>
<div class="ui very small hidden divider" /> <div class="ui very small hidden divider" />
<template v-if="statusData && statusData.quotaStatus"> <template v-if="statusData && statusData.quotaStatus">
<translate > Remaining storage space:
Remaining storage space:
</translate>
{{ humanSize((statusData.quotaStatus.remaining - statusData.uploadedSize) * 1000 * 1000) }} {{ humanSize((statusData.quotaStatus.remaining - statusData.uploadedSize) * 1000 * 1000) }}
</template> </template>
</div> </div>
@ -106,36 +100,28 @@ const isLoading = ref(false)
v-if="step === 1" v-if="step === 1"
class="ui basic cancel button" class="ui basic cancel button"
> >
<translate > Cancel
Cancel
</translate>
</button> </button>
<button <button
v-else-if="step < 3" v-else-if="step < 3"
class="ui basic button" class="ui basic button"
@click.stop.prevent="uploadForm.step -= 1" @click.stop.prevent="uploadForm.step -= 1"
> >
<translate > Previous step
Previous step
</translate>
</button> </button>
<button <button
v-else-if="step === 3" v-else-if="step === 3"
class="ui basic button" class="ui basic button"
@click.stop.prevent="uploadForm.step -= 1" @click.stop.prevent="uploadForm.step -= 1"
> >
<translate > Update
Update
</translate>
</button> </button>
<button <button
v-if="step === 1" v-if="step === 1"
class="ui primary button" class="ui primary button"
@click.stop.prevent="uploadForm.step += 1" @click.stop.prevent="uploadForm.step += 1"
> >
<translate > Next step
Next step
</translate>
</button> </button>
<div <div
v-if="step === 2" v-if="step === 2"
@ -147,9 +133,7 @@ const isLoading = ref(false)
:disabled="!statusData?.canSubmit || undefined" :disabled="!statusData?.canSubmit || undefined"
@click.prevent.stop="uploadForm.publish" @click.prevent.stop="uploadForm.publish"
> >
<translate > Publish
Publish
</translate>
</button> </button>
<button <button
ref="dropdown" ref="dropdown"
@ -164,9 +148,7 @@ const isLoading = ref(false)
class="basic item" class="basic item"
@click="update(false)" @click="update(false)"
> >
<translate > Finish later
Finish later
</translate>
</div> </div>
</div> </div>
</button> </button>
@ -176,9 +158,7 @@ const isLoading = ref(false)
class="ui basic cancel button" class="ui basic cancel button"
@click="update(false)" @click="update(false)"
> >
<translate > Close
Close
</translate>
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>

View File

@ -167,7 +167,7 @@ const launchAction = async () => {
class="right floated" class="right floated"
> >
<span v-if="needsRefresh"> <span v-if="needsRefresh">
<translate >Content has been updated, click refresh to see up-to-date content</translate> Content has been updated, click refresh to see up-to-date content
</span> </span>
<button <button
class="ui basic icon button" class="ui basic icon button"
@ -185,7 +185,7 @@ const launchAction = async () => {
> >
<div class="ui inline fields"> <div class="ui inline fields">
<div class="field"> <div class="field">
<label for="actions-select"><translate >Actions</translate></label> <label for="actions-select">Actions</label>
<select <select
id="actions-select" id="actions-select"
v-model="currentActionName" v-model="currentActionName"
@ -208,9 +208,7 @@ const launchAction = async () => {
:aria-label="labels.performAction" :aria-label="labels.performAction"
@confirm="launchAction" @confirm="launchAction"
> >
<translate > Go
Go
</translate>
<template #modal-header> <template #modal-header>
<p> <p>
<translate <translate
@ -231,7 +229,6 @@ const launchAction = async () => {
</template> </template>
<translate <translate
v-else v-else
> >
This may affect a lot of elements or have irreversible consequences, please double check this is really what you want. This may affect a lot of elements or have irreversible consequences, please double check this is really what you want.
</translate> </translate>
@ -239,9 +236,7 @@ const launchAction = async () => {
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<div :aria-label="labels.performAction"> <div :aria-label="labels.performAction">
<translate > Launch
Launch
</translate>
</div> </div>
</template> </template>
</dangerous-button> </dangerous-button>
@ -252,9 +247,7 @@ const launchAction = async () => {
:class="['ui', {disabled: checked.length === 0}, {'loading': isLoading}, 'button']" :class="['ui', {disabled: checked.length === 0}, {'loading': isLoading}, 'button']"
@click="launchAction" @click="launchAction"
> >
<translate > Go
Go
</translate>
</button> </button>
</div> </div>
<div class="count field"> <div class="count field">
@ -301,7 +294,6 @@ const launchAction = async () => {
> >
<translate <translate
key="4" key="4"
>Select only current page</translate> >Select only current page</translate>
</a> </a>
</template> </template>
@ -313,9 +305,7 @@ const launchAction = async () => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while applying action
Error while applying action
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li

View File

@ -107,9 +107,7 @@ const getAttachmentUrl = (uuid: string) => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Your attachment cannot be saved
Your attachment cannot be saved
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -146,7 +144,7 @@ const getAttachmentUrl = (uuid: string) => {
<div class="eleven wide column"> <div class="eleven wide column">
<div class="file-input"> <div class="file-input">
<label :for="attachmentId"> <label :for="attachmentId">
<translate >Upload New Picture</translate> Upload New Picture
</label> </label>
<input <input
:id="attachmentId" :id="attachmentId"
@ -161,27 +159,21 @@ const getAttachmentUrl = (uuid: string) => {
</div> </div>
<div class="ui very small hidden divider" /> <div class="ui very small hidden divider" />
<p> <p>
<translate > PNG or JPG. Dimensions should be between 1400x1400px and 3000x3000px. Maximum file size allowed is 5MB.
PNG or JPG. Dimensions should be between 1400x1400px and 3000x3000px. Maximum file size allowed is 5MB.
</translate>
</p> </p>
<button <button
v-if="value" v-if="value"
class="ui basic tiny button" class="ui basic tiny button"
@click.stop.prevent="remove(value as string)" @click.stop.prevent="remove(value as string)"
> >
<translate > Remove
Remove
</translate>
</button> </button>
<div <div
v-if="isLoading" v-if="isLoading"
class="ui active inverted dimmer" class="ui active inverted dimmer"
> >
<div class="ui indeterminate text loader"> <div class="ui indeterminate text loader">
<translate > Uploading file
Uploading file
</translate>
</div> </div>
</div> </div>
</div> </div>

View File

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

View File

@ -86,17 +86,13 @@ onMounted(async () => {
:class="[{active: !isPreviewing}, 'item']" :class="[{active: !isPreviewing}, 'item']"
@click.prevent="isPreviewing = false" @click.prevent="isPreviewing = false"
> >
<translate > Write
Write
</translate>
</button> </button>
<button <button
:class="[{active: isPreviewing}, 'item']" :class="[{active: isPreviewing}, 'item']"
@click.prevent="isPreviewing = true" @click.prevent="isPreviewing = true"
> >
<translate > Preview
Preview
</translate>
</button> </button>
</div> </div>
<template v-if="isPreviewing"> <template v-if="isPreviewing">
@ -112,9 +108,7 @@ onMounted(async () => {
</div> </div>
</div> </div>
<p v-else-if="!preview"> <p v-else-if="!preview">
<translate > Nothing to preview.
Nothing to preview.
</translate>
</p> </p>
<sanitized-html <sanitized-html
v-else v-else
@ -141,9 +135,7 @@ onMounted(async () => {
{{ remainingChars }} {{ remainingChars }}
</span> </span>
<p> <p>
<translate > Markdown syntax is supported.
Markdown syntax is supported.
</translate>
</p> </p>
</div> </div>
</div> </div>

View File

@ -22,9 +22,7 @@ const { copy, isSupported: canCopy, copied } = useClipboard({ source: value, cop
v-if="copied" v-if="copied"
class="message" class="message"
> >
<translate > Text copied to clipboard!
Text copied to clipboard!
</translate>
</p> </p>
<input <input
:id="id" :id="id"
@ -39,9 +37,7 @@ const { copy, isSupported: canCopy, copied } = useClipboard({ source: value, cop
@click="copy()" @click="copy()"
> >
<i class="copy icon" /> <i class="copy icon" />
<translate > Copy
Copy
</translate>
</button> </button>
</div> </div>
</template> </template>

View File

@ -42,9 +42,7 @@ const confirm = () => {
> >
<h4 class="header"> <h4 class="header">
<slot name="modal-header"> <slot name="modal-header">
<translate > Do you want to confirm this action?
Do you want to confirm this action?
</translate>
</slot> </slot>
</h4> </h4>
<div class="scrolling content"> <div class="scrolling content">
@ -54,18 +52,14 @@ const confirm = () => {
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui basic cancel button"> <button class="ui basic cancel button">
<translate > Cancel
Cancel
</translate>
</button> </button>
<button <button
:class="['ui', 'confirm', confirmColor, 'button']" :class="['ui', 'confirm', confirmColor, 'button']"
@click="confirm" @click="confirm"
> >
<slot name="modal-confirm"> <slot name="modal-confirm">
<translate > Confirm
Confirm
</translate>
</slot> </slot>
</button> </button>
</div> </div>

View File

@ -19,9 +19,7 @@ withDefaults(defineProps<Props>(), {
<div class="content"> <div class="content">
<slot name="title"> <slot name="title">
<i class="search icon" /> <i class="search icon" />
<translate > No results were found.
No results were found.
</translate>
</slot> </slot>
</div> </div>
</h4> </h4>
@ -32,9 +30,7 @@ withDefaults(defineProps<Props>(), {
class="ui button" class="ui button"
@click="emit('refresh')" @click="emit('refresh')"
> >
<translate > Refresh
Refresh
</translate>
</button> </button>
</div> </div>
</div> </div>

View File

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

View File

@ -42,7 +42,7 @@ const search = () => {
for="search-query" for="search-query"
class="hidden" class="hidden"
> >
<translate >Search</translate> Search
</label> </label>
<input <input
id="search-query" id="search-query"

View File

@ -91,21 +91,19 @@ const submit = async () => {
href="" href=""
@click.stop.prevent="showMore = true" @click.stop.prevent="showMore = true"
> >
<translate >Show more</translate> Show more
</a> </a>
<a <a
v-else v-else
href="" href=""
@click.stop.prevent="showMore = false" @click.stop.prevent="showMore = false"
> >
<translate >Show less</translate> Show less
</a> </a>
</template> </template>
</template> </template>
<p v-else-if="!isUpdating"> <p v-else-if="!isUpdating">
<translate > No description available
No description available
</translate>
</p> </p>
<template v-if="!isUpdating && canUpdate && updateUrl"> <template v-if="!isUpdating && canUpdate && updateUrl">
<div class="ui hidden divider" /> <div class="ui hidden divider" />
@ -114,7 +112,7 @@ const submit = async () => {
@click="isUpdating = true" @click="isUpdating = true"
> >
<i class="pencil icon" /> <i class="pencil icon" />
<translate >Edit</translate> Edit
</span> </span>
</template> </template>
<form <form
@ -128,9 +126,7 @@ const submit = async () => {
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while updating description
Error while updating description
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -149,16 +145,14 @@ const submit = async () => {
class="left floated" class="left floated"
@click.prevent="isUpdating = false" @click.prevent="isUpdating = false"
> >
<translate >Cancel</translate> Cancel
</a> </a>
<button <button
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
type="submit" type="submit"
:disabled="isLoading" :disabled="isLoading"
> >
<translate > Update description
Update description
</translate>
</button> </button>
<div class="ui clearing hidden divider" /> <div class="ui clearing hidden divider" />
</form> </form>

View File

@ -63,13 +63,13 @@ const labels = computed(() => ({
class="menu" class="menu"
> >
<a <a
v-for="t in themes" v-for="th in themes"
:key="t.key" :key="th.key"
:class="[{'active': theme === t.key}, 'item']" :class="[{'active': theme === th.key}, 'item']"
:value="t.key" :value="th.key"
@click="theme = t.key" @click="theme = th.key"
> >
<i :class="t.icon" /> <i :class="th.icon" />
{{ t.name }} {{ t.name }}
</a> </a>
</div> </div>
@ -166,7 +166,7 @@ const labels = computed(() => ({
<div class="divider" /> <div class="divider" />
<router-link <router-link
class="item" class="item"
style="color: var(--danger-color)!important;" style="color: var(--danger-color) !important;"
:to="{ name: 'logout' }" :to="{ name: 'logout' }"
> >
<i class="sign out alternate icon" /> <i class="sign out alternate icon" />

View File

@ -112,9 +112,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<section class="ui vertical center aligned stripe segment"> <section class="ui vertical center aligned stripe segment">
<div :class="['ui', { 'active': isLoading }, 'inverted', 'dimmer']"> <div :class="['ui', { 'active': isLoading }, 'inverted', 'dimmer']">
<div class="ui text loader"> <div class="ui text loader">
<translate > Loading your favorites
Loading your favorites
</translate>
</div> </div>
</div> </div>
<h2 <h2
@ -126,7 +124,6 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
translate-plural="%{ count } favorites" translate-plural="%{ count } favorites"
:translate-n="$store.state.favorites.count" :translate-n="$store.state.favorites.count"
:translate-params="{ count }" :translate-params="{ count }"
> >
%{ count } favorite %{ count } favorite
</translate> </translate>
@ -144,7 +141,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<label for="favorites-ordering"> <label for="favorites-ordering">
<translate >Ordering</translate> Ordering
</label> </label>
<select <select
id="favorites-ordering" id="favorites-ordering"
@ -162,7 +159,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</div> </div>
<div class="field"> <div class="field">
<label for="favorites-ordering-direction"> <label for="favorites-ordering-direction">
<translate >Order</translate> Order
</label> </label>
<select <select
id="favorites-ordering-direction" id="favorites-ordering-direction"
@ -170,20 +167,16 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui dropdown" class="ui dropdown"
> >
<option value="+"> <option value="+">
<translate > Ascending
Ascending
</translate>
</option> </option>
<option value="-"> <option value="-">
<translate > Descending
Descending
</translate>
</option> </option>
</select> </select>
</div> </div>
<div class="field"> <div class="field">
<label for="favorites-results"> <label for="favorites-results">
<translate >Results per page</translate> Results per page
</label> </label>
<select <select
id="favorites-results" id="favorites-results"
@ -222,20 +215,14 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
> >
<div class="ui icon header"> <div class="ui icon header">
<i class="broken heart icon" /> <i class="broken heart icon" />
<translate No tracks have been added to your favorites yet
>
No tracks have been added to your favorites yet
</translate>
</div> </div>
<router-link <router-link
:to="'/library'" :to="'/library'"
class="ui success labeled icon button" class="ui success labeled icon button"
> >
<i class="headphones icon" /> <i class="headphones icon" />
<translate > Browse the library
Browse the library
</translate>
</router-link> </router-link>
</div> </div>
</main> </main>

View File

@ -37,13 +37,11 @@ const title = computed(() => isFavorite.value
<i class="heart icon" /> <i class="heart icon" />
<translate <translate
v-if="isFavorite" v-if="isFavorite"
> >
In favorites In favorites
</translate> </translate>
<translate <translate
v-else v-else
> >
Add to favorites Add to favorites
</translate> </translate>

View File

@ -80,9 +80,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="small" class="small"
> >
<h3 class="header"> <h3 class="header">
<translate > Refreshing object from remote server
Refreshing object from remote server
</translate>
</h3> </h3>
<div class="scrolling content"> <div class="scrolling content">
<template v-if="data && data.status != 'pending'"> <template v-if="data && data.status != 'pending'">
@ -91,14 +89,10 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui message" class="ui message"
> >
<h4 class="header"> <h4 class="header">
<translate > Refresh was skipped
Refresh was skipped
</translate>
</h4> </h4>
<p> <p>
<translate > The remote server answered, but returned data was unsupported by Funkwhale.
The remote server answered, but returned data was unsupported by Funkwhale.
</translate>
</p> </p>
</div> </div>
<div <div
@ -106,14 +100,10 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui success message" class="ui success message"
> >
<h4 class="header"> <h4 class="header">
<translate > Refresh successful
Refresh successful
</translate>
</h4> </h4>
<p> <p>
<translate > Data was refreshed successfully from remote server.
Data was refreshed successfully from remote server.
</translate>
</p> </p>
</div> </div>
<div <div
@ -121,22 +111,16 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui error message" class="ui error message"
> >
<h4 class="header"> <h4 class="header">
<translate > Refresh error
Refresh error
</translate>
</h4> </h4>
<p> <p>
<translate > An error occurred while trying to refresh data:
An error occurred while trying to refresh data:
</translate>
</p> </p>
<table class="ui very basic collapsing celled table"> <table class="ui very basic collapsing celled table">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<translate > Error type
Error type
</translate>
</td> </td>
<td> <td>
{{ data.detail.error_code }} {{ data.detail.error_code }}
@ -144,57 +128,47 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
</tr> </tr>
<tr> <tr>
<td> <td>
<translate > Error detail
Error detail
</translate>
</td> </td>
<td> <td>
<translate <translate
v-if="data.detail.error_code === 'http' && data.detail.status_code" v-if="data.detail.error_code === 'http' && data.detail.status_code"
:translate-params="{status: data.detail.status_code}" :translate-params="{status: data.detail.status_code}"
> >
The remote server answered with HTTP %{ status } The remote server answered with HTTP %{ status }
</translate> </translate>
<translate <translate
v-else-if="['http', 'request'].indexOf(data.detail.error_code) > -1" v-else-if="['http', 'request'].indexOf(data.detail.error_code) > -1"
> >
An HTTP error occurred while contacting the remote server An HTTP error occurred while contacting the remote server
</translate> </translate>
<translate <translate
v-else-if="data.detail.error_code === 'timeout'" v-else-if="data.detail.error_code === 'timeout'"
> >
The remote server didn't respond quickly enough The remote server didn't respond quickly enough
</translate> </translate>
<translate <translate
v-else-if="data.detail.error_code === 'connection'" v-else-if="data.detail.error_code === 'connection'"
> >
Impossible to connect to the remote server Impossible to connect to the remote server
</translate> </translate>
<translate <translate
v-else-if="['invalid_json', 'invalid_jsonld', 'missing_jsonld_type'].indexOf(data.detail.error_code) > -1" v-else-if="['invalid_json', 'invalid_jsonld', 'missing_jsonld_type'].indexOf(data.detail.error_code) > -1"
> >
The remote server returned invalid JSON or JSON-LD data The remote server returned invalid JSON or JSON-LD data
</translate> </translate>
<translate <translate
v-else-if="data.detail.error_code === 'validation'" v-else-if="data.detail.error_code === 'validation'"
> >
Data returned by the remote server had invalid or missing attributes Data returned by the remote server had invalid or missing attributes
</translate> </translate>
<translate <translate
v-else-if="data.detail.error_code === 'unhandled'" v-else-if="data.detail.error_code === 'unhandled'"
> >
Unknown error Unknown error
</translate> </translate>
<translate <translate
v-else v-else
> >
Unknown error Unknown error
</translate> </translate>
@ -209,9 +183,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui active inverted dimmer" class="ui active inverted dimmer"
> >
<div class="ui text loader"> <div class="ui text loader">
<translate > Requesting a fetch
Requesting a fetch
</translate>
</div> </div>
</div> </div>
<div <div
@ -219,9 +191,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui active inverted dimmer" class="ui active inverted dimmer"
> >
<div class="ui text loader"> <div class="ui text loader">
<translate > Waiting for result
Waiting for result
</translate>
</div> </div>
</div> </div>
<div <div
@ -230,9 +200,7 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui negative message" class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
<translate > Error while saving settings
Error while saving settings
</translate>
</h4> </h4>
<ul class="list"> <ul class="list">
<li <li
@ -249,31 +217,23 @@ const { start: startPolling } = useTimeoutFn(poll, 1000, { immediate: false })
class="ui warning message" class="ui warning message"
> >
<h4 class="header"> <h4 class="header">
<translate > Refresh pending
Refresh pending
</translate>
</h4> </h4>
<p> <p>
<translate > The refresh request hasn't been processed in time by our server. It will be processed later.
The refresh request hasn't been processed in time by our server. It will be processed later.
</translate>
</p> </p>
</div> </div>
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui basic cancel button"> <button class="ui basic cancel button">
<translate > Close
Close
</translate>
</button> </button>
<button <button
v-if="data && data.status === 'finished'" v-if="data && data.status === 'finished'"
class="ui confirm success button" class="ui confirm success button"
@click.prevent="showModal = false; emit('refresh')" @click.prevent="showModal = false; emit('refresh')"
> >
<translate > Close and reload page
Close and reload page
</translate>
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>

View File

@ -64,9 +64,7 @@ fetchData()
v-if="!isLoading && libraries.length === 0" v-if="!isLoading && libraries.length === 0"
class="ui subtitle" class="ui subtitle"
> >
<translate > No matching library.
No matching library.
</translate>
</p> </p>
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<div class="ui cards"> <div class="ui cards">
@ -92,9 +90,7 @@ fetchData()
:class="['ui', 'basic', 'button']" :class="['ui', 'basic', 'button']"
@click="fetchData(nextPage)" @click="fetchData(nextPage)"
> >
<translate > Show more
Show more
</translate>
</button> </button>
</template> </template>
</div> </div>

View File

@ -323,9 +323,7 @@ const remove = async () => {
:to="{name: 'library.albums.edit', params: {id: object.id }}" :to="{name: 'library.albums.edit', params: {id: object.id }}"
> >
<i class="pencil icon" /> <i class="pencil icon" />
<translate > Add a description
Add a description
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>
@ -341,9 +339,7 @@ const remove = async () => {
:to="{name: 'library.albums.edit', params: {id: object.id }}" :to="{name: 'library.albums.edit', params: {id: object.id }}"
> >
<i class="pencil icon" /> <i class="pencil icon" />
<translate > Add a description
Add a description
</translate>
</router-link> </router-link>
</template> </template>
</div> </div>

View File

@ -62,13 +62,11 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
<h2 class="ui header"> <h2 class="ui header">
<translate <translate
v-if="isSerie" v-if="isSerie"
> >
Episodes Episodes
</translate> </translate>
<translate <translate
v-else v-else
> >
Tracks Tracks
</translate> </translate>
@ -137,17 +135,13 @@ const paginatedDiscs = computed(() => props.object.tracks.slice(props.paginateBy
<template v-if="!artist.channel && !isSerie"> <template v-if="!artist.channel && !isSerie">
<h2> <h2>
<translate > User libraries
User libraries
</translate>
</h2> </h2>
<library-widget <library-widget
:url="'albums/' + object.id + '/libraries/'" :url="'albums/' + object.id + '/libraries/'"
@loaded="emit('libraries-loaded', $event)" @loaded="emit('libraries-loaded', $event)"
> >
<translate > This album is present in the following libraries:
This album is present in the following libraries:
</translate>
</library-widget> </library-widget>
</template> </template>
</div> </div>

View File

@ -53,7 +53,7 @@ const remove = () => emit('remove')
v-model:show="showEmbedModal" v-model:show="showEmbedModal"
> >
<h4 class="header"> <h4 class="header">
<translate >Embed this album on your website</translate> Embed this album on your website
</h4> </h4>
<div class="scrolling content"> <div class="scrolling content">
<div class="description"> <div class="description">
@ -66,7 +66,7 @@ const remove = () => emit('remove')
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui basic deny button"> <button class="ui basic deny button">
<translate >Cancel</translate> Cancel
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>
@ -86,7 +86,6 @@ const remove = () => emit('remove')
<i class="external icon" /> <i class="external icon" />
<translate <translate
:translate-params="{domain: domain}" :translate-params="{domain: domain}"
>View on %{ domain }</translate> >View on %{ domain }</translate>
</a> </a>
@ -97,7 +96,7 @@ const remove = () => emit('remove')
@click="showEmbedModal = !showEmbedModal" @click="showEmbedModal = !showEmbedModal"
> >
<i class="code icon" /> <i class="code icon" />
<translate >Embed</translate> Embed
</div> </div>
<a <a
v-if="isAlbum && musicbrainzUrl" v-if="isAlbum && musicbrainzUrl"
@ -107,7 +106,7 @@ const remove = () => emit('remove')
class="basic item" class="basic item"
> >
<i class="external icon" /> <i class="external icon" />
<translate >View on MusicBrainz</translate> View on MusicBrainz
</a> </a>
<a <a
v-if="!isChannel && isAlbum" v-if="!isChannel && isAlbum"
@ -117,7 +116,7 @@ const remove = () => emit('remove')
class="basic item" class="basic item"
> >
<i class="external icon" /> <i class="external icon" />
<translate >Search on Discogs</translate> Search on Discogs
</a> </a>
<router-link <router-link
v-if="object.is_local" v-if="object.is_local"
@ -125,7 +124,7 @@ const remove = () => emit('remove')
class="basic item" class="basic item"
> >
<i class="edit icon" /> <i class="edit icon" />
<translate >Edit</translate> Edit
</router-link> </router-link>
<dangerous-button <dangerous-button
v-if="artist && $store.state.auth.authenticated && artist.channel && artist.attributed_to.full_username === $store.state.auth.fullUsername" v-if="artist && $store.state.auth.authenticated && artist.channel && artist.attributed_to.full_username === $store.state.auth.fullUsername"
@ -133,24 +132,22 @@ const remove = () => emit('remove')
@confirm="remove()" @confirm="remove()"
> >
<i class="ui trash icon" /> <i class="ui trash icon" />
<translate >Delete</translate> Delete
<template #modal-header> <template #modal-header>
<p> <p>
<translate >Delete this album?</translate> Delete this album?
</p> </p>
</template> </template>
<template #modal-content> <template #modal-content>
<div> <div>
<p> <p>
<translate > The album will be deleted, as well as any related files and data. This action is irreversible.
The album will be deleted, as well as any related files and data. This action is irreversible.
</translate>
</p> </p>
</div> </div>
</template> </template>
<template #modal-confirm> <template #modal-confirm>
<p> <p>
<translate >Delete</translate> Delete
</p> </p>
</template> </template>
</dangerous-button> </dangerous-button>
@ -171,7 +168,7 @@ const remove = () => emit('remove')
:to="{name: 'manage.library.albums.detail', params: {id: object.id}}" :to="{name: 'manage.library.albums.detail', params: {id: object.id}}"
> >
<i class="wrench icon" /> <i class="wrench icon" />
<translate >Open in moderation interface</translate> Open in moderation interface
</router-link> </router-link>
<a <a
v-if="$store.state.auth.profile && $store.state.auth.profile?.is_superuser" v-if="$store.state.auth.profile && $store.state.auth.profile?.is_superuser"
@ -181,7 +178,7 @@ const remove = () => emit('remove')
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<i class="wrench icon" /> <i class="wrench icon" />
<translate >View in Django's admin</translate>&nbsp; View in Django's admin&nbsp;
</a> </a>
</div> </div>
</button> </button>

View File

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

View File

@ -121,9 +121,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<main v-title="labels.title"> <main v-title="labels.title">
<section class="ui vertical stripe segment"> <section class="ui vertical stripe segment">
<h2 class="ui header"> <h2 class="ui header">
<translate > Browsing albums
Browsing albums
</translate>
</h2> </h2>
<form <form
:class="['ui', {'loading': isLoading}, 'form']" :class="['ui', {'loading': isLoading}, 'form']"
@ -132,7 +130,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
<div class="fields"> <div class="fields">
<div class="field"> <div class="field">
<label for="albums-search"> <label for="albums-search">
<translate >Search</translate> Search
</label> </label>
<div class="ui action input"> <div class="ui action input">
<input <input
@ -152,11 +150,11 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label for="tags-search"><translate >Tags</translate></label> <label for="tags-search">Tags</label>
<tags-selector v-model="tags" /> <tags-selector v-model="tags" />
</div> </div>
<div class="field"> <div class="field">
<label for="album-ordering"><translate >Ordering</translate></label> <label for="album-ordering">Ordering</label>
<select <select
id="album-ordering" id="album-ordering"
v-model="ordering" v-model="ordering"
@ -172,26 +170,22 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</select> </select>
</div> </div>
<div class="field"> <div class="field">
<label for="album-ordering-direction"><translate >Ordering direction</translate></label> <label for="album-ordering-direction">Ordering direction</label>
<select <select
id="album-ordering-direction" id="album-ordering-direction"
v-model="orderingDirection" v-model="orderingDirection"
class="ui dropdown" class="ui dropdown"
> >
<option value="+"> <option value="+">
<translate > Ascending
Ascending
</translate>
</option> </option>
<option value="-"> <option value="-">
<translate > Descending
Descending
</translate>
</option> </option>
</select> </select>
</div> </div>
<div class="field"> <div class="field">
<label for="album-results"><translate >Results per page</translate></label> <label for="album-results">Results per page</label>
<select <select
id="album-results" id="album-results"
v-model="paginateBy" v-model="paginateBy"
@ -234,9 +228,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
> >
<div class="ui icon header"> <div class="ui icon header">
<i class="compact disc icon" /> <i class="compact disc icon" />
<translate > No results matching your query
No results matching your query
</translate>
</div> </div>
<router-link <router-link
v-if="$store.state.auth.authenticated" v-if="$store.state.auth.authenticated"
@ -244,9 +236,7 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
class="ui success button labeled icon" class="ui success button labeled icon"
> >
<i class="upload icon" /> <i class="upload icon" />
<translate > Add some music
Add some music
</translate>
</router-link> </router-link>
</div> </div>
</div> </div>

View File

@ -147,9 +147,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="vibrant" class="vibrant"
:artist="object" :artist="object"
> >
<translate > Play all albums
Play all albums
</translate>
</play-button> </play-button>
</div> </div>
@ -158,9 +156,7 @@ watch(() => props.id, fetchData, { immediate: true })
v-model:show="showEmbedModal" v-model:show="showEmbedModal"
> >
<h4 class="header"> <h4 class="header">
<translate > Embed this artist work on your website
Embed this artist work on your website
</translate>
</h4> </h4>
<div class="scrolling content"> <div class="scrolling content">
<div class="description"> <div class="description">
@ -172,9 +168,7 @@ watch(() => props.id, fetchData, { immediate: true })
</div> </div>
<div class="actions"> <div class="actions">
<button class="ui deny button"> <button class="ui deny button">
<translate > Cancel
Cancel
</translate>
</button> </button>
</div> </div>
</semantic-modal> </semantic-modal>
@ -183,9 +177,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="ui button" class="ui button"
@click="dropdown.click()" @click="dropdown.click()"
> >
<translate > More
More
</translate>
</button> </button>
<button <button
ref="dropdown" ref="dropdown"
@ -203,7 +195,6 @@ watch(() => props.id, fetchData, { immediate: true })
<i class="external icon" /> <i class="external icon" />
<translate <translate
:translate-params="{domain: domain}" :translate-params="{domain: domain}"
>View on %{ domain }</translate> >View on %{ domain }</translate>
</a> </a>
@ -214,9 +205,7 @@ watch(() => props.id, fetchData, { immediate: true })
@click.prevent="showEmbedModal = !showEmbedModal" @click.prevent="showEmbedModal = !showEmbedModal"
> >
<i class="code icon" /> <i class="code icon" />
<translate > Embed
Embed
</translate>
</button> </button>
<a <a
:href="wikipediaUrl" :href="wikipediaUrl"
@ -225,7 +214,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item" class="basic item"
> >
<i class="wikipedia w icon" /> <i class="wikipedia w icon" />
<translate >Search on Wikipedia</translate> Search on Wikipedia
</a> </a>
<a <a
v-if="musicbrainzUrl" v-if="musicbrainzUrl"
@ -235,7 +224,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item" class="basic item"
> >
<i class="external icon" /> <i class="external icon" />
<translate >View on MusicBrainz</translate> View on MusicBrainz
</a> </a>
<a <a
:href="discogsUrl" :href="discogsUrl"
@ -244,7 +233,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item" class="basic item"
> >
<i class="external icon" /> <i class="external icon" />
<translate >Search on Discogs</translate> Search on Discogs
</a> </a>
<router-link <router-link
v-if="object.is_local" v-if="object.is_local"
@ -252,9 +241,7 @@ watch(() => props.id, fetchData, { immediate: true })
class="basic item" class="basic item"
> >
<i class="edit icon" /> <i class="edit icon" />
<translate > Edit
Edit
</translate>
</router-link> </router-link>
<div class="divider" /> <div class="divider" />
<div <div
@ -274,9 +261,7 @@ watch(() => props.id, fetchData, { immediate: true })
:to="{name: 'manage.library.artists.detail', params: {id: object.id}}" :to="{name: 'manage.library.artists.detail', params: {id: object.id}}"
> >
<i class="wrench icon" /> <i class="wrench icon" />
<translate > Open in moderation interface
Open in moderation interface
</translate>
</router-link> </router-link>
<a <a
v-if="$store.state.auth.profile && $store.state.auth.profile.is_superuser" v-if="$store.state.auth.profile && $store.state.auth.profile.is_superuser"
@ -286,7 +271,7 @@ watch(() => props.id, fetchData, { immediate: true })
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<i class="wrench icon" /> <i class="wrench icon" />
<translate >View in Django's admin</translate>&nbsp; View in Django's admin&nbsp;
</a> </a>
</div> </div>
</button> </button>

View File

@ -65,25 +65,19 @@ const loadMoreAlbums = async () => {
<div class="ui hidden divider" /> <div class="ui hidden divider" />
<div class="ui message"> <div class="ui message">
<p> <p>
<translate > You are currently hiding content related to this artist.
You are currently hiding content related to this artist.
</translate>
</p> </p>
<router-link <router-link
class="right floated" class="right floated"
:to="{name: 'settings'}" :to="{name: 'settings'}"
> >
<translate > Review my filters
Review my filters
</translate>
</router-link> </router-link>
<button <button
class="ui basic tiny button" class="ui basic tiny button"
@click="$store.dispatch('moderation/deleteContentFilter', contentFilter.uuid)" @click="$store.dispatch('moderation/deleteContentFilter', contentFilter.uuid)"
> >
<translate > Remove filter
Remove filter
</translate>
</button> </button>
</div> </div>
</div> </div>
@ -98,9 +92,7 @@ const loadMoreAlbums = async () => {
class="ui vertical stripe segment" class="ui vertical stripe segment"
> >
<h2> <h2>
<translate > Albums by this artist
Albums by this artist
</translate>
</h2> </h2>
<div class="ui cards app-cards"> <div class="ui cards app-cards">
<album-card <album-card
@ -115,9 +107,7 @@ const loadMoreAlbums = async () => {
:class="['ui', {loading: isLoadingMoreAlbums}, 'button']" :class="['ui', {loading: isLoadingMoreAlbums}, 'button']"
@click="loadMoreAlbums()" @click="loadMoreAlbums()"
> >
<translate > Load more
Load more
</translate>
</button> </button>
</section> </section>
<section <section
@ -132,9 +122,7 @@ const loadMoreAlbums = async () => {
> >
<template #header> <template #header>
<h2> <h2>
<translate > New tracks by this artist
New tracks by this artist
</translate>
</h2> </h2>
<div class="ui hidden divider" /> <div class="ui hidden divider" />
</template> </template>
@ -142,17 +130,13 @@ const loadMoreAlbums = async () => {
</section> </section>
<section class="ui vertical stripe segment"> <section class="ui vertical stripe segment">
<h2> <h2>
<translate > User libraries
User libraries
</translate>
</h2> </h2>
<library-widget <library-widget
:url="'artists/' + object.id + '/libraries/'" :url="'artists/' + object.id + '/libraries/'"
@loaded="emit('libraries-loaded', $event)" @loaded="emit('libraries-loaded', $event)"
> >
<translate > This artist is present in the following libraries:
This artist is present in the following libraries:
</translate>
</library-widget> </library-widget>
</section> </section>
</div> </div>

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