Merge branch '161-i18n-vue-gettext' into 'develop'
Resolve "Industrialize the i18n workflow" Closes #161 and #167 See merge request funkwhale/funkwhale!286
This commit is contained in:
commit
262c40bc41
|
@ -91,3 +91,4 @@ data/
|
|||
po/*.po
|
||||
docs/swagger
|
||||
_build
|
||||
front/src/translations.json
|
||||
|
|
|
@ -19,9 +19,12 @@ review_front:
|
|||
when: manual
|
||||
allow_failure: true
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install jq -y
|
||||
- cd front
|
||||
script:
|
||||
- yarn install
|
||||
- yarn run i18n-compile
|
||||
# this is to ensure we don't have any errors in the output,
|
||||
# cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
|
||||
- INSTANCE_URL=$REVIEW_INSTANCE_URL yarn run build | tee /dev/stderr | (! grep -i 'ERROR in')
|
||||
|
@ -175,11 +178,11 @@ build_front:
|
|||
stage: build
|
||||
image: node:9
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install jq -y
|
||||
- cd front
|
||||
|
||||
script:
|
||||
- yarn install
|
||||
- yarn run i18n-extract
|
||||
- yarn run i18n-compile
|
||||
# this is to ensure we don't have any errors in the output,
|
||||
# cf https://code.eliotberriot.com/funkwhale/funkwhale/issues/169
|
||||
|
|
|
@ -289,8 +289,9 @@ Typical workflow for a contribution
|
|||
Internationalization
|
||||
--------------------
|
||||
|
||||
We're using https://github.com/Polyconseil/vue-gettext to manage i18n in the project.
|
||||
When working on the front-end, any end-user string should be translated
|
||||
using either ``<i18next path="yourstring">`` or the ``$t('yourstring')``
|
||||
using either ``<translate>yourstring</translate>`` or ``$gettext('yourstring')``
|
||||
function.
|
||||
|
||||
Extraction is done by calling ``yarn run i18n-extract``, which
|
||||
|
|
|
@ -26,4 +26,9 @@ Contribute
|
|||
----------
|
||||
|
||||
Contribution guidelines as well as development installation instructions
|
||||
are outlined in `CONTRIBUTING <CONTRIBUTING>`_
|
||||
are outlined in `CONTRIBUTING <CONTRIBUTING>`_.
|
||||
|
||||
Translate
|
||||
^^^^^^^^^
|
||||
|
||||
Translators willing to help can refer to `TRANSLATORS <TRANSLATORS>`_ for instructions.
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
Translating Funkwhale
|
||||
=====================
|
||||
|
||||
Thank you for reading this! If you want to help translate Funkwhale,
|
||||
you found the proper place :)
|
||||
|
||||
Translation is done via our own Weblate instance at https://translate.funkwhale.audio/projects/funkwhale/front/.
|
||||
|
||||
You can signup/login using your Gitlab account (from https://code.eliotberriot.com).
|
||||
|
||||
Translation workflow
|
||||
--------------------
|
||||
|
||||
Once you're logged-in on the Weblate instance, you can suggest translations. Your suggestions will then be reviewer
|
||||
by the project maintainer or other translators to ensure consistency.
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
Respecting those guidelines is mandatory if you want your translation to be included:
|
||||
|
||||
- Use gender-neutral language and wording
|
||||
|
||||
Requesting a new language
|
||||
-------------------------
|
||||
|
||||
If you'd like to see a new language in Funkwhale, please open an issue here:
|
||||
https://code.eliotberriot.com/funkwhale/funkwhale/issues
|
|
@ -0,0 +1 @@
|
|||
New translation workflow (#161, #167)
|
|
@ -23,6 +23,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in
|
|||
api
|
||||
third-party
|
||||
contributing
|
||||
translators
|
||||
changelog
|
||||
|
||||
Indices and tables
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../TRANSLATORS.rst
|
|
@ -14,8 +14,6 @@ var webpackConfig = process.env.NODE_ENV === 'testing'
|
|||
? require('./webpack.prod.conf')
|
||||
: require('./webpack.dev.conf')
|
||||
|
||||
require('./i18n')
|
||||
|
||||
// default port where dev server listens for incoming traffic
|
||||
var port = process.env.PORT || config.dev.port
|
||||
var host = process.env.HOST || config.dev.host
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { gettextToI18next } = require('i18next-conv');
|
||||
|
||||
const poDir = path.join(__dirname, '..', '..', 'po')
|
||||
const outDir = path.join(__dirname, '..', 'static', 'translations')
|
||||
if (!fs.existsSync(outDir) || !fs.statSync(outDir).isDirectory()) {
|
||||
fs.mkdirSync(outDir)
|
||||
}
|
||||
|
||||
// Convert .po files to i18next files
|
||||
fs.readdir(poDir, (err, files) => {
|
||||
if (err) {
|
||||
return console.log(err)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.po')) {
|
||||
const lang = file.replace(/\.po$/, '')
|
||||
const output = path.join(outDir, `${lang}.json`)
|
||||
fs.readFile(path.join(poDir, file), (err, content) => {
|
||||
if (err) {
|
||||
return console.log(err)
|
||||
}
|
||||
|
||||
gettextToI18next(lang, content).then(res => {
|
||||
fs.writeFile(output, res, err => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
console.log(`Wrote translation file: ${output}`)
|
||||
if (lang === 'en') {
|
||||
// for english, we need to specify that json values are equal to the keys.
|
||||
// otherwise we end up with empty strings on the front end for english
|
||||
var contents = fs.readFileSync(output)
|
||||
var jsonContent = JSON.parse(contents)
|
||||
var finalContent = {}
|
||||
Object.keys(jsonContent).forEach(function(key) {
|
||||
finalContent[key] = key
|
||||
})
|
||||
fs.writeFile(output, JSON.stringify(finalContent))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
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
|
@ -5,11 +5,11 @@
|
|||
"author": "Eliot Berriot <contact@eliotberriot.com>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node build/dev-server.js",
|
||||
"start": "node build/dev-server.js",
|
||||
"dev": "scripts/i18n-compile.sh && node build/dev-server.js",
|
||||
"start": "scripts/i18n-compile.sh && node build/dev-server.js",
|
||||
"build": "node build/build.js",
|
||||
"i18n-extract": "find src/ -name '*.vue' | xargs vendor/vue-i18n-xgettext/index.js > ../po/en.po",
|
||||
"i18n-compile": "node build/i18n.js",
|
||||
"i18n-extract": "scripts/i18n-extract.sh",
|
||||
"i18n-compile": "scripts/i18n-compile.sh",
|
||||
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
|
||||
"unit-watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js",
|
||||
"e2e": "node test/e2e/runner.js",
|
||||
|
@ -21,9 +21,6 @@
|
|||
"axios": "^0.17.1",
|
||||
"dateformat": "^2.0.0",
|
||||
"django-channels": "^1.1.6",
|
||||
"i18next": "^11.1.1",
|
||||
"i18next-conv": "^6.0.0",
|
||||
"i18next-fetch-backend": "^0.1.0",
|
||||
"js-logger": "^1.3.0",
|
||||
"jwt-decode": "^2.2.0",
|
||||
"lodash": "^4.17.4",
|
||||
|
@ -34,6 +31,7 @@
|
|||
"semantic-ui-css": "^2.2.10",
|
||||
"showdown": "^1.8.6",
|
||||
"vue": "^2.5.16",
|
||||
"vue-gettext": "^2.0.31",
|
||||
"vue-lazyload": "^1.1.4",
|
||||
"vue-masonry": "^0.10.16",
|
||||
"vue-router": "^2.3.1",
|
||||
|
@ -61,6 +59,7 @@
|
|||
"cross-env": "^4.0.0",
|
||||
"cross-spawn": "^5.0.1",
|
||||
"css-loader": "^0.28.0",
|
||||
"easygettext": "^2.5.0",
|
||||
"es6-promise": "^4.2.2",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-standard": "^6.2.1",
|
||||
|
@ -104,7 +103,6 @@
|
|||
"sinon-chai": "^2.8.0",
|
||||
"sinon-stub-promise": "^4.0.0",
|
||||
"url-loader": "^0.5.8",
|
||||
"vue-i18n-xgettext": "^0.0.4",
|
||||
"vue-loader": "^12.1.0",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.3.3",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash -eux
|
||||
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
|
||||
find locales -name '*.po' | xargs $(yarn bin gettext-extract)/gettext-compile --output src/translations.json
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash -eux
|
||||
locales=$(tail -n +2 src/locales.js | sed -e 's/export default //' | jq '.locales[].code' | xargs echo)
|
||||
locales_dir="locales"
|
||||
sources=$(find src -name '*.vue' -o -name '*.html' 2> /dev/null)
|
||||
js_sources=$(find src -name '*.vue' -o -name '*.js')
|
||||
touch $locales_dir/app.pot
|
||||
|
||||
# Create a main .pot template, then generate .po files for each available language.
|
||||
# Extract gettext strings from templates files and create a POT dictionary template.
|
||||
$(yarn bin gettext-extract)/gettext-extract --attribute v-translate --quiet --output $locales_dir/app.pot $sources
|
||||
xgettext --language=JavaScript --keyword=npgettext:1c,2,3 \
|
||||
--from-code=utf-8 --join-existing --no-wrap \
|
||||
--package-name=$(node -e "console.log(require('./package.json').name);") \
|
||||
--package-version=$(node -e "console.log(require('./package.json').version);") \
|
||||
--output $locales_dir/app.pot $js_sources
|
||||
|
||||
# Fix broken files path/lines in pot
|
||||
sed -e 's|#: src/|#: front/src/|' -i $locales_dir/app.pot
|
||||
|
||||
# Generate .po files for each available language.
|
||||
echo $locales
|
||||
for lang in $locales; do \
|
||||
po_file=$locales_dir/$lang/LC_MESSAGES/app.po; \
|
||||
echo "msgmerge --update $po_file "; \
|
||||
mkdir -p $(dirname $po_file); \
|
||||
[ -f $po_file ] && msgmerge --lang=$lang --update $po_file $locales_dir/app.pot || msginit --no-translator --locale=$lang --input=$locales_dir/app.pot --output-file=$po_file; \
|
||||
msgattrib --no-wrap --no-obsolete -o $po_file $po_file; \
|
||||
done;
|
|
@ -2,14 +2,14 @@
|
|||
<div id="app">
|
||||
<div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
|
||||
<div class="ui padded segment">
|
||||
<h1 class="ui header">{{ $t('Choose your instance') }}</h1>
|
||||
<h1 class="ui header">{{ $gettext('Choose your instance') }}</h1>
|
||||
<form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)">
|
||||
<p>{{ $t('You need to select an instance in order to continue') }}</p>
|
||||
<p>{{ $gettext('You need to select an instance in order to continue') }}</p>
|
||||
<div class="ui action input">
|
||||
<input type="text" v-model="instanceUrl">
|
||||
<button type="submit" class="ui button">{{ $t('Submit') }}</button>
|
||||
<button type="submit" class="ui button">{{ $gettext('Submit') }}</button>
|
||||
</div>
|
||||
<p>{{ $t('Suggested choices') }}</p>
|
||||
<p>{{ $gettext('Suggested choices') }}</p>
|
||||
<div class="ui bulleted list">
|
||||
<div class="ui item" v-for="url in suggestedInstances">
|
||||
<a @click="instanceUrl = url">{{ url }}</a>
|
||||
|
@ -27,20 +27,20 @@
|
|||
<div class="ui container">
|
||||
<div class="ui stackable equal height stackable grid">
|
||||
<div class="three wide column">
|
||||
<i18next tag="h4" class="ui header" path="Links"></i18next>
|
||||
<h4 v-translate class="ui header">Links</h4>
|
||||
<div class="ui link list">
|
||||
<router-link class="item" to="/about">
|
||||
<i18next path="About this instance" />
|
||||
{{ $gettext('About this instance') }}
|
||||
</router-link>
|
||||
<a href="https://funkwhale.audio" class="item" target="_blank">{{ $t('Official website') }}</a>
|
||||
<a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $t('Documentation') }}</a>
|
||||
<a href="https://funkwhale.audio" class="item" target="_blank">{{ $gettext('Official website') }}</a>
|
||||
<a href="https://docs.funkwhale.audio" class="item" target="_blank">{{ $gettext('Documentation') }}</a>
|
||||
<a href="https://code.eliotberriot.com/funkwhale/funkwhale" class="item" target="_blank">
|
||||
<template v-if="version">{{ $t('Source code ({% version %})', {version: version}) }}</template>
|
||||
<template v-else>{{ $t('Source code') }}</template>
|
||||
<translate :translate-params="{version: version}" v-if="version">Source code (%{version})</translate>
|
||||
<translate v-else>Source code</translate>
|
||||
</a>
|
||||
<a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $t('Issue tracker') }}</a>
|
||||
<a href="https://code.eliotberriot.com/funkwhale/funkwhale/issues" class="item" target="_blank">{{ $gettext('Issue tracker') }}</a>
|
||||
<a @click="switchInstance" class="item" >
|
||||
{{ $t('Use another instance') }}
|
||||
{{ $gettext('Use another instance') }}
|
||||
<template v-if="$store.state.instance.instanceUrl !== '/'">
|
||||
<br>
|
||||
({{ $store.state.instance.instanceUrl }})
|
||||
|
@ -49,14 +49,26 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="ten wide column">
|
||||
<i18next tag="h4" class="ui header" path="About funkwhale" />
|
||||
<h4 v-translate class="ui header">About Funkwhale</h4>
|
||||
<p>
|
||||
<i18next path="Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!"/>
|
||||
<translate>Funkwhale is a free and open-source project run by volunteers. You can help us improve the platform by reporting bugs, suggesting features and share the project with your friends!</translate>
|
||||
</p>
|
||||
<p>
|
||||
<i18next path="The funkwhale logo was kindly designed and provided by Francis Gading."/>
|
||||
<translate>The funkwhale logo was kindly designed and provided by Francis Gading.</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div class="three wide column">
|
||||
<h4 v-translate class="ui header">Options</h4>
|
||||
<div class="ui form">
|
||||
<div class="ui field">
|
||||
<label>{{ $gettext('Change language') }}</label>
|
||||
<select class="ui dropdown" v-model="$language.current">
|
||||
<option v-for="(language, key) in $language.available" :value="key">{{ language }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,7 +127,7 @@ export default {
|
|||
})
|
||||
},
|
||||
switchInstance () {
|
||||
let confirm = window.confirm(this.$t('This will erase your local data and disconnect you, do you want to continue?'))
|
||||
let confirm = window.confirm(this.$gettext('This will erase your local data and disconnect you, do you want to continue?'))
|
||||
if (confirm) {
|
||||
this.$store.commit('instance/instanceUrl', null)
|
||||
}
|
||||
|
@ -144,6 +156,9 @@ export default {
|
|||
'$store.state.instance.instanceUrl' () {
|
||||
this.$store.dispatch('instance/fetchSettings')
|
||||
this.fetchNodeInfo()
|
||||
},
|
||||
'$language.current' (newValue) {
|
||||
this.$store.commit('ui/currentLanguage', newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,21 +3,23 @@
|
|||
<div class="ui vertical center aligned stripe segment">
|
||||
<div class="ui text container">
|
||||
<h1 class="ui huge header">
|
||||
<template v-if="instance.name.value">{{ $t('About {%instance%}', { instance: instance.name.value }) }}</template>
|
||||
<template v-else="instance.name.value">{{ $t('About this instance') }}</template>
|
||||
<template v-if="instance.name.value" :template-params="{instance: instance.name}">
|
||||
About %{ instance }
|
||||
</template>
|
||||
<template v-else="instance.name.value">{{ $gettext('About this instance') }}</template>
|
||||
</h1>
|
||||
<stats></stats>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui vertical stripe segment">
|
||||
<p v-if="!instance.short_description.value && !instance.long_description.value">
|
||||
{{ $t('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
|
||||
{{ $gettext('Unfortunately, owners of this instance did not yet take the time to complete this page.') }}
|
||||
</p>
|
||||
<router-link
|
||||
class="ui button"
|
||||
v-if="$store.state.auth.availablePermissions['settings']"
|
||||
:to="{path: '/manage/settings', hash: 'instance'}">
|
||||
<i class="pencil icon"></i>{{ $t('Edit instance info') }}
|
||||
<i class="pencil icon"></i>{{ $gettext('Edit instance info') }}
|
||||
</router-link>
|
||||
<div
|
||||
v-if="instance.short_description.value"
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<div class="ui vertical center aligned stripe segment">
|
||||
<div class="ui text container">
|
||||
<h1 class="ui huge header">
|
||||
{{ $t('Welcome on Funkwhale') }}
|
||||
{{ $gettext('Welcome on Funkwhale') }}
|
||||
</h1>
|
||||
<p>{{ $t('We think listening to music should be simple.') }}</p>
|
||||
<p>{{ $gettext('We think listening to music should be simple.') }}</p>
|
||||
<router-link class="ui icon button" to="/about">
|
||||
<i class="info icon"></i>
|
||||
{{ $t('Learn more about this instance') }}
|
||||
{{ $gettext('Learn more about this instance') }}
|
||||
</router-link>
|
||||
<router-link class="ui icon teal button" to="/library">
|
||||
{{ $t('Get me to the library') }}
|
||||
{{ $gettext('Get me to the library') }}
|
||||
<i class="right arrow icon"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
|
@ -22,9 +22,9 @@
|
|||
<div class="row">
|
||||
<div class="eight wide left floated column">
|
||||
<h2 class="ui header">
|
||||
{{ $t('Why funkwhale?') }}
|
||||
{{ $gettext('Why funkwhale?') }}
|
||||
</h2>
|
||||
<p>{{ $t('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
|
||||
<p>{{ $gettext('That\'s simple: we loved Grooveshark and we want to build something even better.') }}</p>
|
||||
</div>
|
||||
<div class="four wide left floated column">
|
||||
<img class="ui medium image" src="../assets/logo/logo.png" />
|
||||
|
@ -35,26 +35,26 @@
|
|||
<div class="ui middle aligned stackable text container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<h2 class="ui header">
|
||||
{{ $t('Unlimited music') }}
|
||||
{{ $gettext('Unlimited music') }}
|
||||
</h2>
|
||||
<p>{{ $t('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
|
||||
<p>{{ $gettext('Funkwhale is designed to make it easy to listen to music you like, or to discover new artists.') }}</p>
|
||||
<div class="ui list">
|
||||
<div class="item">
|
||||
<i class="sound icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Click once, listen for hours using built-in radios') }}
|
||||
{{ $gettext('Click once, listen for hours using built-in radios') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="heart icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Keep a track of your favorite songs') }}
|
||||
{{ $gettext('Keep a track of your favorite songs') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="list icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Playlists? We got them') }}
|
||||
{{ $gettext('Playlists? We got them') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -62,28 +62,31 @@
|
|||
<div class="ui middle aligned stackable text container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<h2 class="ui header">
|
||||
{{ $t('Clean library') }}
|
||||
{{ $gettext('Clean library') }}
|
||||
</h2>
|
||||
<p>{{ $t('Funkwhale takes care of handling your music') }}.</p>
|
||||
<p>{{ $gettext('Funkwhale takes care of handling your music') }}.</p>
|
||||
<div class="ui list">
|
||||
<div class="item">
|
||||
<i class="download icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Import music from various platforms, such as YouTube or SoundCloud') }}
|
||||
{{ $gettext('Import music from various platforms, such as YouTube or SoundCloud') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="tag icon"></i>
|
||||
<div class="content">
|
||||
<i18next path="Get quality metadata about your music thanks to {%0%}">
|
||||
<a href="https://musicbrainz.org" target="_blank">{{ $t('MusicBrainz') }}</a>
|
||||
</i18next>
|
||||
<template v-translate>
|
||||
Get quality metadata about your music thanks to
|
||||
<a href="https://musicbrainz.org" target="_blank">
|
||||
MusicBrainz
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="plus icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Covers, lyrics, our goal is to have them all ;)') }}
|
||||
{{ $gettext('Covers, lyrics, our goal is to have them all ;)') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -91,20 +94,20 @@
|
|||
<div class="ui middle aligned stackable text container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<h2 class="ui header">
|
||||
{{ $t('Easy to use') }}
|
||||
{{ $gettext('Easy to use') }}
|
||||
</h2>
|
||||
<p>{{ $t('Funkwhale is dead simple to use.') }}</p>
|
||||
<p>{{ $gettext('Funkwhale is dead simple to use.') }}</p>
|
||||
<div class="ui list">
|
||||
<div class="item">
|
||||
<i class="book icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('No add-ons, no plugins : you only need a web library') }}
|
||||
{{ $gettext('No add-ons, no plugins : you only need a web library') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="wizard icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('Access your music from a clean interface that focus on what really matters') }}
|
||||
{{ $gettext('Access your music from a clean interface that focus on what really matters') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -112,26 +115,26 @@
|
|||
<div class="ui middle aligned stackable text container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<h2 class="ui header">
|
||||
{{ $t('Your music, your way') }}
|
||||
{{ $gettext('Your music, your way') }}
|
||||
</h2>
|
||||
<p>{{ $t('Funkwhale is free and gives you control on your music.') }}</p>
|
||||
<p>{{ $gettext('Funkwhale is free and gives you control on your music.') }}</p>
|
||||
<div class="ui list">
|
||||
<div class="item">
|
||||
<i class="smile icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('The plaform is free and open-source, you can install it and modify it without worries') }}
|
||||
{{ $gettext('The plaform is free and open-source, you can install it and modify it without worries') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="protect icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('We do not track you or bother you with ads') }}
|
||||
{{ $gettext('We do not track you or bother you with ads') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<i class="users icon"></i>
|
||||
<div class="content">
|
||||
{{ $t('You can invite friends and family to your instance so they can enjoy your music') }}
|
||||
{{ $gettext('You can invite friends and family to your instance so they can enjoy your music') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
<h1 class="ui huge header">
|
||||
<i class="warning icon"></i>
|
||||
<div class="content">
|
||||
<strike>{{ $t('Whale') }}</strike> {{ $t('Page not found!') }}
|
||||
<strike>{{ $gettext('Whale') }}</strike> {{ $gettext('Page not found!') }}
|
||||
</div>
|
||||
</h1>
|
||||
<p>{{ $t('We\'re sorry, the page you asked for does not exists.') }}</p>
|
||||
<i18next path="Requested URL: {%0%}"><a :href="path">{{ path }}</a></i18next>
|
||||
<p>{{ $gettext('We\'re sorry, the page you asked for does not exist:') }}</p>
|
||||
<a :href="path">{{ path }}</a>
|
||||
<div class="ui hidden divider"></div>
|
||||
<router-link class="ui icon button" to="/">
|
||||
{{ $t('Go to home page') }}
|
||||
{{ $gettext('Go to home page') }}
|
||||
<i class="right arrow icon"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
<div class="ui compact fluid two item inverted menu">
|
||||
<a class="active item" @click="selectedTab = 'library'" data-tab="library">Browse</a>
|
||||
<a class="item" @click="selectedTab = 'queue'" data-tab="queue">
|
||||
{{ $t('Queue') }}
|
||||
{{ $gettext('Queue') }}
|
||||
<template v-if="queue.tracks.length === 0">
|
||||
{{ $t('(empty)') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('({%index%} of {%length%})', { index: queue.currentIndex + 1, length: queue.tracks.length }) }}
|
||||
{{ $gettext('(empty)') }}
|
||||
</template>
|
||||
<translate v-else :translate-params="{index: queue.currentIndex + 1, length: queue.tracks.length}">
|
||||
(%{ index } of %{ length })
|
||||
</translate>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,40 +32,45 @@
|
|||
<div class="ui bottom attached active tab" data-tab="library">
|
||||
<div class="ui inverted vertical large fluid menu">
|
||||
<div class="item">
|
||||
<div class="header">{{ $t('My account') }}</div>
|
||||
<div class="header">{{ $gettext('My account') }}</div>
|
||||
<div class="menu">
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}"><i class="user icon"></i>{{ $t('Logged in as {%name%}', { name: $store.state.auth.username }) }}</router-link>
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i>{{ $t('Logout') }}</router-link>
|
||||
<router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i>{{ $t('Login') }}</router-link>
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}">
|
||||
<i class="user icon"></i>
|
||||
<translate :translate-params="{username: $store.state.auth.username}">
|
||||
Logged in as %{ username }
|
||||
</translate>
|
||||
</router-link>
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i>{{ $gettext('Logout') }}</router-link>
|
||||
<router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i>{{ $gettext('Login') }}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="header">{{ $t('Music') }}</div>
|
||||
<div class="header">{{ $gettext('Music') }}</div>
|
||||
<div class="menu">
|
||||
<router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i>{{ $t('Browse library') }}</router-link>
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i>{{ $t('Favorites') }}</router-link>
|
||||
<router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i>{{ $gettext('Browse library') }}</router-link>
|
||||
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{path: '/favorites'}"><i class="heart icon"></i>{{ $gettext('Favorites') }}</router-link>
|
||||
<a
|
||||
@click="$store.commit('playlists/chooseTrack', null)"
|
||||
v-if="$store.state.auth.authenticated"
|
||||
class="item">
|
||||
<i class="list icon"></i>{{ $t('Playlists') }}
|
||||
<i class="list icon"></i>{{ $gettext('Playlists') }}
|
||||
</a>
|
||||
<router-link
|
||||
v-if="$store.state.auth.authenticated"
|
||||
class="item" :to="{path: '/activity'}"><i class="bell icon"></i>{{ $t('Activity') }}</router-link>
|
||||
class="item" :to="{path: '/activity'}"><i class="bell icon"></i>{{ $gettext('Activity') }}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" v-if="showAdmin">
|
||||
<div class="header">{{ $t('Administration') }}</div>
|
||||
<div class="header">{{ $gettext('Administration') }}</div>
|
||||
<div class="menu">
|
||||
<router-link
|
||||
class="item"
|
||||
v-if="$store.state.auth.availablePermissions['library']"
|
||||
:to="{name: 'manage.library.files'}">
|
||||
<i class="book icon"></i>{{ $t('Library') }}
|
||||
<i class="book icon"></i>{{ $gettext('Library') }}
|
||||
<div
|
||||
:class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
|
||||
:title="$t('Pending import requests')">
|
||||
:title="$gettext('Pending import requests')">
|
||||
{{ $store.state.ui.notifications.importRequests }}</div>
|
||||
|
||||
</router-link>
|
||||
|
@ -73,29 +78,29 @@
|
|||
class="item"
|
||||
v-else-if="$store.state.auth.availablePermissions['upload']"
|
||||
to="/library/import/launch">
|
||||
<i class="download icon"></i>{{ $t('Import music') }}
|
||||
<i class="download icon"></i>{{ $gettext('Import music') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="item"
|
||||
v-if="$store.state.auth.availablePermissions['federation']"
|
||||
:to="{path: '/manage/federation/libraries'}">
|
||||
<i class="sitemap icon"></i>{{ $t('Federation') }}
|
||||
<i class="sitemap icon"></i>{{ $gettext('Federation') }}
|
||||
<div
|
||||
:class="['ui', {'teal': $store.state.ui.notifications.federation > 0}, 'label']"
|
||||
:title="$t('Pending follow requests')">
|
||||
:title="$gettext('Pending follow requests')">
|
||||
{{ $store.state.ui.notifications.federation }}</div>
|
||||
</router-link>
|
||||
<router-link
|
||||
class="item"
|
||||
v-if="$store.state.auth.availablePermissions['settings']"
|
||||
:to="{path: '/manage/settings'}">
|
||||
<i class="settings icon"></i>{{ $t('Settings') }}
|
||||
<i class="settings icon"></i>{{ $gettext('Settings') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="item"
|
||||
v-if="$store.state.auth.availablePermissions['settings']"
|
||||
:to="{name: 'manage.users.users.list'}">
|
||||
<i class="users icon"></i>{{ $t('Users') }}
|
||||
<i class="users icon"></i>{{ $gettext('Users') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -105,12 +110,19 @@
|
|||
<i class="history icon"></i>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
{{ $t('Do you want to restore your previous queue?') }}
|
||||
{{ $gettext('Do you want to restore your previous queue?') }}
|
||||
</div>
|
||||
<p>{{ $t('{%count%} tracks', { count: queue.previousQueue.tracks.length }) }}</p>
|
||||
<p>
|
||||
<translate
|
||||
translate-plural="%{ count } tracks"
|
||||
:translate-n="queue.previousQueue.tracks.length"
|
||||
:translate-params="{count: queue.previousQueue.tracks.length}">
|
||||
%{ count } track
|
||||
</translate>
|
||||
</p>
|
||||
<div class="ui two buttons">
|
||||
<div @click="queue.restore()" class="ui basic inverted green button">{{ $t('Yes') }}</div>
|
||||
<div @click="queue.removePrevious()" class="ui basic inverted red button">{{ $t('No') }}</div>
|
||||
<div @click="queue.restore()" class="ui basic inverted green button">{{ $gettext('Yes') }}</div>
|
||||
<div @click="queue.removePrevious()" class="ui basic inverted red button">{{ $gettext('No') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -141,10 +153,10 @@
|
|||
<div v-if="$store.state.radios.running" class="ui black message">
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<i class="feed icon"></i> {{ $t('You have a radio playing') }}
|
||||
<i class="feed icon"></i> {{ $gettext('You have a radio playing') }}
|
||||
</div>
|
||||
<p>{{ $t('New tracks will be appended here automatically.') }}</p>
|
||||
<div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button">{{ $t('Stop radio') }}</div>
|
||||
<p>{{ $gettext('New tracks will be appended here automatically.') }}</p>
|
||||
<div @click="$store.dispatch('radios/stop')" class="ui basic inverted red button">{{ $gettext('Stop radio') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,20 +5,12 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<div class="summary">
|
||||
<i18next path="{%0%} favorited a track">
|
||||
<username class="user" :username="event.actor.local_id" />
|
||||
</i18next>
|
||||
<translate :translate-params="{user: event.actor.local_id}">%{ user } favorited a track</translate>
|
||||
<human-date class="date" :date="event.published" />
|
||||
</div>
|
||||
<div class="extra text">
|
||||
<router-link :to="{name: 'library.tracks.detail', params: {id: event.object.local_id }}">{{ event.object.name }}</router-link>
|
||||
<i18next path="from album {%0%}, by {%1%}" v-if="event.object.album">
|
||||
{{ event.object.album }}
|
||||
<em>{{ event.object.artist }}</em>
|
||||
</i18next>
|
||||
<i18next path=", by {%0%}" v-else>
|
||||
<em>{{ event.object.artist }}</em>
|
||||
</i18next>
|
||||
<translate :translate-params="{album: event.object.album, artist: event.object.artist}">from %{ album } by %{ artist }</translate>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,20 +5,12 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<div class="summary">
|
||||
<i18next path="{%0%} listened to a track">
|
||||
<username class="user" :username="event.actor.local_id" />
|
||||
</i18next>
|
||||
<human-date class="date" :date="event.published" />
|
||||
|
||||
<translate :translate-params="{user: event.actor.local_id}">%{ user } listened to a track</translate>
|
||||
<human-date class="date" :date="event.published" />
|
||||
</div>
|
||||
<div class="extra text">
|
||||
<router-link :to="{name: 'library.tracks.detail', params: {id: event.object.local_id }}">{{ event.object.name }}</router-link>
|
||||
<i18next path="from album {%0%}, by {%1%}" v-if="event.object.album">
|
||||
{{ event.object.album }}<em>{{ event.object.artist }}</em>
|
||||
</i18next>
|
||||
<i18next path=", by {%0%}" v-else>
|
||||
<em>{{ event.object.artist }}</em>
|
||||
</i18next>
|
||||
<translate :translate-params="{album: event.object.album, artist: event.object.artist}">from %{ album } by %{ artist }</translate>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<div class="ui divider" />
|
||||
<h3 class="ui header">{{ group.label }}</h3>
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while saving settings') }}</div>
|
||||
<div class="header">{{ $gettext('Error while saving settings') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="result" class="ui positive message">
|
||||
{{ $t('Settings updated successfully.') }}
|
||||
{{ $gettext('Settings updated successfully.') }}
|
||||
</div>
|
||||
<p v-if="group.help">{{ group.help }}</p>
|
||||
<div v-for="setting in settings" class="ui field">
|
||||
|
@ -61,7 +61,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']">
|
||||
{{ $t('Save') }}
|
||||
{{ $gettext('Save') }}
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<template>
|
||||
<div :title="title" :class="['ui', {'tiny': discrete}, 'buttons']">
|
||||
<button
|
||||
:title="$t('Add to current queue')"
|
||||
:title="$gettext('Add to current queue')"
|
||||
@click="addNext(true)"
|
||||
:disabled="!playable"
|
||||
:class="['ui', {loading: isLoading}, {'mini': discrete}, {disabled: !playable}, 'button']">
|
||||
<i class="ui play icon"></i>
|
||||
<template v-if="!discrete"><slot><i18next path="Play"/></slot></template>
|
||||
<template v-if="!discrete"><slot>{{ $gettext('Play') }}</slot></template>
|
||||
</button>
|
||||
<div v-if="!discrete" :class="['ui', {disabled: !playable}, 'floating', 'dropdown', 'icon', 'button']">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" :disabled="!playable" @click="add"><i class="plus icon"></i><i18next path="Add to queue"/></div>
|
||||
<div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i><i18next path="Play next"/></div>
|
||||
<div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i><i18next path="Play now"/></div>
|
||||
<div class="item" :disabled="!playable" @click="add"><i class="plus icon"></i>{{ $gettext('Add to queue') }}</div>
|
||||
<div class="item" :disabled="!playable" @click="addNext()"><i class="step forward icon"></i>{{ $gettext('Play next') }}</div>
|
||||
<div class="item" :disabled="!playable" @click="addNext(true)"><i class="arrow down icon"></i>{{ $gettext('Play now') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,10 +44,10 @@ export default {
|
|||
computed: {
|
||||
title () {
|
||||
if (this.playable) {
|
||||
return this.$t('Play immediatly')
|
||||
return this.$gettext('Play immediatly')
|
||||
} else {
|
||||
if (this.track) {
|
||||
return this.$t('This track is not imported and cannot be played')
|
||||
return this.$gettext('This track is not imported and cannot be played')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -142,8 +142,9 @@ export default {
|
|||
if (tracks.length < 1) {
|
||||
return
|
||||
}
|
||||
let msg = this.$ngettext('%{ count } track was added to your queue', '%{ count } tracks were added to your queue', tracks.length)
|
||||
this.$store.commit('ui/addMessage', {
|
||||
content: this.$t('{% tracks %} tracks were added to your queue.', {tracks: tracks.length}),
|
||||
content: this.$gettextInterpolate(msg, {count: tracks.length}),
|
||||
date: new Date()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -57,44 +57,44 @@
|
|||
|
||||
<div class="two wide column controls ui grid">
|
||||
<div
|
||||
:title="$t('Previous track')"
|
||||
:title="$gettext('Previous track')"
|
||||
class="two wide column control"
|
||||
:disabled="emptyQueue">
|
||||
<i @click="previous" :class="['ui', 'backward', {'disabled': emptyQueue}, 'big', 'icon']"></i>
|
||||
</div>
|
||||
<div
|
||||
v-if="!playing"
|
||||
:title="$t('Play track')"
|
||||
:title="$gettext('Play track')"
|
||||
class="two wide column control">
|
||||
<i @click="togglePlay" :class="['ui', 'play', {'disabled': !currentTrack}, 'big', 'icon']"></i>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:title="$t('Pause track')"
|
||||
:title="$gettext('Pause track')"
|
||||
class="two wide column control">
|
||||
<i @click="togglePlay" :class="['ui', 'pause', {'disabled': !currentTrack}, 'big', 'icon']"></i>
|
||||
</div>
|
||||
<div
|
||||
:title="$t('Next track')"
|
||||
:title="$gettext('Next track')"
|
||||
class="two wide column control"
|
||||
:disabled="!hasNext">
|
||||
<i @click="next" :class="['ui', {'disabled': !hasNext}, 'step', 'forward', 'big', 'icon']" ></i>
|
||||
</div>
|
||||
<div class="two wide column control volume-control">
|
||||
<i :title="$t('Unmute')" @click="$store.commit('player/volume', 1)" v-if="volume === 0" class="volume off secondary icon"></i>
|
||||
<i :title="$t('Mute')" @click="$store.commit('player/volume', 0)" v-else-if="volume < 0.5" class="volume down secondary icon"></i>
|
||||
<i :title="$t('Mute')" @click="$store.commit('player/volume', 0)" v-else class="volume up secondary icon"></i>
|
||||
<i :title="$gettext('Unmute')" @click="$store.commit('player/volume', 1)" v-if="volume === 0" class="volume off secondary icon"></i>
|
||||
<i :title="$gettext('Mute')" @click="$store.commit('player/volume', 0)" v-else-if="volume < 0.5" class="volume down secondary icon"></i>
|
||||
<i :title="$gettext('Mute')" @click="$store.commit('player/volume', 0)" v-else class="volume up secondary icon"></i>
|
||||
<input type="range" step="0.05" min="0" max="1" v-model="sliderVolume" />
|
||||
</div>
|
||||
<div class="two wide column control looping">
|
||||
<i
|
||||
:title="$t('Looping disabled. Click to switch to single-track looping.')"
|
||||
:title="$gettext('Looping disabled. Click to switch to single-track looping.')"
|
||||
v-if="looping === 0"
|
||||
@click="$store.commit('player/looping', 1)"
|
||||
:disabled="!currentTrack"
|
||||
:class="['ui', {'disabled': !currentTrack}, 'step', 'repeat', 'secondary', 'icon']"></i>
|
||||
<i
|
||||
:title="$t('Looping on a single track. Click to switch to whole queue looping.')"
|
||||
:title="$gettext('Looping on a single track. Click to switch to whole queue looping.')"
|
||||
v-if="looping === 1"
|
||||
@click="$store.commit('player/looping', 2)"
|
||||
:disabled="!currentTrack"
|
||||
|
@ -102,7 +102,7 @@
|
|||
<span class="ui circular tiny orange label">1</span>
|
||||
</i>
|
||||
<i
|
||||
:title="$t('Looping on whole queue. Click to disable looping.')"
|
||||
:title="$gettext('Looping on whole queue. Click to disable looping.')"
|
||||
v-if="looping === 2"
|
||||
@click="$store.commit('player/looping', 0)"
|
||||
:disabled="!currentTrack"
|
||||
|
@ -111,7 +111,7 @@
|
|||
</div>
|
||||
<div
|
||||
:disabled="queue.tracks.length === 0"
|
||||
:title="$t('Shuffle your queue')"
|
||||
:title="$gettext('Shuffle your queue')"
|
||||
class="two wide column control">
|
||||
<div v-if="isShuffling" class="ui inline shuffling inverted small active loader"></div>
|
||||
<i v-else @click="shuffle()" :class="['ui', 'random', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
|
||||
|
@ -119,7 +119,7 @@
|
|||
<div class="one wide column"></div>
|
||||
<div
|
||||
:disabled="queue.tracks.length === 0"
|
||||
:title="$t('Clear your queue')"
|
||||
:title="$gettext('Clear your queue')"
|
||||
class="two wide column control">
|
||||
<i @click="clean()" :class="['ui', 'trash', 'secondary', {'disabled': queue.tracks.length === 0}, 'icon']" ></i>
|
||||
</div>
|
||||
|
@ -180,12 +180,13 @@ export default {
|
|||
return
|
||||
}
|
||||
let self = this
|
||||
let msg = this.$gettext('Queue shuffled!')
|
||||
this.isShuffling = true
|
||||
setTimeout(() => {
|
||||
self.$store.dispatch('queue/shuffle', () => {
|
||||
self.isShuffling = false
|
||||
self.$store.commit('ui/addMessage', {
|
||||
content: self.$t('Queue shuffled!'),
|
||||
content: msg,
|
||||
date: new Date()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<h2><i18next path="Search for some music"/></h2>
|
||||
<h2>{{ $gettext('Search for some music') }}</h2>
|
||||
<div :class="['ui', {'loading': isLoading }, 'search']">
|
||||
<div class="ui icon big input">
|
||||
<i class="search icon"></i>
|
||||
|
@ -8,22 +8,22 @@
|
|||
</div>
|
||||
</div>
|
||||
<template v-if="query.length > 0">
|
||||
<h3 class="ui title"><i18next path="Artists"/></h3>
|
||||
<h3 class="ui title">{{ $gettext('Artists') }}</h3>
|
||||
<div v-if="results.artists.length > 0" class="ui stackable three column grid">
|
||||
<div class="column" :key="artist.id" v-for="artist in results.artists">
|
||||
<artist-card class="fluid" :artist="artist" ></artist-card>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else><i18next path="Sorry, we did not found any artist matching your query"/></p>
|
||||
<p v-else>{{ $gettext('Sorry, we did not found any artist matching your query') }}</p>
|
||||
</template>
|
||||
<template v-if="query.length > 0">
|
||||
<h3 class="ui title"><i18next path="Albums"/></h3>
|
||||
<h3 class="ui title">{{ $gettext('Albums') }}</h3>
|
||||
<div v-if="results.albums.length > 0" class="ui stackable three column grid">
|
||||
<div class="column" :key="album.id" v-for="album in results.albums">
|
||||
<album-card class="fluid" :album="album" ></album-card>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else><i18next path="Sorry, we did not found any album matching your query"/></p>
|
||||
<p v-else>{{ $gettext('Sorry, we did not found any album matching your query') }}</p>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
</div>
|
||||
<div class="meta">
|
||||
<span>
|
||||
<i18next path="By {%0%}">
|
||||
<router-link tag="span" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
|
||||
{{ album.artist.name }}</router-link>
|
||||
</i18next>
|
||||
<router-link tag="span" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
|
||||
<translate :translate-params="{artist: album.artist.name}">By %{ artist }</translate>
|
||||
</router-link>
|
||||
</span><span class="time" v-if="album.release_date">– {{ album.release_date | year }}</span>
|
||||
</div>
|
||||
<div class="description" v-if="mode === 'rich'">
|
||||
|
@ -39,23 +38,21 @@
|
|||
</table>
|
||||
<div class="center aligned segment" v-if="album.tracks.length > initialTracks">
|
||||
<em v-if="!showAllTracks" @click="showAllTracks = true" class="expand">
|
||||
<i18next path="Show {%0%} more tracks">{{ album.tracks.length - initialTracks }}</i18next>
|
||||
<translate :translate-params="{count: album.tracks.length - initialTracks}" :translate-n="album.tracks.length - initialTracks" translate-plural="Show %{ count } more tracks">Show 1 more track</translate>
|
||||
</em>
|
||||
<em v-else @click="showAllTracks = false" class="expand">
|
||||
<i18next path="Collapse" />
|
||||
{{ $gettext('Collapse') }}
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<play-button class="mini basic orange right floated" :tracks="album.tracks">
|
||||
<i18next path="Play all"/>
|
||||
{{ $gettext('Play all') }}
|
||||
</play-button>
|
||||
<span>
|
||||
<i class="music icon"></i>
|
||||
<i18next path="{%0%} tracks">
|
||||
{{ album.tracks.length }}
|
||||
</i18next>
|
||||
<translate :translate-params="{count: album.tracks.length}" :translate-n="album.tracks.length" translate-plural="%{ count } tracks">1 track</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -28,12 +28,10 @@
|
|||
</table>
|
||||
<div class="center aligned segment" v-if="artist.albums.length > initialAlbums">
|
||||
<em v-if="!showAllAlbums" @click="showAllAlbums = true" class="expand">
|
||||
<i18next path="Show {%0%} more albums">
|
||||
{{ artist.albums.length - initialAlbums }}
|
||||
</i18next>
|
||||
<translate :translate-params="{count: artist.albums.length - initialAlbums}" :translate-n="artist.albums.length - initialAlbums" translate-plural="Show %{ count } more albums">Show 1 more album</translate>
|
||||
</em>
|
||||
<em v-else @click="showAllAlbums = false" class="expand">
|
||||
<i18next path="Collapse"/>
|
||||
{{ $gettext('Collapse') }}
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,12 +39,10 @@
|
|||
<div class="extra content">
|
||||
<span>
|
||||
<i class="sound icon"></i>
|
||||
<i18next path="{%0%} albums">
|
||||
{{ artist.albums.length }}
|
||||
</i18next>
|
||||
<translate :translate-params="{count: artist.albums.length}" :translate-n="artist.albums.length" translate-plural="%{ count } albums">1 album</translate>
|
||||
</span>
|
||||
<play-button class="mini basic orange right floated" :artist="artist.id">
|
||||
<i18next path="Play all"/>
|
||||
{{ $gettext('Play all') }}
|
||||
</play-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<i18next tag="th" colspan="6" path="Title"/>
|
||||
<i18next tag="th" colspan="6" path="Artist"/>
|
||||
<i18next tag="th" colspan="6" path="Album"/>
|
||||
<th colspan="6">{{ $gettext('Title') }}</th>
|
||||
<th colspan="6">{{ $gettext('Artist') }}</th>
|
||||
<th colspan="6">{{ $gettext('Album') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -21,17 +21,17 @@
|
|||
<tr>
|
||||
<th colspan="3">
|
||||
<button @click="showDownloadModal = !showDownloadModal" class="ui basic button">
|
||||
<i18next path="Download..."/>
|
||||
{{ $gettext('Download') }}
|
||||
</button>
|
||||
<modal :show.sync="showDownloadModal">
|
||||
<i18next tag="div" path="Download tracks" class="header" />
|
||||
<div class="header">{{ $gettext('Download tracks') }}</div>
|
||||
<div class="content">
|
||||
<div class="description">
|
||||
<i18next tag="p" path="There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as {%0%} to easily download a list of tracks.">
|
||||
<a href="https://curl.haxx.se/" target="_blank">cURL</a>
|
||||
</i18next>
|
||||
<i18next path="Simply copy paste the snippet below into a terminal to launch the download."/>
|
||||
<i18next tag="div" class="ui warning message" path="Keep your PRIVATE_TOKEN secret as it gives access to your account."/>
|
||||
<p>{{ $gettext('There is currently no way to download directly multiple tracks from funkwhale as a ZIP archive. However, you can use a command line tools such as cURL to easily download a list of tracks.') }}</p>
|
||||
{{ $gettext('Simply copy paste the snippet below into a terminal to launch the download.') }}
|
||||
<div class="ui warning message">
|
||||
{{ $gettext('Keep your PRIVATE_TOKEN secret as it gives access to your account.') }}
|
||||
</div>
|
||||
<pre>
|
||||
export PRIVATE_TOKEN="{{ $store.state.auth.token }}"
|
||||
<template v-for="track in tracks"><template v-if="track.files.length > 0">
|
||||
|
@ -40,7 +40,7 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="$store.state.auth.aut
|
|||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<i18next tag="div" class="ui black deny button" path="Cancel" />
|
||||
<div class="ui black deny button">{{ $gettext('Cancel') }}</div>
|
||||
</div>
|
||||
</modal>
|
||||
</th>
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
<div class="main pusher" v-title="'Log In'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2><i18next path="Log in to your Funkwhale account"/></h2>
|
||||
<h2>{{ $gettext('Log in to your Funkwhale account') }}</h2>
|
||||
<form class="ui form" @submit.prevent="submit()">
|
||||
<div v-if="error" class="ui negative message">
|
||||
<div class="header"><i18next path="We cannot log you in"/></div>
|
||||
<div class="header">{{ $gettext('We cannot log you in') }}</div>
|
||||
<ul class="list">
|
||||
<i18next tag="li" v-if="error == 'invalid_credentials'" path="Please double-check your username/password couple is correct"/>
|
||||
<i18next tag="li" v-else path="An unknown error happend, this can mean the server is down or cannot be reached"/>
|
||||
<li v-if="error == 'invalid_credentials'">{{ $gettext('Please double-check your username/password couple is correct') }}</li>
|
||||
<li v-else>{{ $gettext('An unknown error happend, this can mean the server is down or cannot be reached') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ $t('Username or email') }} |
|
||||
{{ $gettext('Username or email') }} |
|
||||
<router-link :to="{path: '/signup'}">
|
||||
{{ $t('Create an account') }}
|
||||
{{ $gettext('Create an account') }}
|
||||
</router-link>
|
||||
</label>
|
||||
<input
|
||||
|
@ -30,15 +30,17 @@
|
|||
</div>
|
||||
<div class="field">
|
||||
<label>
|
||||
{{ $t('Password') }} |
|
||||
{{ $gettext('Password') }} |
|
||||
<router-link :to="{name: 'auth.password-reset', query: {email: credentials.username}}">
|
||||
{{ $t('Reset your password') }}
|
||||
{{ $gettext('Reset your password') }}
|
||||
</router-link>
|
||||
</label>
|
||||
<password-input :index="2" required v-model="credentials.password" />
|
||||
|
||||
</div>
|
||||
<button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit"><i18next path="Login"/></button>
|
||||
<button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
|
||||
{{ $gettext('Login') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
<div class="main pusher" v-title="'Log Out'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2><i18next path="Are you sure you want to log out?"/></h2>
|
||||
<i18next tag="p" path="You are currently logged in as {%0%}">{{ $store.state.auth.username }}</i18next>
|
||||
<button class="ui button" @click="$store.dispatch('auth/logout')"><i18next path="Yes, log me out!"/></button>
|
||||
</form>
|
||||
<h2>
|
||||
{{ $gettext('Are you sure you want to log out?') }}
|
||||
</h2>
|
||||
<p v-translate="{username: $store.state.auth.username}">You are currently logged in as %{ username }</p>
|
||||
<button class="ui button" @click="$store.dispatch('auth/logout')">{{ $gettext('Yes, log me out!') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,17 +9,19 @@
|
|||
<i class="circular inverted user green icon"></i>
|
||||
<div class="content">
|
||||
{{ $store.state.auth.profile.username }}
|
||||
<i18next class="sub header" path="Registered since {%0%}">{{ signupDate }}</i18next>
|
||||
<div class="sub header" v-translate="{date: signupDate}">Registered since %{ date }</div>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="ui basic green label"><i18next path="This is you!"/></div>
|
||||
<div class="ui basic green label">
|
||||
{{ $gettext('This is you!') }}
|
||||
</div>
|
||||
<div v-if="$store.state.auth.profile.is_staff" class="ui yellow label">
|
||||
<i class="star icon"></i>
|
||||
<i18next path="Staff member"/>
|
||||
{{ $gettext('Staff member') }}
|
||||
</div>
|
||||
<router-link class="ui tiny basic button" :to="{path: '/settings'}">
|
||||
<i class="setting icon"> </i>
|
||||
<i18next path="Settings..."/>
|
||||
{{ $gettext('Settings...') }}
|
||||
</router-link>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
<div class="main pusher" v-title="'Account Settings'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2 class="ui header"><i18next path="Account settings"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Account settings') }}
|
||||
</h2>
|
||||
<form class="ui form" @submit.prevent="submitSettings()">
|
||||
<div v-if="settings.success" class="ui positive message">
|
||||
<div class="header"><i18next path="Settings updated"/></div>
|
||||
<div class="header">
|
||||
{{ $gettext('Settings updated') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="settings.errors.length > 0" class="ui negative message">
|
||||
<i18next tag="div" class="header" path="We cannot save your settings"/>
|
||||
<div class="header">{{ $gettext('We cannot save your settings') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in settings.errors">{{ error }}</li>
|
||||
</ul>
|
||||
|
@ -20,46 +24,52 @@
|
|||
<option :value="c.value" v-for="c in f.choices">{{ c.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button :class="['ui', {'loading': isLoading}, 'button']" type="submit"><i18next path="Update settings"/></button>
|
||||
<button :class="['ui', {'loading': isLoading}, 'button']" type="submit">
|
||||
{{ $gettext('Update settings') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui small text container">
|
||||
<h2 class="ui header"><i18next path="Change my password"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Change my password') }}
|
||||
</h2>
|
||||
<div class="ui message">
|
||||
{{ $t('Changing your password will also change your Subsonic API password if you have requested one.') }}
|
||||
{{ $t('You will have to update your password on your clients that use this password.') }}
|
||||
{{ $gettext('Changing your password will also change your Subsonic API password if you have requested one.') }}
|
||||
{{ $gettext('You will have to update your password on your clients that use this password.') }}
|
||||
</div>
|
||||
<form class="ui form" @submit.prevent="submitPassword()">
|
||||
<div v-if="passwordError" class="ui negative message">
|
||||
<div class="header"><i18next path="Cannot change your password"/></div>
|
||||
<div class="header">
|
||||
{{ $gettext('Cannot change your password') }}
|
||||
</div>
|
||||
<ul class="list">
|
||||
<i18next tag="li" v-if="passwordError == 'invalid_credentials'" path="Please double-check your password is correct"/>
|
||||
<li v-if="passwordError == 'invalid_credentials'">{{ $gettext('Please double-check your password is correct') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label><i18next path="Old password"/></label>
|
||||
<label>{{ $gettext('Old password') }}</label>
|
||||
<password-input required v-model="old_password" />
|
||||
|
||||
</div>
|
||||
<div class="field">
|
||||
<label><i18next path="New password"/></label>
|
||||
<label>{{ $gettext('New password') }}</label>
|
||||
<password-input required v-model="new_password" />
|
||||
</div>
|
||||
<dangerous-button
|
||||
color="yellow"
|
||||
:class="['ui', {'loading': isLoading}, 'button']"
|
||||
:action="submitPassword">
|
||||
{{ $t('Change password') }}
|
||||
<p slot="modal-header">{{ $t('Change your password?') }}</p>
|
||||
{{ $gettext('Change password') }}
|
||||
<p slot="modal-header">{{ $gettext('Change your password?') }}</p>
|
||||
<div slot="modal-content">
|
||||
<p>{{ $t("Changing your password will have the following consequences") }}</p>
|
||||
<p>{{ $gettext("Changing your password will have the following consequences") }}</p>
|
||||
<ul>
|
||||
<li>{{ $t('You will be logged out from this session and have to log out with the new one') }}</li>
|
||||
<li>{{ $t('Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password') }}</li>
|
||||
<li>{{ $gettext('You will be logged out from this session and have to log out with the new one') }}</li>
|
||||
<li>{{ $gettext('Your Subsonic password will be changed to a new, random one, logging you out from devices that used the old Subsonic password') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p slot="modal-confirm">{{ $t('Disable access') }}</p>
|
||||
<p slot="modal-confirm">{{ $gettext('Disable access') }}</p>
|
||||
</dangerous-button>
|
||||
</form>
|
||||
<div class="ui hidden divider" />
|
||||
|
|
|
@ -2,22 +2,22 @@
|
|||
<div class="main pusher" v-title="'Sign Up'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2>{{ $t("Create a funkwhale account") }}</h2>
|
||||
<h2>{{ $gettext("Create a funkwhale account") }}</h2>
|
||||
<form
|
||||
:class="['ui', {'loading': isLoadingInstanceSetting}, 'form']"
|
||||
@submit.prevent="submit()">
|
||||
<p class="ui message" v-if="!$store.state.instance.settings.users.registration_enabled.value">
|
||||
{{ $t('Registration are closed on this instance, you will need an invitation code to signup.') }}
|
||||
{{ $gettext('Registration are closed on this instance, you will need an invitation code to signup.') }}
|
||||
</p>
|
||||
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t("We cannot create your account") }}</div>
|
||||
<div class="header">{{ $gettext("We cannot create your account") }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Username") }}</label>
|
||||
<label>{{ $gettext("Username") }}</label>
|
||||
<input
|
||||
ref="username"
|
||||
required
|
||||
|
@ -27,7 +27,7 @@
|
|||
v-model="username">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Email") }}</label>
|
||||
<label>{{ $gettext("Email") }}</label>
|
||||
<input
|
||||
ref="email"
|
||||
required
|
||||
|
@ -36,20 +36,20 @@
|
|||
v-model="email">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Password") }}</label>
|
||||
<label>{{ $gettext("Password") }}</label>
|
||||
<password-input v-model="password" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label v-if="!$store.state.instance.settings.users.registration_enabled.value">{{ $t("Invitation code") }}</label>
|
||||
<label v-else>{{ $t("Invitation code (optional)") }}</label>
|
||||
<label v-if="!$store.state.instance.settings.users.registration_enabled.value">{{ $gettext("Invitation code") }}</label>
|
||||
<label v-else>{{ $gettext("Invitation code (optional)") }}</label>
|
||||
<input
|
||||
:required="!$store.state.instance.settings.users.registration_enabled.value"
|
||||
type="text"
|
||||
:placeholder="$t('Enter your invitation code (case insensitive)')"
|
||||
:placeholder="$gettext('Enter your invitation code (case insensitive)')"
|
||||
v-model="invitation">
|
||||
</div>
|
||||
<button :class="['ui', 'green', {'loading': isLoading}, 'button']" type="submit">
|
||||
{{ $t("Create my account") }}
|
||||
{{ $gettext("Create my account") }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<template>
|
||||
<form class="ui form" @submit.prevent="requestNewToken()">
|
||||
<h2>{{ $t('Subsonic API password') }}</h2>
|
||||
<h2>{{ $gettext('Subsonic API password') }}</h2>
|
||||
<p class="ui message" v-if="!subsonicEnabled">
|
||||
{{ $t('The Subsonic API is not available on this Funkwhale instance.') }}
|
||||
{{ $gettext('The Subsonic API is not available on this Funkwhale instance.') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('Funkwhale is compatible with other music players that support the Subsonic API.') }}
|
||||
{{ $t('You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.') }}
|
||||
{{ $gettext('Funkwhale is compatible with other music players that support the Subsonic API.') }}
|
||||
{{ $gettext('You can use those to enjoy your playlist and music in offline mode, on your smartphone or tablet, for instance.') }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('However, accessing Funkwhale from those clients require a separate password you can set below.') }}
|
||||
{{ $gettext('However, accessing Funkwhale from those clients require a separate password you can set below.') }}
|
||||
</p>
|
||||
<p><a href="https://docs.funkwhale.audio/users/apps.html#subsonic-compatible-clients" target="_blank">
|
||||
{{ $t('Discover how to use Funkwhale from other apps') }}
|
||||
{{ $gettext('Discover how to use Funkwhale from other apps') }}
|
||||
</a></p>
|
||||
<div v-if="success" class="ui positive message">
|
||||
<div class="header">{{ successMessage }}</div>
|
||||
</div>
|
||||
<div v-if="subsonicEnabled && errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error') }}</div>
|
||||
<div class="header">{{ $gettext('Error') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
|
@ -32,25 +32,25 @@
|
|||
color="grey"
|
||||
:class="['ui', {'loading': isLoading}, 'button']"
|
||||
:action="requestNewToken">
|
||||
{{ $t('Request a new password') }}
|
||||
<p slot="modal-header">{{ $t('Request a new Subsonic API password?') }}</p>
|
||||
<p slot="modal-content">{{ $t('This will log you out from existing devices that use the current password.') }}</p>
|
||||
<p slot="modal-confirm">{{ $t('Request a new password') }}</p>
|
||||
{{ $gettext('Request a new password') }}
|
||||
<p slot="modal-header">{{ $gettext('Request a new Subsonic API password?') }}</p>
|
||||
<p slot="modal-content">{{ $gettext('This will log you out from existing devices that use the current password.') }}</p>
|
||||
<p slot="modal-confirm">{{ $gettext('Request a new password') }}</p>
|
||||
</dangerous-button>
|
||||
<button
|
||||
v-else
|
||||
color="grey"
|
||||
:class="['ui', {'loading': isLoading}, 'button']"
|
||||
@click="requestNewToken">{{ $t('Request a password') }}</button>
|
||||
@click="requestNewToken">{{ $gettext('Request a password') }}</button>
|
||||
<dangerous-button
|
||||
v-if="token"
|
||||
color="yellow"
|
||||
:class="['ui', {'loading': isLoading}, 'button']"
|
||||
:action="disable">
|
||||
{{ $t('Disable Subsonic access') }}
|
||||
<p slot="modal-header">{{ $t('Disable Subsonic API access?') }}</p>
|
||||
<p slot="modal-content">{{ $t('This will completely disable access to the Subsonic API using from account.') }}</p>
|
||||
<p slot="modal-confirm">{{ $t('Disable access') }}</p>
|
||||
{{ $gettext('Disable Subsonic access') }}
|
||||
<p slot="modal-header">{{ $gettext('Disable Subsonic API access?') }}</p>
|
||||
<p slot="modal-content">{{ $gettext('This will completely disable access to the Subsonic API using from account.') }}</p>
|
||||
<p slot="modal-confirm">{{ $gettext('Disable access') }}</p>
|
||||
</dangerous-button>
|
||||
</template>
|
||||
</form>
|
||||
|
@ -92,7 +92,7 @@ export default {
|
|||
})
|
||||
},
|
||||
requestNewToken () {
|
||||
this.successMessage = this.$t('Password updated')
|
||||
this.successMessage = this.$gettext('Password updated')
|
||||
this.success = false
|
||||
this.errors = []
|
||||
this.isLoading = true
|
||||
|
@ -108,7 +108,7 @@ export default {
|
|||
})
|
||||
},
|
||||
disable () {
|
||||
this.successMessage = this.$t('Access disabled')
|
||||
this.successMessage = this.$gettext('Access disabled')
|
||||
this.success = false
|
||||
this.errors = []
|
||||
this.isLoading = true
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="ui small form">
|
||||
<div class="ui inline fields">
|
||||
<div class="field">
|
||||
<label>{{ $t('Actions') }}</label>
|
||||
<label>{{ $gettext('Actions') }}</label>
|
||||
<select class="ui dropdown" v-model="currentActionName">
|
||||
<option v-for="action in actions" :value="action.name">
|
||||
{{ action.label }}
|
||||
|
@ -19,41 +19,75 @@
|
|||
@click="launchAction"
|
||||
:disabled="checked.length === 0"
|
||||
:class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']">
|
||||
{{ $t('Go') }}</div>
|
||||
{{ $gettext('Go') }}</div>
|
||||
<dangerous-button
|
||||
v-else-if="!currentAction.isDangerous" :class="['ui', {disabled: checked.length === 0}, {'loading': actionLoading}, 'button']"
|
||||
confirm-color="green"
|
||||
color=""
|
||||
@confirm="launchAction">
|
||||
{{ $t('Go') }}
|
||||
<p slot="modal-header">{{ $t('Do you want to launch action "{% action %}" on {% total %} elements?', {action: currentActionName, total: objectsData.count}) }}
|
||||
<p slot="modal-content">
|
||||
{{ $t('This may affect a lot of elements, please double check this is really what you want.')}}
|
||||
{{ $gettext('Go') }}
|
||||
<p slot="modal-header">
|
||||
<translate
|
||||
:translate-n="objectsData.count"
|
||||
:translate-params="{count: objectsData.count, action: currentActionName}"
|
||||
translate-plural="Do you want to launch %{ action } on %{ count } elements?">
|
||||
Do you want to launch %{ action } on %{ count } element?
|
||||
</translate>
|
||||
</p>
|
||||
<p slot="modal-confirm">{{ $t('Launch') }}</p>
|
||||
<p slot="modal-content">
|
||||
{{ $gettext('This may affect a lot of elements, please double check this is really what you want.')}}
|
||||
</p>
|
||||
<p slot="modal-confirm">{{ $gettext('Launch') }}</p>
|
||||
</dangerous-button>
|
||||
</div>
|
||||
<div class="count field">
|
||||
<span v-if="selectAll">{{ $t('{% count %} on {% total %} selected', {count: objectsData.count, total: objectsData.count}) }}</span>
|
||||
<span v-else>{{ $t('{% count %} on {% total %} selected', {count: checked.length, total: objectsData.count}) }}</span>
|
||||
<translate
|
||||
tag="span"
|
||||
v-if="selectAll"
|
||||
:translate-n="objectsData.count"
|
||||
:translate-params="{count: objectsData.count, total: objectsData.count}"
|
||||
translate-plural="%{ count } on %{ total } selected">
|
||||
%{ count } on %{ total } selected
|
||||
</translate>
|
||||
<translate
|
||||
tag="span"
|
||||
v-else
|
||||
:translate-n="checked.length"
|
||||
:translate-params="{count: checked.length, total: objectsData.count}"
|
||||
translate-plural="%{ count } on %{ total } selected">
|
||||
%{ count } on %{ total } selected
|
||||
</translate>
|
||||
<template v-if="!currentAction.isDangerous && checkable.length > 0 && checkable.length === checked.length">
|
||||
<a @click="selectAll = true" v-if="!selectAll">
|
||||
{{ $t('Select all {% total %} elements', {total: objectsData.count}) }}
|
||||
<translate
|
||||
:translate-n="objectsData.count"
|
||||
:translate-params="{total: objectsData.count}"
|
||||
translate-plural="Select all %{ total } elements">
|
||||
Select all %{ total } elements
|
||||
</translate>
|
||||
</a>
|
||||
<a @click="selectAll = false" v-else>
|
||||
{{ $t('Select only current page') }}
|
||||
{{ $gettext('Select only current page') }}
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="actionErrors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while applying action') }}</div>
|
||||
<div class="header">{{ $gettext('Error while applying action') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in actionErrors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="actionResult" class="ui positive message">
|
||||
<p>{{ $t('Action {% action %} was launched successfully on {% count %} objects.', {action: actionResult.action, count: actionResult.updated}) }}</p>
|
||||
<p>
|
||||
<translate
|
||||
:translate-n="actionResult.updated"
|
||||
:translate-params="{count: actionResult.updated, action: actionResult.action}"
|
||||
translate-plural="Action %{ action } was launched successfully on %{ count } elements">
|
||||
Action %{ action } was launched successfully on %{ count } element
|
||||
</translate>
|
||||
</p>
|
||||
|
||||
<slot name="action-success-footer" :result="actionResult">
|
||||
</slot>
|
||||
</div>
|
||||
|
@ -81,7 +115,6 @@
|
|||
:disabled="checkable.indexOf(obj.id) === -1"
|
||||
@click="toggleCheck($event, obj.id, index)"
|
||||
:checked="checked.indexOf(obj.id) > -1"><label> </label>
|
||||
</div>
|
||||
</td>
|
||||
<slot name="row-cells" :obj="obj"></slot>
|
||||
</tr>
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
<modal class="small" :show.sync="showModal">
|
||||
<div class="header">
|
||||
<slot name="modal-header"><i18next path="Do you want to confirm this action?"/></slot>
|
||||
<slot name="modal-header">
|
||||
{{ $gettext('Do you want to confirm this action?') }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="scrolling content">
|
||||
<div class="description">
|
||||
|
@ -12,9 +14,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui cancel button"><i18next path="Cancel"/></div>
|
||||
<div class="ui cancel button">
|
||||
{{ $gettext('Cancel') }}
|
||||
</div>
|
||||
<div :class="['ui', 'confirm', confirmButtonColor, 'button']" @click="confirm">
|
||||
<slot name="modal-confirm"><i18next path="Confirm"/></slot>
|
||||
<slot name="modal-confirm">
|
||||
{{ $gettext('Confirm') }}
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
|
|
|
@ -11,13 +11,15 @@
|
|||
<span
|
||||
@click="collapsed = false"
|
||||
v-if="truncated && collapsed"
|
||||
class="expand"
|
||||
path="Expand"/>
|
||||
<i18next
|
||||
class="expand">
|
||||
{{ $gettext('Expand') }}
|
||||
</span>
|
||||
<span
|
||||
@click="collapsed = true"
|
||||
v-if="truncated && !collapsed"
|
||||
class="collapse"
|
||||
path="Collapse"/>
|
||||
class="collapse">
|
||||
{{ $gettext('Collapse') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,13 +2,18 @@
|
|||
<div class="main pusher" v-title="'Your Favorites'">
|
||||
<div class="ui vertical center aligned stripe segment">
|
||||
<div :class="['ui', {'active': isLoading}, 'inverted', 'dimmer']">
|
||||
<div class="ui text loader"><i18next path="Loading your favorites..."/></div>
|
||||
<div class="ui text loader">
|
||||
{{ $gettext('Loading your favorites...') }}
|
||||
</div>
|
||||
</div>
|
||||
<h2 v-if="results" class="ui center aligned icon header">
|
||||
<i class="circular inverted heart pink icon"></i>
|
||||
<i18next path="{%0%} favorites">
|
||||
{{ $store.state.favorites.count }}
|
||||
</i18next>
|
||||
<translate
|
||||
translate-plural="%{ count } favorites"
|
||||
:translate-n="$store.state.favorites.count"
|
||||
:translate-params="{count: $store.state.favorites.count}">
|
||||
1 favorite
|
||||
</translate>
|
||||
</h2>
|
||||
<radio-button type="favorites"></radio-button>
|
||||
</div>
|
||||
|
@ -16,7 +21,7 @@
|
|||
<div :class="['ui', {'loading': isLoading}, 'form']">
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -24,14 +29,14 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+"><i18next path="Ascending"/></option>
|
||||
<option value="-"><i18next path="Descending"/></option>
|
||||
<option value="+">{{ $gettext('Ascending') }}</option>
|
||||
<option value="-">{{ $gettext('Descending') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Results per page"/>
|
||||
<label>{{ $gettext('Results per page') }}</label>
|
||||
<select class="ui dropdown" v-model="paginateBy">
|
||||
<option :value="parseInt(12)">12</option>
|
||||
<option :value="parseInt(25)">25</option>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<button @click="$store.dispatch('favorites/toggle', track.id)" v-if="button" :class="['ui', 'pink', {'inverted': isFavorite}, {'favorited': isFavorite}, 'button']">
|
||||
<i class="heart icon"></i>
|
||||
<i18next v-if="isFavorite" path="In favorites"/>
|
||||
<i18next v-else path="Add to favorites"/>
|
||||
<translate v-if="isFavorite">In favorites</translate>
|
||||
<translate v-else>Add to favorites</translate>
|
||||
</button>
|
||||
<i v-else @click="$store.dispatch('favorites/toggle', track.id)" :class="['favorite-icon', 'heart', {'pink': isFavorite}, {'favorited': isFavorite}, 'link', 'icon']" :title="title"></i>
|
||||
</template>
|
||||
|
@ -16,9 +16,9 @@ export default {
|
|||
computed: {
|
||||
title () {
|
||||
if (this.isFavorite) {
|
||||
return this.$t('Remove from favorites')
|
||||
return this.$gettext('Remove from favorites')
|
||||
} else {
|
||||
return this.$t('Add to favorites')
|
||||
return this.$gettext('Add to favorites')
|
||||
}
|
||||
},
|
||||
isFavorite () {
|
||||
|
|
|
@ -13,39 +13,42 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<span class="right floated" v-if="following">
|
||||
<i class="check icon"></i><i18next path="Following"/>
|
||||
<i class="check icon"></i>{{ $gettext('Following') }}
|
||||
</span>
|
||||
<span class="right floated" v-else-if="manuallyApprovesFollowers">
|
||||
<i class="lock icon"></i><i18next path="Followers only"/>
|
||||
<i class="lock icon"></i>{{ $gettext('Followers only') }}
|
||||
</span>
|
||||
<span class="right floated" v-else>
|
||||
<i class="open lock icon"></i><i18next path="Open"/>
|
||||
<i class="open lock icon"></i>{{ $gettext('Open') }}
|
||||
</span>
|
||||
<span v-if="totalItems">
|
||||
<i class="music icon"></i>
|
||||
<i18next path="{%0%} tracks">
|
||||
{{ totalItems }}
|
||||
</i18next>
|
||||
<translate
|
||||
translate-plural="%{ count } tracks"
|
||||
:translate-n="totalItems"
|
||||
:translate-params="{count: totalItems}">
|
||||
1 track
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<template v-if="awaitingApproval">
|
||||
<i class="clock icon"></i>
|
||||
<i18next path="Follow request pending approval"/>
|
||||
{{ $gettext('Follow request pending approval') }}
|
||||
</template>
|
||||
<div
|
||||
v-if="!library"
|
||||
@click="follow"
|
||||
:disabled="isLoading"
|
||||
:class="['ui', 'basic', {loading: isLoading}, 'green', 'button']">
|
||||
<i18next v-if="manuallyApprovesFollowers" path="Send a follow request"/>
|
||||
<i18next v-else path="Follow"/>
|
||||
<translate v-if="manuallyApprovesFollowers">Send a follow request</translate>
|
||||
<translate v-else>Follow</translate>
|
||||
</div>
|
||||
<router-link
|
||||
v-else
|
||||
class="ui basic button"
|
||||
:to="{name: 'federation.libraries.detail', params: {id: library.uuid }}">
|
||||
<i18next path="Detail"/>
|
||||
{{ $gettext('Detail') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
<div class="ui four wide inline field">
|
||||
<div class="ui checkbox">
|
||||
<input v-model="pending" type="checkbox">
|
||||
<label><i18next path="Pending approval"/></label>
|
||||
<label>
|
||||
{{ $gettext('Pending approval') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,10 +19,10 @@
|
|||
<table v-if="result" class="ui very basic single line unstackable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<i18next tag="th" path="Actor"/>
|
||||
<i18next tag="th" path="Creation date"/>
|
||||
<i18next tag="th" path="Status"/>
|
||||
<i18next tag="th" path="Actions"/>
|
||||
<th>{{ $gettext('Actor') }}</th>
|
||||
<th>{{ $gettext('Creation date') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -33,36 +35,49 @@
|
|||
</td>
|
||||
<td>
|
||||
<template v-if="follow.approved === true">
|
||||
<i class="check icon"></i><i18next path="Approved"/>
|
||||
<i class="check icon"></i>
|
||||
{{ $gettext('Approved') }}
|
||||
</template>
|
||||
<template v-else-if="follow.approved === false">
|
||||
<i class="x icon"></i><i18next path="Refused"/>
|
||||
<i class="x icon"></i>
|
||||
{{ $gettext('Refused') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="clock icon"></i><i18next path="Pending"/>
|
||||
<i class="clock icon"></i>
|
||||
{{ $gettext('Pending') }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<dangerous-button v-if="follow.approved !== false" class="tiny basic labeled icon" color='red' @confirm="updateFollow(follow, false)">
|
||||
<i class="x icon"></i><i18next path="Deny"/>
|
||||
<p slot="modal-header"><i18next path="Deny access?"/></p>
|
||||
<p slot="modal-content">
|
||||
<i18next path="By confirming, {%0%}@{%1%} will be denied access to your library.">
|
||||
{{ follow.actor.preferred_username }}
|
||||
{{ follow.actor.domain }}
|
||||
</i18next>
|
||||
<i class="x icon"></i>
|
||||
{{ $gettext('Deny') }}
|
||||
<p slot="modal-header">
|
||||
{{ $gettext('Deny access?') }}
|
||||
</p>
|
||||
<p slot="modal-content">
|
||||
<translate
|
||||
:translate-params="{username: follow.actor.preferred_username + '@' + follow.actor.domain}">
|
||||
By confirming, %{ username } will be denied access to your library.
|
||||
</translate>
|
||||
</p>
|
||||
<p slot="modal-confirm">
|
||||
{{ $gettext('Deny') }}
|
||||
</p>
|
||||
<p slot="modal-confirm"><i18next path="Deny"/></p>
|
||||
</dangerous-button>
|
||||
<dangerous-button v-if="follow.approved !== true" class="tiny basic labeled icon" color='green' @confirm="updateFollow(follow, true)">
|
||||
<i class="check icon"></i> <i18next path="Approve"/>
|
||||
<p slot="modal-header"><i18next path="Approve access?"/></p>
|
||||
<i class="check icon"></i>
|
||||
{{ $gettext('Approve') }}
|
||||
<p slot="modal-header">
|
||||
{{ $gettext('Approve access?') }}
|
||||
</p>
|
||||
<p slot="modal-content">
|
||||
<i18next path="By confirming, {%0%}@{%1%} will be granted access to your library.">
|
||||
{{ follow.actor.preferred_username }}
|
||||
{{ follow.actor.domain }}
|
||||
</i18next>
|
||||
<p slot="modal-confirm"><i18next path="Approve"/></p>
|
||||
<translate
|
||||
:translate-params="{username: follow.actor.preferred_username + '@' + follow.actor.domain}">
|
||||
By confirming, %{ username } will be granted access to your library.
|
||||
</translate>
|
||||
<p slot="modal-confirm">
|
||||
{{ $gettext('Approve') }}
|
||||
</p>
|
||||
</dangerous-button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -80,11 +95,10 @@
|
|||
></pagination>
|
||||
</th>
|
||||
<th v-if="result && result.results.length > 0">
|
||||
<i18next path="Showing results {%0%}-{%1%} on {%2%}">
|
||||
{{ ((page-1) * paginateBy) + 1 }}
|
||||
{{ ((page-1) * paginateBy) + result.results.length }}
|
||||
{{ result.count }}
|
||||
</i18next>
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<template>
|
||||
<form class="ui form" @submit.prevent="fetchInstanceInfo">
|
||||
<h3 class="ui header"><i18next path="Federate with a new instance"/></h3>
|
||||
<p><i18next path="Use this form to scan an instance and setup federation."/></p>
|
||||
<h3 class="ui header">
|
||||
{{ $gettext('Federate with a new instance') }}
|
||||
</h3>
|
||||
<p>
|
||||
{{ $gettext('Use this form to scan an instance and setup federation.') }}
|
||||
</p>
|
||||
<div v-if="errors.length > 0 || scanErrors.length > 0" class="ui negative message">
|
||||
<div class="header"><i18next path="Error while scanning library"/></div>
|
||||
<div class="header">
|
||||
{{ $gettext('Error while scanning library') }}
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
<li v-for="error in scanErrors">{{ error }}</li>
|
||||
|
@ -11,7 +17,9 @@
|
|||
</div>
|
||||
<div class="ui two fields">
|
||||
<div class="ui field">
|
||||
<label><i18next path="Library name"/></label>
|
||||
<label>
|
||||
{{ $gettext('Library name') }}
|
||||
</label>
|
||||
<input v-model="libraryUsername" type="text" placeholder="library@demo.funkwhale.audio" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
|
@ -21,7 +29,7 @@
|
|||
:disabled="isLoading"
|
||||
:class="['ui', 'icon', {loading: isLoading}, 'button']">
|
||||
<i class="search icon"></i>
|
||||
<i18next path="Launch scan"/>
|
||||
{{ $gettext('Launch scan') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Import status') }}</label>
|
||||
<label>{{ $gettext('Import status') }}</label>
|
||||
<select class="ui dropdown" v-model="importedFilter">
|
||||
<option :value="null">{{ $t('Any') }}</option>
|
||||
<option :value="'imported'">{{ $t('Imported') }}</option>
|
||||
<option :value="'not_imported'">{{ $t('Not imported') }}</option>
|
||||
<option :value="'import_pending'">{{ $t('Import pending') }}</option>
|
||||
<option :value="null">{{ $gettext('Any') }}</option>
|
||||
<option :value="'imported'">{{ $gettext('Imported') }}</option>
|
||||
<option :value="'not_imported'">{{ $gettext('Not imported') }}</option>
|
||||
<option :value="'import_pending'">{{ $gettext('Import pending') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,25 +29,28 @@
|
|||
:action-url="'federation/library-tracks/action/'"
|
||||
:filters="actionFilters">
|
||||
<template slot="header-cells">
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $t('Title') }}</th>
|
||||
<th>{{ $t('Artist') }}</th>
|
||||
<th>{{ $t('Album') }}</th>
|
||||
<th>{{ $t('Published date') }}</th>
|
||||
<th v-if="showLibrary">{{ $t('Library') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Title') }}</th>
|
||||
<th>{{ $gettext('Artist') }}</th>
|
||||
<th>{{ $gettext('Album') }}</th>
|
||||
<th>{{ $gettext('Published date') }}</th>
|
||||
<th v-if="showLibrary">{{ $gettext('Library') }}</th>
|
||||
</template>
|
||||
<template slot="action-success-footer" slot-scope="scope">
|
||||
<router-link
|
||||
v-if="scope.result.action === 'import'"
|
||||
:to="{name: 'library.import.batches.detail', params: {id: scope.result.result.batch.id }}">
|
||||
{{ $t('Import #{% id %} launched', {id: scope.result.result.batch.id}) }}
|
||||
<translate
|
||||
:translate-params="{id: scope.result.result.batch.id}">
|
||||
Import #%{ id } launched
|
||||
</translate>
|
||||
</router-link>
|
||||
</template>
|
||||
<template slot="row-cells" slot-scope="scope">
|
||||
<td>
|
||||
<span v-if="scope.obj.status === 'imported'" class="ui basic green label">{{ $t('In library') }}</span>
|
||||
<span v-else-if="scope.obj.status === 'import_pending'" class="ui basic yellow label">{{ $t('Import pending') }}</span>
|
||||
<span v-else class="ui basic label">{{ $t('Not imported') }}</span>
|
||||
<span v-if="scope.obj.status === 'imported'" class="ui basic green label">{{ $gettext('In library') }}</span>
|
||||
<span v-else-if="scope.obj.status === 'import_pending'" class="ui basic yellow label">{{ $gettext('Import pending') }}</span>
|
||||
<span v-else class="ui basic label">{{ $gettext('Not imported') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span :title="scope.obj.title">{{ scope.obj.title|truncate(30) }}</span>
|
||||
|
@ -78,7 +81,10 @@
|
|||
></pagination>
|
||||
|
||||
<span v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -150,10 +156,11 @@ export default {
|
|||
}
|
||||
},
|
||||
actions () {
|
||||
let msg = this.$gettext('Import')
|
||||
return [
|
||||
{
|
||||
name: 'import',
|
||||
label: this.$t('Import'),
|
||||
label: msg,
|
||||
filterCheckable: (obj) => { return obj.status === 'not_imported' }
|
||||
}
|
||||
]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:type="passwordInputType"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
:value="value">
|
||||
<span @click="showPassword = !showPassword" :title="$t('Show/hide password')" class="ui icon button">
|
||||
<span @click="showPassword = !showPassword" :title="$gettext('Show/hide password')" class="ui icon button">
|
||||
<i class="eye icon"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -2,26 +2,28 @@
|
|||
<div>
|
||||
<div v-if="stats" class="ui stackable two column grid">
|
||||
<div class="column">
|
||||
<h3 class="ui left aligned header"><i18next path="User activity"/></h3>
|
||||
<h3 class="ui left aligned header">
|
||||
{{ $gettext('User activity') }}
|
||||
</h3>
|
||||
<div v-if="stats" class="ui mini horizontal statistics">
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="green user icon"></i>
|
||||
{{ stats.users }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="users"/>
|
||||
<div class="label">{{ $gettext('users') }}</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="orange sound icon"></i> {{ stats.listenings }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="tracks listened"/>
|
||||
<div class="label">{{ $gettext('tracks listened') }}</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
<i class="pink heart icon"></i> {{ stats.trackFavorites }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="Tracks favorited"/>
|
||||
<div class="label">{{ $gettext('Tracks favorited') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,25 +34,25 @@
|
|||
<div class="value">
|
||||
{{ parseInt(stats.musicDuration) }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="hours of music"/>
|
||||
<div class="label">{{ $gettext('Hours of music') }}</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
{{ stats.artists }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="Artists"/>
|
||||
<div class="label">{{ $gettext('Artists') }}</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
{{ stats.albums }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="Albums"/>
|
||||
<div class="label">{{ $gettext('Albums') }}</div>
|
||||
</div>
|
||||
<div class="statistic">
|
||||
<div class="value">
|
||||
{{ stats.tracks }}
|
||||
</div>
|
||||
<i18next tag="div" class="label" path="tracks"/>
|
||||
<div class="label">{{ $gettext('tracks') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,30 +10,39 @@
|
|||
<i class="circular inverted sound yellow icon"></i>
|
||||
<div class="content">
|
||||
{{ album.title }}
|
||||
<i18next tag="div" class="sub header" path="Album containing {%0%} tracks, by {%1%}">
|
||||
{{ album.tracks.length }}
|
||||
<router-link :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
|
||||
{{ album.artist.name }}
|
||||
</router-link>
|
||||
</i18next>
|
||||
<translate
|
||||
tag="div"
|
||||
translate-plural="Album containing %{ count } tracks, by %{ artist }"
|
||||
:translate-n="album.tracks.length"
|
||||
:translate-params="{count: album.tracks.length, artist: album.artist.name}">
|
||||
Album containing %{ count } track, by %{ artist }
|
||||
</translate>
|
||||
</div>
|
||||
<div class="ui basic buttons">
|
||||
<router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: album.artist.id }}">
|
||||
{{ $gettext('Artist page') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="ui hidden divider"></div>
|
||||
</button>
|
||||
<play-button class="orange" :tracks="album.tracks"><i18next path="Play all"/></play-button>
|
||||
<play-button class="orange" :tracks="album.tracks">
|
||||
{{ $gettext('Play all') }}
|
||||
</play-button>
|
||||
|
||||
<a :href="wikipediaUrl" target="_blank" class="ui button">
|
||||
<i class="wikipedia icon"></i>
|
||||
<i18next path="Search on Wikipedia"/>
|
||||
{{ $gettext('Search on Wikipedia') }}
|
||||
</a>
|
||||
<a :href="musicbrainzUrl" target="_blank" class="ui button">
|
||||
<i class="external icon"></i>
|
||||
<i18next path="View on MusicBrainz"/>
|
||||
{{ $gettext('View on MusicBrainz') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2><i18next path="Tracks"/></h2>
|
||||
<h2>
|
||||
{{ $gettext('Tracks') }}
|
||||
</h2>
|
||||
<track-table v-if="album" :display-position="true" :tracks="album.tracks"></track-table>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -11,22 +11,29 @@
|
|||
<div class="content">
|
||||
{{ artist.name }}
|
||||
<div class="sub header" v-if="albums">
|
||||
{{ $t('{% track_count %} tracks in {% album_count %} albums', {track_count: totalTracks, album_count: albums.length})}}
|
||||
<translate
|
||||
tag="div"
|
||||
translate-plural="%{ count } tracks in %{ albumsCount } albums"
|
||||
:translate-n="totalTracks"
|
||||
:translate-params="{count: totalTracks, albumsCount: albums.length}">
|
||||
%{ count } track in %{ albumsCount } albums
|
||||
</translate>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="ui hidden divider"></div>
|
||||
<radio-button type="artist" :object-id="artist.id"></radio-button>
|
||||
</button>
|
||||
<play-button class="orange" :artist="artist.id"><i18next path="Play all albums"/></play-button>
|
||||
<play-button class="orange" :artist="artist.id">
|
||||
{{ $gettext('Play all albums') }}
|
||||
</play-button>
|
||||
|
||||
<a :href="wikipediaUrl" target="_blank" class="ui button">
|
||||
<i class="wikipedia icon"></i>
|
||||
<i18next path="Search on Wikipedia"/>
|
||||
{{ $gettext('Search on Wikipedia') }}
|
||||
</a>
|
||||
<a :href="musicbrainzUrl" target="_blank" class="ui button">
|
||||
<i class="external icon"></i>
|
||||
<i18next path="View on MusicBrainz"/>
|
||||
{{ $gettext('View on MusicBrainz') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -34,7 +41,9 @@
|
|||
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
|
||||
</div>
|
||||
<div v-else-if="albums" class="ui vertical stripe segment">
|
||||
<h2><i18next path="Albums by this artist"/></h2>
|
||||
<h2>
|
||||
{{ $gettext('Albums by this artist') }}
|
||||
</h2>
|
||||
<div class="ui stackable doubling three column grid">
|
||||
<div class="column" :key="album.id" v-for="album in albums">
|
||||
<album-card :mode="'rich'" class="fluid" :album="album"></album-card>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<div v-title="'Artists'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header"><i18next path="Browsing artists"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Browsing artists') }}
|
||||
</h2>
|
||||
<div :class="['ui', {'loading': isLoading}, 'form']">
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label><i18next path="Search"/></label>
|
||||
<label>
|
||||
{{ $gettext('Search') }}
|
||||
</label>
|
||||
<input type="text" v-model="query" placeholder="Enter an artist name..."/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -17,14 +21,14 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+">Ascending</option>
|
||||
<option value="-">Descending</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Results per page"/>
|
||||
<label>{{ $gettext('Results per page') }}</label>
|
||||
<select class="ui dropdown" v-model="paginateBy">
|
||||
<option :value="parseInt(12)">12</option>
|
||||
<option :value="parseInt(25)">25</option>
|
||||
|
|
|
@ -6,20 +6,26 @@
|
|||
<div class="ui vertical stripe segment">
|
||||
<div class="ui stackable three column grid">
|
||||
<div class="column">
|
||||
<h2 class="ui header"><i18next path="Latest artists"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Latest artists') }}
|
||||
</h2>
|
||||
<div :class="['ui', {'active': isLoadingArtists}, 'inline', 'loader']"></div>
|
||||
<div v-if="artists.length > 0" v-for="artist in artists.slice(0, 3)" :key="artist.id" class="ui cards">
|
||||
<artist-card :artist="artist"></artist-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="ui header"><i18next path="Radios"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Radios') }}
|
||||
</h2>
|
||||
<radio-card :type="'favorites'"></radio-card>
|
||||
<radio-card :type="'random'"></radio-card>
|
||||
<radio-card :type="'less-listened'"></radio-card>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="ui header"><i18next path="Music requests"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Music requests') }}
|
||||
</h2>
|
||||
<request-form v-if="$store.state.auth.authenticated"></request-form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
<template>
|
||||
<div class="main library pusher">
|
||||
<div class="ui secondary pointing menu">
|
||||
<router-link class="ui item" to="/library" exact><i18next path="Browse"/></router-link>
|
||||
<router-link class="ui item" to="/library/artists" exact><i18next path="Artists"/></router-link>
|
||||
<router-link class="ui item" to="/library/radios" exact><i18next path="Radios"/></router-link>
|
||||
<router-link class="ui item" to="/library/playlists" exact><i18next path="Playlists"/></router-link>
|
||||
<router-link class="ui item" to="/library" exact>
|
||||
{{ $gettext('Browse') }}
|
||||
</router-link>
|
||||
<router-link class="ui item" to="/library/artists" exact>
|
||||
{{ $gettext('Artists') }}
|
||||
</router-link>
|
||||
<router-link class="ui item" to="/library/radios" exact>
|
||||
{{ $gettext('Radios') }}
|
||||
</router-link>
|
||||
<router-link class="ui item" to="/library/playlists" exact>
|
||||
{{ $gettext('Playlists') }}
|
||||
</router-link>
|
||||
<div class="ui secondary right menu">
|
||||
<router-link v-if="showImports" class="ui item" to="/library/import/launch" exact>
|
||||
<i18next path="Import"/>
|
||||
{{ $gettext('Import') }}
|
||||
</router-link>
|
||||
<router-link v-if="showImports" class="ui item" to="/library/import/batches">
|
||||
<i18next path="Import batches"/>
|
||||
{{ $gettext('Import batches') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
<template>
|
||||
<div v-title="'Radios'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header"><i18next path="Browsing radios"/></h2>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Browsing radios') }}
|
||||
</h2>
|
||||
<router-link class="ui green basic button" to="/library/radios/build" exact>
|
||||
<i18next path="Create your own radio"/>
|
||||
{{ $gettext('Create your own radio') }}
|
||||
</router-link>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div :class="['ui', {'loading': isLoading}, 'form']">
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Search"/>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="query" placeholder="Enter a radio name..."/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -21,14 +23,18 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+"><i18next path="Ascending"/></option>
|
||||
<option value="-"><i18next path="Descending"/></option>
|
||||
<option value="+">
|
||||
{{ $gettext('Ascending') }}
|
||||
</option>
|
||||
<option value="-">
|
||||
{{ $gettext('Descending') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Results per page"/>
|
||||
<label>{{ $gettext('Results per page') }}</label>
|
||||
<select class="ui dropdown" v-model="paginateBy">
|
||||
<option :value="parseInt(12)">12</option>
|
||||
<option :value="parseInt(25)">25</option>
|
||||
|
|
|
@ -11,18 +11,25 @@
|
|||
<div class="content">
|
||||
{{ track.title }}
|
||||
<div class="sub header">
|
||||
<i18next path="From album {%0%} by {%1%}">
|
||||
<router-link :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
|
||||
{{ track.album.title }}
|
||||
</router-link><router-link :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
|
||||
{{ track.artist.name }}
|
||||
</router-link>
|
||||
</i18next>
|
||||
<translate :translate-params="{album: track.album.title, artist: track.artist.name}">
|
||||
From album %{ album } by %{ artist }
|
||||
</translate>
|
||||
</div>
|
||||
<br>
|
||||
<div class="ui basic buttons">
|
||||
<router-link class="ui button" :to="{name: 'library.albums.detail', params: {id: track.album.id }}">
|
||||
{{ $gettext('Album page') }}
|
||||
</router-link>
|
||||
<router-link class="ui button" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">
|
||||
{{ $gettext('Artist page') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<play-button class="orange" :track="track"><i18next path="Play"/></play-button>
|
||||
<play-button class="orange" :track="track">
|
||||
{{ $gettext('Play') }}
|
||||
</play-button>
|
||||
<track-favorite-icon :track="track" :button="true"></track-favorite-icon>
|
||||
<track-playlist-icon
|
||||
:button="true"
|
||||
|
@ -31,70 +38,72 @@
|
|||
|
||||
<a :href="wikipediaUrl" target="_blank" class="ui button">
|
||||
<i class="wikipedia icon"></i>
|
||||
<i18next path="Search on Wikipedia"/>
|
||||
{{ $gettext('Search on Wikipedia') }}
|
||||
</a>
|
||||
<a :href="musicbrainzUrl" target="_blank" class="ui button">
|
||||
<i class="external icon"></i>
|
||||
<i18next path="View on MusicBrainz"/>
|
||||
{{ $gettext('View on MusicBrainz') }}
|
||||
</a>
|
||||
<a v-if="downloadUrl" :href="downloadUrl" target="_blank" class="ui button">
|
||||
<i class="download icon"></i>
|
||||
<i18next path="Download"/>
|
||||
{{ $gettext('Download') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="file" class="ui vertical stripe center aligned segment">
|
||||
<h2 class="ui header">{{ $t('Track information') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Track information') }}</h2>
|
||||
<table class="ui very basic collapsing celled center aligned table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Duration') }}
|
||||
{{ $gettext('Duration') }}
|
||||
</td>
|
||||
<td v-if="file.duration">
|
||||
{{ time.parse(file.duration) }}
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Size') }}
|
||||
{{ $gettext('Size') }}
|
||||
</td>
|
||||
<td v-if="file.size">
|
||||
{{ file.size | humanSize }}
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Bitrate') }}
|
||||
{{ $gettext('Bitrate') }}
|
||||
</td>
|
||||
<td v-if="file.bitrate">
|
||||
{{ file.bitrate | humanSize }}/s
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ui vertical stripe center aligned segment">
|
||||
<h2><i18next path="Lyrics"/></h2>
|
||||
<h2>
|
||||
{{ $gettext('Lyrics') }}
|
||||
</h2>
|
||||
<div v-if="isLoadingLyrics" class="ui vertical segment">
|
||||
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
|
||||
</div>
|
||||
<div v-if="lyrics" v-html="lyrics.content_rendered">
|
||||
</div>
|
||||
<template v-if="!isLoadingLyrics & !lyrics">
|
||||
<i18next tag="p" path="No lyrics available for this track."/>
|
||||
<p>{{ $gettext('No lyrics available for this track.') }}</p>
|
||||
<a class="ui button" target="_blank" :href="lyricsSearchUrl">
|
||||
<i class="search icon"></i>
|
||||
<i18next path="Search on lyrics.wikia.com"/>
|
||||
{{ $gettext('Search on lyrics.wikia.com') }}
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
<a :href="getMusicbrainzUrl('artist', metadata.id)" target="_blank" title="View on MusicBrainz">{{ metadata.name }}</a>
|
||||
</h3>
|
||||
<form class="ui form" @submit.prevent="">
|
||||
<h6 class="ui header"><i18next path="Filter album types"/></h6>
|
||||
<h6 class="ui header">
|
||||
{{ $gettext('Filter album types') }}
|
||||
</h6>
|
||||
<div class="inline fields">
|
||||
<div class="field" v-for="t in availableReleaseTypes">
|
||||
<div class="ui checkbox">
|
||||
|
@ -13,7 +15,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Query template"/>
|
||||
<label>{{ $gettext('Query template') }}</label>
|
||||
<input v-model="customQueryTemplate" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ $t('Import batch') }}</strong>
|
||||
<strong>{{ $gettext('Import batch') }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
#{{ batch.id }}
|
||||
|
@ -16,7 +16,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ $t('Launch date') }}</strong>
|
||||
<strong>{{ $gettext('Launch date') }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<human-date :date="batch.creation_date"></human-date>
|
||||
|
@ -24,22 +24,22 @@
|
|||
</tr>
|
||||
<tr v-if="batch.user">
|
||||
<td>
|
||||
<strong>{{ $t('Submitted by') }}</strong>
|
||||
<strong>{{ $gettext('Submitted by') }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<username :username="batch.user.username" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="stats">
|
||||
<td><strong>{{ $t('Pending') }}</strong></td>
|
||||
<td><strong>{{ $gettext('Pending') }}</strong></td>
|
||||
<td>{{ stats.pending }}</td>
|
||||
</tr>
|
||||
<tr v-if="stats">
|
||||
<td><strong>{{ $t('Skipped') }}</strong></td>
|
||||
<td><strong>{{ $gettext('Skipped') }}</strong></td>
|
||||
<td>{{ stats.skipped }}</td>
|
||||
</tr>
|
||||
<tr v-if="stats">
|
||||
<td><strong>{{ $t('Errored') }}</strong></td>
|
||||
<td><strong>{{ $gettext('Errored') }}</strong></td>
|
||||
<td>
|
||||
{{ stats.errored }}
|
||||
<button
|
||||
|
@ -47,12 +47,12 @@
|
|||
v-if="stats.errored > 0"
|
||||
class="ui tiny basic icon button">
|
||||
<i class="redo icon" />
|
||||
{{ $t('Rerun errored jobs')}}
|
||||
{{ $gettext('Rerun errored jobs')}}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="stats">
|
||||
<td><strong>{{ $t('Finished') }}</strong></td>
|
||||
<td><strong>{{ $gettext('Finished') }}</strong></td>
|
||||
<td>{{ stats.finished }}/{{ stats.count}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -60,17 +60,17 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="jobFilters.search" placeholder="Search by source..." />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Status') }}</label>
|
||||
<label>{{ $gettext('Status') }}</label>
|
||||
<select class="ui dropdown" v-model="jobFilters.status">
|
||||
<option :value="null">{{ $t('Any') }}</option>
|
||||
<option :value="'pending'">{{ $t('Pending') }}</option>
|
||||
<option :value="'errored'">{{ $t('Errored') }}</option>
|
||||
<option :value="'finished'">{{ $t('Success') }}</option>
|
||||
<option :value="'skipped'">{{ $t('Skipped') }}</option>
|
||||
<option :value="null">{{ $gettext('Any') }}</option>
|
||||
<option :value="'pending'">{{ $gettext('Pending') }}</option>
|
||||
<option :value="'errored'">{{ $gettext('Errored') }}</option>
|
||||
<option :value="'finished'">{{ $gettext('Success') }}</option>
|
||||
<option :value="'skipped'">{{ $gettext('Skipped') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,11 +78,11 @@
|
|||
<table v-if="jobResult" class="ui unstackable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('Job ID') }}</th>
|
||||
<th>{{ $t('Recording MusicBrainz ID') }}</th>
|
||||
<th>{{ $t('Source') }}</th>
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $t('Track') }}</th>
|
||||
<th>{{ $gettext('Job ID') }}</th>
|
||||
<th>{{ $gettext('Recording MusicBrainz ID') }}</th>
|
||||
<th>{{ $gettext('Source') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Track') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -103,7 +103,7 @@
|
|||
<button
|
||||
@click="rerun({batches: [], jobs: [job.id]})"
|
||||
v-if="job.status === 'errored'"
|
||||
:title="$t('Rerun job')"
|
||||
:title="$gettext('Rerun job')"
|
||||
class="ui tiny basic icon button">
|
||||
<i class="redo icon" />
|
||||
</button>
|
||||
|
@ -126,7 +126,10 @@
|
|||
></pagination>
|
||||
</th>
|
||||
<th v-if="jobResult && jobResult.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((jobFilters.page-1) * jobFilters.paginateBy) + 1 , end: ((jobFilters.page-1) * jobFilters.paginateBy) + jobResult.results.length, total: jobResult.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((jobFilters.page-1) * jobFilters.paginateBy) + 1, end: ((jobFilters.page-1) * jobFilters.paginateBy) + jobResult.results.length, total: jobResult.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
<th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
|
|
|
@ -5,25 +5,25 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="filters.search" placeholder="Search by submitter, source..." />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Status') }}</label>
|
||||
<label>{{ $gettext('Status') }}</label>
|
||||
<select class="ui dropdown" v-model="filters.status">
|
||||
<option :value="null">{{ $t('Any') }}</option>
|
||||
<option :value="'pending'">{{ $t('Pending') }}</option>
|
||||
<option :value="'errored'">{{ $t('Errored') }}</option>
|
||||
<option :value="'finished'">{{ $t('Success') }}</option>
|
||||
<option :value="null">{{ $gettext('Any') }}</option>
|
||||
<option :value="'pending'">{{ $gettext('Pending') }}</option>
|
||||
<option :value="'errored'">{{ $gettext('Errored') }}</option>
|
||||
<option :value="'finished'">{{ $gettext('Success') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Import source') }}</label>
|
||||
<label>{{ $gettext('Import source') }}</label>
|
||||
<select class="ui dropdown" v-model="filters.source">
|
||||
<option :value="null">{{ $t('Any') }}</option>
|
||||
<option :value="'shell'">{{ $t('CLI') }}</option>
|
||||
<option :value="'api'">{{ $t('API') }}</option>
|
||||
<option :value="'federation'">{{ $t('Federation') }}</option>
|
||||
<option :value="null">{{ $gettext('Any') }}</option>
|
||||
<option :value="'shell'">{{ $gettext('CLI') }}</option>
|
||||
<option :value="'api'">{{ $gettext('API') }}</option>
|
||||
<option :value="'federation'">{{ $gettext('Federation') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,12 +32,12 @@
|
|||
<table v-if="result && result.results.length > 0" class="ui unstackable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('ID') }}</th>
|
||||
<th>{{ $t('Launch date') }}</th>
|
||||
<th>{{ $t('Jobs') }}</th>
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $t('Source') }}</th>
|
||||
<th>{{ $t('Submitted by') }}</th>
|
||||
<th>{{ $gettext('ID') }}</th>
|
||||
<th>{{ $gettext('Launch date') }}</th>
|
||||
<th>{{ $gettext('Jobs') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Source') }}</th>
|
||||
<th>{{ $gettext('Submitted by') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -71,7 +71,10 @@
|
|||
></pagination>
|
||||
</th>
|
||||
<th v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((filters.page-1) * filters.paginateBy) + 1 , end: ((filters.page-1) * filters.paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((filters.page-1) * filters.paginateBy) + 1, end: ((filters.page-1) * filters.paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
<th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<div>
|
||||
<div v-if="batch" class="ui container">
|
||||
<div class="ui message">
|
||||
{{ $t('Ensure your music files are properly tagged before uploading them.') }}
|
||||
<a href="http://picard.musicbrainz.org/" target='_blank'>{{ $t('We recommend using Picard for that purpose.') }}</a>
|
||||
{{ $gettext('Ensure your music files are properly tagged before uploading them.') }}
|
||||
<a href="http://picard.musicbrainz.org/" target='_blank'>{{ $gettext('We recommend using Picard for that purpose.') }}</a>
|
||||
</div>
|
||||
<file-upload-widget
|
||||
:class="['ui', 'icon', 'left', 'floated', 'button']"
|
||||
|
@ -20,31 +20,30 @@
|
|||
@input-file="inputFile"
|
||||
ref="upload">
|
||||
<i class="upload icon"></i>
|
||||
<i18next path="Select files to upload..."/>
|
||||
{{ $gettext('Select files to upload...') }}
|
||||
</file-upload-widget>
|
||||
<button
|
||||
:class="['ui', 'right', 'floated', 'icon', {disabled: files.length === 0}, 'button']"
|
||||
v-if="!$refs.upload || !$refs.upload.active" @click.prevent="startUpload()">
|
||||
<i class="play icon" aria-hidden="true"></i>
|
||||
<i18next path="Start Upload"/>
|
||||
{{ $gettext('Start Upload') }}
|
||||
</button>
|
||||
<button type="button" class="ui right floated icon yellow button" v-else @click.prevent="$refs.upload.active = false">
|
||||
<i class="pause icon" aria-hidden="true"></i>
|
||||
<i18next path="Stop Upload"/>
|
||||
{{ $gettext('Stop Upload') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ui hidden clearing divider"></div>
|
||||
<i18next v-if="batch" path="Once all your files are uploaded, simply head over {%0%} to check the import status.">
|
||||
<router-link :to="{name: 'library.import.batches.detail', params: {id: batch.id }}">
|
||||
<i18next path="import detail page"/>
|
||||
</router-link>
|
||||
</i18next>
|
||||
<template v-if="batch">{{ $gettext('Once all your files are uploaded, simply click the following button to check the import status.') }}</template>
|
||||
<router-link class="ui basic button" v-if="batch" :to="{name: 'library.import.batches.detail', params: {id: batch.id }}">
|
||||
{{ $gettext('Import detail page') }}
|
||||
</router-link>
|
||||
<table class="ui single line table">
|
||||
<thead>
|
||||
<tr>
|
||||
<i18next tag="th" path="File name"/>
|
||||
<i18next tag="th" path="Size"/>
|
||||
<i18next tag="th" path="Status"/>
|
||||
<th>{{ $gettext('File name') }}</th>
|
||||
<th>{{ $gettext('Size') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -55,10 +54,10 @@
|
|||
<span v-if="file.error" class="ui red label">
|
||||
{{ file.error }}
|
||||
</span>
|
||||
<i18next v-else-if="file.success" class="ui green label" path="Success"/>
|
||||
<i18next v-else-if="file.active" class="ui yellow label" path="Uploading..."/>
|
||||
<span v-else-if="file.success" class="ui green label">{{ $gettext('Success') }}</span>
|
||||
<span v-else-if="file.active" class="ui yellow label">{{ $gettext('Uploading...') }}</span>
|
||||
<template v-else>
|
||||
<i18next class="ui label" path="Pending"/>
|
||||
<span class="ui label">{{ $gettext('Pending') }}</span>
|
||||
<button class="ui tiny basic red icon button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
|
||||
</template>
|
||||
</td>
|
||||
|
|
|
@ -4,34 +4,44 @@
|
|||
<div class="ui top three attached ordered steps">
|
||||
<a @click="currentStep = 0" :class="['step', {'active': currentStep === 0}, {'completed': currentStep > 0}]">
|
||||
<div class="content">
|
||||
<i18next tag="div" class="title" path="Import source"/>
|
||||
<i18next tag="div" class="description" path="Uploaded files or external source"/>
|
||||
<div class="title">{{ $gettext('Import source') }}</div>
|
||||
<div class="description">{{ $gettext('Uploaded files or external source') }}</div>
|
||||
</div>
|
||||
</a>
|
||||
<a @click="currentStep = 1" :class="['step', {'active': currentStep === 1}, {'completed': currentStep > 1}]">
|
||||
<div class="content">
|
||||
<i18next tag="div" class="title" path="Metadata"/>
|
||||
<i18next tag="div" class="description" path="Grab corresponding metadata"/>
|
||||
<div class="title">{{ $gettext('Metadata') }}</div>
|
||||
<div class="description">{{ $gettext('Grab corresponding metadata') }}</div>
|
||||
</div>
|
||||
</a>
|
||||
<a @click="currentStep = 2" :class="['step', {'active': currentStep === 2}, {'completed': currentStep > 2}]">
|
||||
<div class="content">
|
||||
<i18next tag="div" class="title" path="Music"/>
|
||||
<i18next tag="div" class="description" path="Select relevant sources or files for import"/>
|
||||
<div class="title">{{ $gettext('Music') }}</div>
|
||||
<div class="description">{{ $gettext('Select relevant sources or files for import') }}</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui centered buttons">
|
||||
<button @click="currentStep -= 1" :disabled="currentStep === 0" class="ui icon button"><i class="left arrow icon"></i><i18next path="Previous step"/></button>
|
||||
<button @click="nextStep()" v-if="currentStep < 2" class="ui icon button"><i18next path="Next step"/><i class="right arrow icon"></i></button>
|
||||
<button @click="currentStep -= 1" :disabled="currentStep === 0" class="ui icon button"><i class="left arrow icon"></i>
|
||||
{{ $gettext('Previous step') }}
|
||||
</button>
|
||||
<button @click="nextStep()" v-if="currentStep < 2" class="ui icon button">
|
||||
{{ $gettext('Next step') }}
|
||||
<i class="right arrow icon"></i>
|
||||
</button>
|
||||
<button
|
||||
@click="$refs.import.launchImport()"
|
||||
v-if="currentStep === 2 && currentSource != 'upload'"
|
||||
:class="['ui', 'positive', 'icon', {'loading': isImporting}, 'button']"
|
||||
:disabled="isImporting || importData.count === 0"
|
||||
>
|
||||
<i18next path="Import {%0%} tracks">{{ importData.count }}</i18next>
|
||||
<translate
|
||||
:translate-params="{count: importData.count || 0}"
|
||||
:translate-n="importData.count || 0"
|
||||
translate-plural="Import %{ count } tracks">
|
||||
Import %{ count } track
|
||||
</translate>
|
||||
<i class="check icon"></i>
|
||||
</button>
|
||||
<button
|
||||
|
@ -40,20 +50,20 @@
|
|||
:class="['ui', 'positive', 'icon', {'disabled': !importBatch}, 'button']"
|
||||
:disabled="!importBatch"
|
||||
>
|
||||
{{ $t('Finish import' )}}
|
||||
{{ $gettext('Finish import' )}}
|
||||
<i class="check icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui attached segment">
|
||||
<template v-if="currentStep === 0">
|
||||
<i18next tag="p" path="First, choose where you want to import the music from"/>
|
||||
<p>{{ $gettext('First, choose where you want to import the music from') }}</p>
|
||||
<form class="ui form">
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" id="external" value="external" v-model="currentSource">
|
||||
<label for="external">
|
||||
<i18next path="External source. Supported backends"/>
|
||||
{{ $gettext('External source. Supported backends') }}
|
||||
<div v-for="backend in backends" class="ui basic label">
|
||||
<i v-if="backend.icon" :class="[backend.icon, 'icon']"></i>
|
||||
{{ backend.label }}
|
||||
|
@ -64,7 +74,7 @@
|
|||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" id="upload" value="upload" v-model="currentSource">
|
||||
<i18next tag="label" for="upload" path="File upload" />
|
||||
<label for="upload">{{ $gettext('File upload') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -73,7 +83,7 @@
|
|||
<div class="column">
|
||||
<form class="ui form" @submit.prevent="">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Search an entity you want to import:"/>
|
||||
<label>{{ $gettext('Search an entity you want to import:') }}</label>
|
||||
<metadata-search
|
||||
:mb-type="mbType"
|
||||
:mb-id="mbId"
|
||||
|
@ -81,29 +91,35 @@
|
|||
@type-changed="updateType"></metadata-search>
|
||||
</div>
|
||||
</form>
|
||||
<i18next tag="div" class="ui horizontal divider" path="Or"/>
|
||||
<div class="ui horizontal divider">{{ $gettext('Or') }}</div>
|
||||
<form class="ui form" @submit.prevent="">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Input a MusicBrainz ID manually:"/>
|
||||
<label>{{ $gettext('Input a MusicBrainz ID manually:') }}</label>
|
||||
<input type="text" v-model="currentId" />
|
||||
</div>
|
||||
</form>
|
||||
<div class="ui hidden divider"></div>
|
||||
<template v-if="currentType && currentId">
|
||||
<h4 class="ui header"><i18next path="You will import:"/></h4>
|
||||
<h4 class="ui header">
|
||||
{{ $gettext('You will import:') }}
|
||||
</h4>
|
||||
<component
|
||||
:mbId="currentId"
|
||||
:is="metadataComponent"
|
||||
@metadata-changed="this.updateMetadata"
|
||||
></component>
|
||||
</template>
|
||||
<i18next tag="p" path="You can also skip this step and enter metadata manually."/>
|
||||
<p>{{ $gettext('You can also skip this step and enter metadata manually.') }}</p>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h5 class="ui header">What is metadata?</h5>
|
||||
<i18next tag="p" path="Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the {%0%} project, which you can think about as the Wikipedia of music.">
|
||||
<a href="http://musicbrainz.org/" target="_blank">MusicBrainz</a>
|
||||
</i18next>
|
||||
<template v-translate>
|
||||
Metadata is the data related to the music you want to import. This includes all the information about the artists, albums and tracks. In order to have a high quality library, it is recommended to grab data from the
|
||||
<a href="https://musicbrainz.org" target="_blank">
|
||||
MusicBrainz
|
||||
</a>
|
||||
project, which you can think about as the Wikipedia of music.
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentStep === 2">
|
||||
|
@ -128,8 +144,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="ui vertical stripe segment" v-if="currentRequest">
|
||||
<h3 class="ui header"><i18next path="Music request"/></h3>
|
||||
<i18next tag="p" path="This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled."/>
|
||||
<h3 class="ui header">
|
||||
{{ $gettext('Music request') }}
|
||||
</h3>
|
||||
<p>{{ $gettext('This import will be associated with the music request below. After the import is finished, the request will be marked as fulfilled.') }}</p>
|
||||
<request-card :request="currentRequest" :import-action="false"></request-card>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<template>
|
||||
<div>
|
||||
<h3 class="ui dividing block header">
|
||||
<i18next path="Album {%0%} ({%1%} tracks) by {%2%}">
|
||||
<a :href="getMusicbrainzUrl('release', metadata.id)" target="_blank" title="View on MusicBrainz">{{ metadata.title }}</a>
|
||||
({{ tracks.length}} tracks)
|
||||
<a :href="getMusicbrainzUrl('artist', metadata['artist-credit'][0]['artist']['id'])" target="_blank" title="View on MusicBrainz">{{ metadata['artist-credit-phrase'] }}</a>
|
||||
</i18next>
|
||||
<translate
|
||||
tag="div"
|
||||
translate-plural="Album %{ title } (%{ count } tracks) by %{ artist }"
|
||||
:translate-n="tracks.length"
|
||||
:translate-params="{count: tracks.length, title: metadata.title, artist: metadata['artist-credit-phrase']}">
|
||||
Album %{ title } (%{ count } track) by %{ artist }
|
||||
</translate>
|
||||
<div class="ui divider"></div>
|
||||
<div class="sub header">
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" v-model="enabled" />
|
||||
<i18next tag="label" path="Import this release"/>
|
||||
<label>{{ $gettext('Import this release') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</h3>
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
</h5>
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" v-model="enabled" />
|
||||
<i18next tag="label" path="Import this track"/>
|
||||
<label>{{ $gettext('Import this track') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column" v-if="enabled">
|
||||
<form class="ui mini form" @submit.prevent="">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Source"/>
|
||||
<label>{{ $gettext('Source') }}</label>
|
||||
<select v-model="currentBackendId">
|
||||
<option v-for="backend in backends" :value="backend.id">
|
||||
{{ backend.label }}
|
||||
|
@ -28,10 +28,10 @@
|
|||
<button @click="currentResultIndex -= 1" class="ui basic tiny icon button" :disabled="currentResultIndex === 0">
|
||||
<i class="left arrow icon"></i>
|
||||
</button>
|
||||
<i18next path="Result {%0%}/{%1%}">
|
||||
{{ currentResultIndex + 1 }}
|
||||
{{ results.length }}
|
||||
</i18next>
|
||||
{{ results.total }}
|
||||
<translate :translate-params="{current: currentResultIndex + 1, total: results.length}">
|
||||
Result %{ current }/%{ total }
|
||||
</translate>
|
||||
<button @click="currentResultIndex += 1" class="ui basic tiny icon button" :disabled="currentResultIndex + 1 === results.length">
|
||||
<i class="right arrow icon"></i>
|
||||
</button>
|
||||
|
@ -40,9 +40,9 @@
|
|||
<div class="four wide column" v-if="enabled">
|
||||
<form class="ui mini form" @submit.prevent="">
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Search query"/>
|
||||
<label>{{ $gettext('Search query') }}</label>
|
||||
<input type="text" v-model="query" />
|
||||
<i18next tag="label" path="Imported URL"/>
|
||||
<label>{{ $gettext('Imported URL') }}</label>
|
||||
<input type="text" v-model="importedUrl" />
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -2,30 +2,40 @@
|
|||
<div class="ui vertical stripe segment" v-title="'Radio Builder'">
|
||||
<div>
|
||||
<div>
|
||||
<h2 class="ui header"><i18next path="Builder"/></h2>
|
||||
<i18next tag="p" path="You can use this interface to build your own custom radio, which will play tracks according to your criteria"/>
|
||||
<h2 class="ui header">
|
||||
{{ $gettext('Builder') }}
|
||||
</h2>
|
||||
<p>{{ $gettext('You can use this interface to build your own custom radio, which will play tracks according to your criteria.') }}</p>
|
||||
<div class="ui form">
|
||||
<div class="inline fields">
|
||||
<div class="field">
|
||||
<i18next tag="label" for="name" path="Radio name"/>
|
||||
<label for="name">{{ $gettext('Radio name') }}</label>
|
||||
<input id="name" type="text" v-model="radioName" placeholder="My awesome radio" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<input id="public" type="checkbox" v-model="isPublic" />
|
||||
<i18next tag="label" for="public" path="Display publicly"/>
|
||||
<label for="public">{{ $gettext('Display publicly') }}</label>
|
||||
</div>
|
||||
<button :disabled="!canSave" @click="save" class="ui green button"><i18next path="Save"/></button>
|
||||
<button :disabled="!canSave" @click="save" class="ui green button">
|
||||
{{ $gettext('Save') }}
|
||||
</button>
|
||||
<radio-button v-if="id" type="custom" :custom-radio-id="id"></radio-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui form">
|
||||
<p><i18next path="Add filters to customize your radio"/></p>
|
||||
<p>
|
||||
{{ $gettext('Add filters to customize your radio') }}
|
||||
</p>
|
||||
<div class="inline field">
|
||||
<select class="ui dropdown" v-model="currentFilterType">
|
||||
<option value=""><i18next path="Select a filter"/></option>
|
||||
<option value="">
|
||||
{{ $gettext('Select a filter') }}
|
||||
</option>
|
||||
<option v-for="f in availableFilters" :value="f.type">{{ f.label }}</option>
|
||||
</select>
|
||||
<button :disabled="!currentFilterType" @click="add" class="ui button"><i18next path="Add filter"/></button>
|
||||
<button :disabled="!currentFilterType" @click="add" class="ui button">
|
||||
{{ $gettext('Add filter') }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="currentFilter">
|
||||
{{ currentFilter.help_text }}
|
||||
|
@ -34,11 +44,11 @@
|
|||
<table class="ui table">
|
||||
<thead>
|
||||
<tr>
|
||||
<i18next tag="th" class="two wide" path="Filter name"/>
|
||||
<i18next tag="th" class="one wide" path="Exclude"/>
|
||||
<i18next tag="th" class="six wide" path="Config"/>
|
||||
<i18next tag="th" class="five wide" path="Candidates"/>
|
||||
<i18next tag="th" class="two wide" path="Actions"/>
|
||||
<th class="two wide">{{ $gettext('Filter name') }}</th>
|
||||
<th class="one wide">{{ $gettext('Exclude') }}</th>
|
||||
<th class="six wide">{{ $gettext('Config') }}</th>
|
||||
<th class="five wide">{{ $gettext('Candidates') }}</th>
|
||||
<th class="two wide">{{ $gettext('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -54,9 +64,13 @@
|
|||
</tbody>
|
||||
</table>
|
||||
<template v-if="checkResult">
|
||||
<i18next tag="h3" class="ui header" path="{%0%} tracks matching combined filters">
|
||||
{{ checkResult.candidates.count }}
|
||||
</i18next>
|
||||
<h3
|
||||
class="ui header"
|
||||
v-translate="{count: checkResult.candidates.count}"
|
||||
:translate-n="checkResult.candidates.count"
|
||||
translate-plural="%{ count } tracks matching combined filters">
|
||||
%{ count } track matching combined filters
|
||||
</h3>
|
||||
<track-table v-if="checkResult.candidates.sample" :tracks="checkResult.candidates.sample"></track-table>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</span>
|
||||
<modal v-if="checkResult" :show.sync="showCandidadesModal">
|
||||
<div class="header">
|
||||
<i18next path="Track matching filter"/>
|
||||
{{ $gettext('Track matching filter') }}
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="description">
|
||||
|
@ -51,13 +51,13 @@
|
|||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui black deny button">
|
||||
<i18next path="Cancel"/>
|
||||
{{ $gettext('Cancel') }}
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</td>
|
||||
<td>
|
||||
<button @click="$emit('delete', index)" class="ui basic red button"><i18next path="Remove"/></button>
|
||||
<button @click="$emit('delete', index)" class="ui basic red button">{{ $gettext('Remove') }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="search" placeholder="Search by title, artist, domain..." />
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -15,7 +15,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+">Ascending</option>
|
||||
<option value="-">Descending</option>
|
||||
|
@ -35,14 +35,14 @@
|
|||
:action-url="'manage/library/track-files/action/'"
|
||||
:filters="actionFilters">
|
||||
<template slot="header-cells">
|
||||
<th>{{ $t('Title') }}</th>
|
||||
<th>{{ $t('Artist') }}</th>
|
||||
<th>{{ $t('Album') }}</th>
|
||||
<th>{{ $t('Import date') }}</th>
|
||||
<th>{{ $t('Type') }}</th>
|
||||
<th>{{ $t('Bitrate') }}</th>
|
||||
<th>{{ $t('Duration') }}</th>
|
||||
<th>{{ $t('Size') }}</th>
|
||||
<th>{{ $gettext('Title') }}</th>
|
||||
<th>{{ $gettext('Artist') }}</th>
|
||||
<th>{{ $gettext('Album') }}</th>
|
||||
<th>{{ $gettext('Import date') }}</th>
|
||||
<th>{{ $gettext('Type') }}</th>
|
||||
<th>{{ $gettext('Bitrate') }}</th>
|
||||
<th>{{ $gettext('Duration') }}</th>
|
||||
<th>{{ $gettext('Size') }}</th>
|
||||
</template>
|
||||
<template slot="row-cells" slot-scope="scope">
|
||||
<td>
|
||||
|
@ -61,25 +61,25 @@
|
|||
{{ scope.obj.audio_mimetype }}
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
<td v-if="scope.obj.bitrate">
|
||||
{{ scope.obj.bitrate | humanSize }}/s
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
<td v-if="scope.obj.duration">
|
||||
{{ time.parse(scope.obj.duration) }}
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
<td v-if="scope.obj.size">
|
||||
{{ scope.obj.size | humanSize }}
|
||||
</td>
|
||||
<td v-else>
|
||||
{{ $t('N/A') }}
|
||||
{{ $gettext('N/A') }}
|
||||
</td>
|
||||
</template>
|
||||
</action-table>
|
||||
|
@ -95,7 +95,10 @@
|
|||
></pagination>
|
||||
|
||||
<span v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -178,10 +181,11 @@ export default {
|
|||
}
|
||||
},
|
||||
actions () {
|
||||
let msg = this.$gettext('Delete')
|
||||
return [
|
||||
{
|
||||
name: 'delete',
|
||||
label: this.$t('Delete'),
|
||||
label: msg,
|
||||
isDangerous: true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="search" placeholder="Search by artist, username, comment..." />
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -15,20 +15,20 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+">Ascending</option>
|
||||
<option value="-">Descending</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Status") }}</label>
|
||||
<label>{{ $gettext("Status") }}</label>
|
||||
<select class="ui dropdown" v-model="status">
|
||||
<option :value="null">{{ $t('All') }}</option>
|
||||
<option :value="'pending'">{{ $t('Pending') }}</option>
|
||||
<option :value="'accepted'">{{ $t('Accepted') }}</option>
|
||||
<option :value="'imported'">{{ $t('Imported') }}</option>
|
||||
<option :value="'closed'">{{ $t('Closed') }}</option>
|
||||
<option :value="null">{{ $gettext('All') }}</option>
|
||||
<option :value="'pending'">{{ $gettext('Pending') }}</option>
|
||||
<option :value="'accepted'">{{ $gettext('Accepted') }}</option>
|
||||
<option :value="'imported'">{{ $gettext('Imported') }}</option>
|
||||
<option :value="'closed'">{{ $gettext('Closed') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,48 +45,48 @@
|
|||
:action-url="'manage/requests/import-requests/action/'"
|
||||
:filters="actionFilters">
|
||||
<template slot="header-cells">
|
||||
<th>{{ $t('User') }}</th>
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $t('Artist') }}</th>
|
||||
<th>{{ $t('Albums') }}</th>
|
||||
<th>{{ $t('Comment') }}</th>
|
||||
<th>{{ $t('Creation date') }}</th>
|
||||
<th>{{ $t('Import date') }}</th>
|
||||
<th>{{ $t('Actions') }}</th>
|
||||
<th>{{ $gettext('User') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Artist') }}</th>
|
||||
<th>{{ $gettext('Albums') }}</th>
|
||||
<th>{{ $gettext('Comment') }}</th>
|
||||
<th>{{ $gettext('Creation date') }}</th>
|
||||
<th>{{ $gettext('Import date') }}</th>
|
||||
<th>{{ $gettext('Actions') }}</th>
|
||||
</template>
|
||||
<template slot="row-cells" slot-scope="scope">
|
||||
<td>
|
||||
{{ scope.obj.user.username }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="ui green basic label" v-if="scope.obj.status === 'imported'">{{ $t('Imported') }}</span>
|
||||
<span class="ui pink basic label" v-else-if="scope.obj.status === 'accepted'">{{ $t('Accepted') }}</span>
|
||||
<span class="ui yellow basic label" v-else-if="scope.obj.status === 'pending'">{{ $t('Pending') }}</span>
|
||||
<span class="ui red basic label" v-else-if="scope.obj.status === 'closed'">{{ $t('Closed') }}</span>
|
||||
<span class="ui green basic label" v-if="scope.obj.status === 'imported'">{{ $gettext('Imported') }}</span>
|
||||
<span class="ui pink basic label" v-else-if="scope.obj.status === 'accepted'">{{ $gettext('Accepted') }}</span>
|
||||
<span class="ui yellow basic label" v-else-if="scope.obj.status === 'pending'">{{ $gettext('Pending') }}</span>
|
||||
<span class="ui red basic label" v-else-if="scope.obj.status === 'closed'">{{ $gettext('Closed') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span :title="scope.obj.artist_name">{{ scope.obj.artist_name|truncate(30) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="scope.obj.albums" :title="scope.obj.albums">{{ scope.obj.albums|truncate(30) }}</span>
|
||||
<template v-else>{{ $t('N/A') }}</template>
|
||||
<template v-else>{{ $gettext('N/A') }}</template>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="scope.obj.comment" :title="scope.obj.comment">{{ scope.obj.comment|truncate(30) }}</span>
|
||||
<template v-else>{{ $t('N/A') }}</template>
|
||||
<template v-else>{{ $gettext('N/A') }}</template>
|
||||
</td>
|
||||
<td>
|
||||
<human-date :date="scope.obj.creation_date"></human-date>
|
||||
</td>
|
||||
<td>
|
||||
<human-date v-if="scope.obj.imported_date" :date="scope.obj.creation_date"></human-date>
|
||||
<template v-else>{{ $t('N/A') }}</template>
|
||||
<template v-else>{{ $gettext('N/A') }}</template>
|
||||
</td>
|
||||
<td>
|
||||
<router-link
|
||||
class="ui tiny basic button"
|
||||
:to="{name: 'library.import.launch', query: {request: scope.obj.id}}"
|
||||
v-if="scope.obj.status === 'pending'">{{ $t('Create import') }}</router-link>
|
||||
v-if="scope.obj.status === 'pending'">{{ $gettext('Create import') }}</router-link>
|
||||
</td>
|
||||
</template>
|
||||
</action-table>
|
||||
|
@ -102,7 +102,10 @@
|
|||
></pagination>
|
||||
|
||||
<span v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -183,21 +186,25 @@ export default {
|
|||
}
|
||||
},
|
||||
actions () {
|
||||
// somehow, extraction fails otherwise
|
||||
let deleteLabel = this.$gettext('Delete')
|
||||
let markImportedLabel = this.$gettext('Mark as imported')
|
||||
let markClosedLabel = this.$gettext('Mark as closed')
|
||||
return [
|
||||
{
|
||||
name: 'delete',
|
||||
label: this.$t('Delete'),
|
||||
label: deleteLabel,
|
||||
isDangerous: true
|
||||
},
|
||||
{
|
||||
name: 'mark_imported',
|
||||
label: this.$t('Mark as imported'),
|
||||
label: markImportedLabel,
|
||||
filterCheckable: (obj) => { return ['pending', 'accepted'].indexOf(obj.status) > -1 },
|
||||
isDangerous: true
|
||||
},
|
||||
{
|
||||
name: 'mark_closed',
|
||||
label: this.$t('Mark as closed'),
|
||||
label: markClosedLabel,
|
||||
filterCheckable: (obj) => { return ['pending', 'accepted'].indexOf(obj.status) > -1 },
|
||||
isDangerous: true
|
||||
}
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
<div>
|
||||
<form class="ui form" @submit.prevent="submit">
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while creating invitation') }}</div>
|
||||
<div class="header">{{ $gettext('Error while creating invitation') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="inline fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Invitation code')}}</label>
|
||||
<input type="text" v-model="code" :placeholder="$t('Leave empty for a random code')" />
|
||||
<label>{{ $gettext('Invitation code')}}</label>
|
||||
<input type="text" v-model="code" :placeholder="$gettext('Leave empty for a random code')" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button :class="['ui', {loading: isLoading}, 'button']" :disabled="isLoading" type="submit">
|
||||
{{ $t('Get a new invitation') }}
|
||||
{{ $gettext('Get a new invitation') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,8 +24,8 @@
|
|||
<table class="ui ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('Code') }}</th>
|
||||
<th>{{ $t('Share link') }}</th>
|
||||
<th>{{ $gettext('Code') }}</th>
|
||||
<th>{{ $gettext('Share link') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -35,7 +35,7 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button class="ui basic button" @click="invitations = []">{{ $t('Clear') }}</button>
|
||||
<button class="ui basic button" @click="invitations = []">{{ $gettext('Clear') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="search" placeholder="Search by username, email, code..." />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Ordering") }}</label>
|
||||
<label>{{ $gettext("Ordering") }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -15,11 +15,11 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t("Status") }}</label>
|
||||
<label>{{ $gettext("Status") }}</label>
|
||||
<select class="ui dropdown" v-model="isOpen">
|
||||
<option :value="null">{{ $t('All') }}</option>
|
||||
<option :value="true">{{ $t('Open') }}</option>
|
||||
<option :value="false">{{ $t('Expired/used') }}</option>
|
||||
<option :value="null">{{ $gettext('All') }}</option>
|
||||
<option :value="true">{{ $gettext('Open') }}</option>
|
||||
<option :value="false">{{ $gettext('Expired/used') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,20 +36,20 @@
|
|||
:action-url="'manage/users/invitations/action/'"
|
||||
:filters="actionFilters">
|
||||
<template slot="header-cells">
|
||||
<th>{{ $t('Owner') }}</th>
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $t('Creation date') }}</th>
|
||||
<th>{{ $t('Expiration date') }}</th>
|
||||
<th>{{ $t('Code') }}</th>
|
||||
<th>{{ $gettext('Owner') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
<th>{{ $gettext('Creation date') }}</th>
|
||||
<th>{{ $gettext('Expiration date') }}</th>
|
||||
<th>{{ $gettext('Code') }}</th>
|
||||
</template>
|
||||
<template slot="row-cells" slot-scope="scope">
|
||||
<td>
|
||||
<router-link :to="{name: 'manage.users.users.detail', params: {id: scope.obj.id }}">{{ scope.obj.owner.username }}</router-link>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="scope.obj.users.length > 0" class="ui green basic label">{{ $t('Used') }}</span>
|
||||
<span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label">{{ $t('Expired') }}</span>
|
||||
<span v-else class="ui basic label">{{ $t('Not used') }}</span>
|
||||
<span v-if="scope.obj.users.length > 0" class="ui green basic label">{{ $gettext('Used') }}</span>
|
||||
<span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label">{{ $gettext('Expired') }}</span>
|
||||
<span v-else class="ui basic label">{{ $gettext('Not used') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<human-date :date="scope.obj.creation_date"></human-date>
|
||||
|
@ -74,7 +74,10 @@
|
|||
></pagination>
|
||||
|
||||
<span v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -155,10 +158,11 @@ export default {
|
|||
}
|
||||
},
|
||||
actions () {
|
||||
let deleteLabel = this.$gettext('Delete')
|
||||
return [
|
||||
{
|
||||
name: 'delete',
|
||||
label: this.$t('Delete'),
|
||||
label: deleteLabel,
|
||||
filterCheckable: (obj) => {
|
||||
return obj.users.length === 0 && moment().isBefore(obj.expiration_date)
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<div class="ui inline form">
|
||||
<div class="fields">
|
||||
<div class="ui field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input type="text" v-model="search" placeholder="Search by username, email, name..." />
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering"/>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -15,10 +15,10 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<i18next tag="label" path="Ordering direction"/>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+">{{ $t('Ascending') }}</option>
|
||||
<option value="-">{{ $t('Descending') }}</option>
|
||||
<option value="+">{{ $gettext('Ascending') }}</option>
|
||||
<option value="-">{{ $gettext('Descending') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -35,13 +35,13 @@
|
|||
:action-url="'manage/library/track-files/action/'"
|
||||
:filters="actionFilters">
|
||||
<template slot="header-cells">
|
||||
<th>{{ $t('Username') }}</th>
|
||||
<th>{{ $t('Email') }}</th>
|
||||
<th>{{ $t('Account status') }}</th>
|
||||
<th>{{ $t('Sign-up') }}</th>
|
||||
<th>{{ $t('Last activity') }}</th>
|
||||
<th>{{ $t('Permissions') }}</th>
|
||||
<th>{{ $t('Status') }}</th>
|
||||
<th>{{ $gettext('Username') }}</th>
|
||||
<th>{{ $gettext('Email') }}</th>
|
||||
<th>{{ $gettext('Account status') }}</th>
|
||||
<th>{{ $gettext('Sign-up') }}</th>
|
||||
<th>{{ $gettext('Last activity') }}</th>
|
||||
<th>{{ $gettext('Permissions') }}</th>
|
||||
<th>{{ $gettext('Status') }}</th>
|
||||
</template>
|
||||
<template slot="row-cells" slot-scope="scope">
|
||||
<td>
|
||||
|
@ -51,15 +51,15 @@
|
|||
<span>{{ scope.obj.email }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="scope.obj.is_active" class="ui basic green label">{{ $t('Active') }}</span>
|
||||
<span v-else class="ui basic grey label">{{ $t('Inactive') }}</span>
|
||||
<span v-if="scope.obj.is_active" class="ui basic green label">{{ $gettext('Active') }}</span>
|
||||
<span v-else class="ui basic grey label">{{ $gettext('Inactive') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<human-date :date="scope.obj.date_joined"></human-date>
|
||||
</td>
|
||||
<td>
|
||||
<human-date v-if="scope.obj.last_activity" :date="scope.obj.last_activity"></human-date>
|
||||
<template v-else>{{ $t('N/A') }}</template>
|
||||
<template v-else>{{ $gettext('N/A') }}</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-for="p in permissions">
|
||||
|
@ -67,9 +67,9 @@
|
|||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="scope.obj.is_superuser" class="ui pink label">{{ $t('Admin') }}</span>
|
||||
<span v-else-if="scope.obj.is_staff" class="ui purple label">{{ $t('Staff member') }}</span>
|
||||
<span v-else class="ui basic label">{{ $t('regular user') }}</span>
|
||||
<span v-if="scope.obj.is_superuser" class="ui pink label">{{ $gettext('Admin') }}</span>
|
||||
<span v-else-if="scope.obj.is_staff" class="ui purple label">{{ $gettext('Staff member') }}</span>
|
||||
<span v-else class="ui basic label">{{ $gettext('regular user') }}</span>
|
||||
</td>
|
||||
</template>
|
||||
</action-table>
|
||||
|
@ -85,7 +85,10 @@
|
|||
></pagination>
|
||||
|
||||
<span v-if="result && result.results.length > 0">
|
||||
{{ $t('Showing results {%start%}-{%end%} on {%total%}', {start: ((page-1) * paginateBy) + 1 , end: ((page-1) * paginateBy) + result.results.length, total: result.count})}}
|
||||
<translate
|
||||
:translate-params="{start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}">
|
||||
Showing results %{ start }-%{ end } on %{ total }
|
||||
</translate>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -161,19 +164,19 @@ export default {
|
|||
return [
|
||||
{
|
||||
'code': 'upload',
|
||||
'label': this.$t('Upload')
|
||||
'label': this.$gettext('Upload')
|
||||
},
|
||||
{
|
||||
'code': 'library',
|
||||
'label': this.$t('Library')
|
||||
'label': this.$gettext('Library')
|
||||
},
|
||||
{
|
||||
'code': 'federation',
|
||||
'label': this.$t('Federation')
|
||||
'label': this.$gettext('Federation')
|
||||
},
|
||||
{
|
||||
'code': 'settings',
|
||||
'label': this.$t('Settings')
|
||||
'label': this.$gettext('Settings')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -191,7 +194,7 @@ export default {
|
|||
return [
|
||||
// {
|
||||
// name: 'delete',
|
||||
// label: this.$t('Delete'),
|
||||
// label: this.$gettext('Delete'),
|
||||
// isDangerous: true
|
||||
// }
|
||||
]
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
{{ group['first-release-date'] }}
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<a :href="getMusicbrainzUrl('release-group', group.id)" class="discrete link" target="_blank" :title="$t('View on MusicBrainz')">
|
||||
<a :href="getMusicbrainzUrl('release-group', group.id)" class="discrete link" target="_blank" :title="$gettext('View on MusicBrainz')">
|
||||
{{ group.title }}
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{{ track.position }}
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<a :href="getMusicbrainzUrl('recording', track.id)" class="discrete link" target="_blank" :title="$t('View on MusicBrainz')">
|
||||
<a :href="getMusicbrainzUrl('recording', track.id)" class="discrete link" target="_blank" :title="$gettext('View on MusicBrainz')">
|
||||
{{ track.recording.title }}
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
<div class="ui fluid search">
|
||||
<div class="ui icon input">
|
||||
<input class="prompt" :placeholder="$t('Enter your search query...')" type="text">
|
||||
<input class="prompt" :placeholder="$gettext('Enter your search query...')" type="text">
|
||||
<i class="search icon"></i>
|
||||
</div>
|
||||
<div class="results"></div>
|
||||
|
@ -122,15 +122,15 @@ export default {
|
|||
return [
|
||||
{
|
||||
value: 'artist',
|
||||
label: this.$t('Artist')
|
||||
label: this.$gettext('Artist')
|
||||
},
|
||||
{
|
||||
value: 'release',
|
||||
label: this.$t('Album')
|
||||
label: this.$gettext('Album')
|
||||
},
|
||||
{
|
||||
value: 'recording',
|
||||
label: this.$t('Track')
|
||||
label: this.$gettext('Track')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -11,15 +11,18 @@
|
|||
</div>
|
||||
<div class="meta">
|
||||
<i class="clock icon"></i>
|
||||
<i18next path="Updated {%0%}">
|
||||
<human-date :date="playlist.modification_date" />
|
||||
</i18next>
|
||||
<human-date :date="playlist.modification_date" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<span>
|
||||
<i class="sound icon"></i>
|
||||
{{ $t('{%count%} tracks', { count: playlist.tracks_count }) }}
|
||||
<translate
|
||||
translate-plural="%{ count } tracks"
|
||||
:translate-n="playlist.tracks_count"
|
||||
:translate-params="{count: playlist.tracks_count}">
|
||||
%{ count} track
|
||||
</translate>
|
||||
</span>
|
||||
<play-button class="mini basic orange right floated" :playlist="playlist">Play all</play-button>
|
||||
</div>
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
<div class="ui text container">
|
||||
<playlist-form @updated="$emit('playlist-updated', $event)" :title="false" :playlist="playlist"></playlist-form>
|
||||
<h3 class="ui top attached header">
|
||||
{{ $t('Playlist editor') }}
|
||||
{{ $gettext('Playlist editor') }}
|
||||
</h3>
|
||||
<div class="ui attached segment">
|
||||
<template v-if="status === 'loading'">
|
||||
<div class="ui active tiny inline loader"></div>
|
||||
{{ $t('Syncing changes to server...') }}
|
||||
{{ $gettext('Syncing changes to server...') }}
|
||||
</template>
|
||||
<template v-else-if="status === 'errored'">
|
||||
<i class="red close icon"></i>
|
||||
{{ $t('An error occured while saving your changes') }}
|
||||
{{ $gettext('An error occured while saving your changes') }}
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<template v-else-if="status === 'saved'">
|
||||
<i class="green check icon"></i> {{ $t('Changes synced with server') }}
|
||||
<i class="green check icon"></i> {{ $gettext('Changes synced with server') }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="ui bottom attached segment">
|
||||
|
@ -29,14 +29,21 @@
|
|||
:class="['ui', {disabled: queueTracks.length === 0}, 'labeled', 'icon', 'button']"
|
||||
title="Copy tracks from current queue to playlist">
|
||||
<i class="plus icon"></i>
|
||||
{{ $t('Insert from queue ({%count%} tracks)', { count: queueTracks.length }) }}
|
||||
<translate
|
||||
translate-plural="Insert from queue (%{ count } tracks)"
|
||||
:translate-n="queueTracks.length"
|
||||
:translate-params="{count: queueTracks.length}">
|
||||
Insert from queue (%{ count } track)
|
||||
</translate>
|
||||
</div>
|
||||
|
||||
<dangerous-button :disabled="plts.length === 0" class="labeled right floated icon" color='yellow' :action="clearPlaylist">
|
||||
<i class="eraser icon"></i> {{ $t('Clear playlist') }}
|
||||
<p slot="modal-header">{{ $t('Do you want to clear the playlist "{%name%}"?', { name: playlist.name }) }}</p>
|
||||
<p slot="modal-content">{{ $t('This will remove all tracks from this playlist and cannot be undone.') }}</p>
|
||||
<p slot="modal-confirm">{{ $t('Clear playlist') }}</p>
|
||||
<i class="eraser icon"></i> {{ $gettext('Clear playlist') }}
|
||||
<p slot="modal-header">
|
||||
<translate :translate-params="{playlist: playlist.name}">Do you want to clear the playlist "%{ playlist }"?</translate>
|
||||
</p>
|
||||
<p slot="modal-content">{{ $gettext('This will remove all tracks from this playlist and cannot be undone.') }}</p>
|
||||
<p slot="modal-confirm">{{ $gettext('Clear playlist') }}</p>
|
||||
</dangerous-button>
|
||||
<div class="ui hidden divider"></div>
|
||||
<template v-if="plts.length > 0">
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
<template>
|
||||
<form class="ui form" @submit.prevent="submit()">
|
||||
<h4 v-if="title" class="ui header">{{ $t('Create a new playlist') }}</h4>
|
||||
<h4 v-if="title" class="ui header">{{ $gettext('Create a new playlist') }}</h4>
|
||||
<div v-if="success" class="ui positive message">
|
||||
<div class="header">
|
||||
<template v-if="playlist">
|
||||
{{ $t('Playlist updated') }}
|
||||
{{ $gettext('Playlist updated') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('Playlist created') }}
|
||||
{{ $gettext('Playlist created') }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('We cannot create the playlist') }}</div>
|
||||
<div class="header">{{ $gettext('We cannot create the playlist') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>{{ $t('Playlist name') }}</label>
|
||||
<label>{{ $gettext('Playlist name') }}</label>
|
||||
<input v-model="name" required type="text" placeholder="My awesome playlist" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Playlist visibility') }}</label>
|
||||
<label>{{ $gettext('Playlist visibility') }}</label>
|
||||
<select class="ui dropdown" v-model="privacyLevel">
|
||||
<option :value="c.value" v-for="c in privacyLevelChoices">{{ c.label }}</option>
|
||||
</select>
|
||||
|
@ -31,8 +31,8 @@
|
|||
<div class="field">
|
||||
<label> </label>
|
||||
<button :class="['ui', 'fluid', {'loading': isLoading}, 'button']" type="submit">
|
||||
<template v-if="playlist">{{ $t('Update playlist') }}</template>
|
||||
<template v-else>{{ $t('Create playlist') }}</template>
|
||||
<template v-if="playlist">{{ $gettext('Update playlist') }}</template>
|
||||
<template v-else>{{ $gettext('Create playlist') }}</template>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,15 +73,15 @@ export default {
|
|||
return [
|
||||
{
|
||||
value: 'me',
|
||||
label: this.$t('Nobody except me')
|
||||
label: this.$gettext('Nobody except me')
|
||||
},
|
||||
{
|
||||
value: 'instance',
|
||||
label: this.$t('Everyone on this instance')
|
||||
label: this.$gettext('Everyone on this instance')
|
||||
},
|
||||
{
|
||||
value: 'everyone',
|
||||
label: this.$t('Everyone')
|
||||
label: this.$gettext('Everyone')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,33 +1,37 @@
|
|||
<template>
|
||||
<modal @update:show="update" :show="$store.state.playlists.showModal">
|
||||
<div class="header">
|
||||
{{ $t('Manage playlists') }}
|
||||
{{ $gettext('Manage playlists') }}
|
||||
</div>
|
||||
<div class="scrolling content">
|
||||
<div class="description">
|
||||
<template v-if="track">
|
||||
<h4 class="ui header">{{ $t('Current track') }}</h4>
|
||||
<div v-html='trackDisplay'></div>
|
||||
<h4 class="ui header">{{ $gettext('Current track') }}</h4>
|
||||
<div
|
||||
v-translate="{artist: track.artist.name, title: track.title}"
|
||||
:template-params="{artist: track.artist.name, title: track.title}">
|
||||
"%{ title }", by %{ artist }
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
</template>
|
||||
|
||||
<playlist-form></playlist-form>
|
||||
<div class="ui divider"></div>
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('We cannot add the track to a playlist') }}</div>
|
||||
<div class="header">{{ $gettext('We cannot add the track to a playlist') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="ui header">{{ $t('Available playlists') }}</h4>
|
||||
<h4 class="ui header">{{ $gettext('Available playlists') }}</h4>
|
||||
<table class="ui unstackable very basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{{ $t('Name') }}</th>
|
||||
<th class="sorted descending">{{ $t('Last modification') }}</th>
|
||||
<th>{{ $t('Tracks') }}</th>
|
||||
<th>{{ $gettext('Name') }}</th>
|
||||
<th class="sorted descending">{{ $gettext('Last modification') }}</th>
|
||||
<th>{{ $gettext('Tracks') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -46,9 +50,9 @@
|
|||
<div
|
||||
v-if="track"
|
||||
class="ui green icon basic small right floated button"
|
||||
:title="$t('Add to this playlist')"
|
||||
:title="$gettext('Add to this playlist')"
|
||||
@click="addToPlaylist(playlist.id)">
|
||||
<i class="plus icon"></i> {{ $t('Add track') }}
|
||||
<i class="plus icon"></i> {{ $gettext('Add track') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -57,7 +61,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui cancel button">{{ $t('Cancel') }}</div>
|
||||
<div class="ui cancel button">{{ $gettext('Cancel') }}</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
@ -110,12 +114,6 @@ export default {
|
|||
let p = _.sortBy(this.playlists, [(e) => { return e.modification_date }])
|
||||
p.reverse()
|
||||
return p
|
||||
},
|
||||
trackDisplay () {
|
||||
return this.$t('"{%title%}" by {%artist%}', {
|
||||
title: this.track.title,
|
||||
artist: this.track.artist.name }
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
v-if="button"
|
||||
:class="['ui', 'button']">
|
||||
<i class="list icon"></i>
|
||||
{{ $t('Add to playlist...') }}
|
||||
{{ $gettext('Add to playlist...') }}
|
||||
</button>
|
||||
<i
|
||||
v-else
|
||||
@click="$store.commit('playlists/chooseTrack', track)"
|
||||
:class="['playlist-icon', 'list', 'link', 'icon']"
|
||||
:title="$t('Add to playlist...')">
|
||||
:title="$gettext('Add to playlist...')">
|
||||
</i>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<button @click="toggleRadio" :class="['ui', 'blue', {'inverted': running}, 'button']">
|
||||
<i class="ui feed icon"></i>
|
||||
<template v-if="running">{{ $t('Stop') }}</template>
|
||||
<template v-else>{{ $t('Start') }}</template>
|
||||
<template v-if="running">{{ $gettext('Stop') }}</template>
|
||||
<template v-else>{{ $gettext('Start') }}</template>
|
||||
radio
|
||||
</button>
|
||||
</template>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
class="ui basic yellow button"
|
||||
v-if="$store.state.auth.authenticated && type === 'custom' && customRadio.user === $store.state.auth.profile.id"
|
||||
:to="{name: 'library.radios.edit', params: {id: customRadioId }}">
|
||||
{{ $t('Edit...') }}
|
||||
{{ $gettext('Edit...') }}
|
||||
</router-link>
|
||||
<radio-button class="right floated button" :type="type" :custom-radio-id="customRadioId"></radio-button>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<button
|
||||
@click="createImport"
|
||||
v-if="request.status === 'pending' && importAction && $store.state.auth.availablePermissions['library']"
|
||||
class="ui mini basic green right floated button">{{ $t('Create import') }}</button>
|
||||
class="ui mini basic green right floated button">{{ $gettext('Create import') }}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
<template>
|
||||
<div>
|
||||
<form v-if="!over" class="ui form" @submit.prevent="submit">
|
||||
<p>{{ $t('Something\'s missing in the library? Let us know what you would like to listen!') }}</p>
|
||||
<p>{{ $gettext('Something\'s missing in the library? Let us know what you would like to listen!') }}</p>
|
||||
<div class="required field">
|
||||
<label>{{ $t('Artist name') }}</label>
|
||||
<label>{{ $gettext('Artist name') }}</label>
|
||||
<input v-model="currentArtistName" placeholder="The Beatles, Mickael Jackson…" required maxlength="200">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Albums') }}</label>
|
||||
<p>{{ $t('Leave this field empty if you\'re requesting the whole discography.') }}</p>
|
||||
<label>{{ $gettext('Albums') }}</label>
|
||||
<p>{{ $gettext('Leave this field empty if you\'re requesting the whole discography.') }}</p>
|
||||
<input v-model="currentAlbums" placeholder="The White Album, Thriller…" maxlength="2000">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Comment') }}</label>
|
||||
<label>{{ $gettext('Comment') }}</label>
|
||||
<textarea v-model="currentComment" rows="3" placeholder="Use this comment box to add details to your request if needed" maxlength="2000"></textarea>
|
||||
</div>
|
||||
<button class="ui submit button" type="submit">{{ $t('Submit') }}</button>
|
||||
<button class="ui submit button" type="submit">{{ $gettext('Submit') }}</button>
|
||||
</form>
|
||||
<div v-else class="ui success message">
|
||||
<div class="header">Request submitted!</div>
|
||||
<p>{{ $t('We\'ve received your request, you\'ll get some groove soon ;)') }}</p>
|
||||
<button @click="reset" class="ui button">{{ $t('Submit another request') }}</button>
|
||||
<p>{{ $gettext('We\'ve received your request, you\'ll get some groove soon ;)') }}</p>
|
||||
<button @click="reset" class="ui button">{{ $gettext('Submit another request') }}</button>
|
||||
</div>
|
||||
<div v-if="requests.length > 0">
|
||||
<div class="ui divider"></div>
|
||||
<h3 class="ui header">{{ $t('Pending requests') }}</h3>
|
||||
<h3 class="ui header">{{ $gettext('Pending requests') }}</h3>
|
||||
<div class="ui list">
|
||||
<div v-for="request in requests" class="item">
|
||||
<div class="content">
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/* eslint-disable */
|
||||
export default {
|
||||
"locales": [
|
||||
{
|
||||
"code": "en_US",
|
||||
"label": "English (United-States)"
|
||||
},
|
||||
{
|
||||
"code": "fr_FR",
|
||||
"label": "Français"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -11,11 +11,12 @@ import router from './router'
|
|||
import axios from 'axios'
|
||||
import {VueMasonryPlugin} from 'vue-masonry'
|
||||
import VueLazyload from 'vue-lazyload'
|
||||
import i18next from 'i18next'
|
||||
import i18nextFetch from 'i18next-fetch-backend'
|
||||
import VueI18Next from '@panter/vue-i18next'
|
||||
import store from './store'
|
||||
import GetTextPlugin from 'vue-gettext'
|
||||
import { sync } from 'vuex-router-sync'
|
||||
import translations from './translations.json'
|
||||
import locales from '@/locales'
|
||||
|
||||
import filters from '@/filters' // eslint-disable-line
|
||||
import globals from '@/components/globals' // eslint-disable-line
|
||||
|
||||
|
@ -28,8 +29,31 @@ window.$ = window.jQuery = require('jquery')
|
|||
// require('./semantic/semantic.css')
|
||||
require('semantic-ui-css/semantic.js')
|
||||
require('masonry-layout')
|
||||
let availableLanguages = (function () {
|
||||
let l = {}
|
||||
locales.locales.forEach(c => {
|
||||
l[c.code] = c.label
|
||||
})
|
||||
return l
|
||||
})()
|
||||
let defaultLanguage = 'en_US'
|
||||
if (availableLanguages[store.state.ui.currentLanguage]) {
|
||||
defaultLanguage = store.state.ui.currentLanguage
|
||||
}
|
||||
Vue.use(GetTextPlugin, {
|
||||
availableLanguages: availableLanguages,
|
||||
defaultLanguage: defaultLanguage,
|
||||
languageVmMixin: {
|
||||
computed: {
|
||||
currentKebabCase: function () {
|
||||
return this.current.toLowerCase().replace('_', '-')
|
||||
}
|
||||
}
|
||||
},
|
||||
translations: translations,
|
||||
silent: true
|
||||
})
|
||||
|
||||
Vue.use(VueI18Next)
|
||||
Vue.use(VueMasonryPlugin)
|
||||
Vue.use(VueLazyload)
|
||||
Vue.config.productionTip = false
|
||||
|
@ -96,35 +120,17 @@ axios.interceptors.response.use(function (response) {
|
|||
}
|
||||
}
|
||||
if (error.backendErrors.length === 0) {
|
||||
error.backendErrors.push(i18next.t('An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running'))
|
||||
error.backendErrors.push('An unknown error occured, ensure your are connected to the internet and your funkwhale instance is up and running')
|
||||
}
|
||||
// Do something with response error
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
// i18n
|
||||
i18next
|
||||
.use(i18nextFetch)
|
||||
.init({
|
||||
lng: navigator.language,
|
||||
fallbackLng: ['en'],
|
||||
preload: [navigator.language, 'en'],
|
||||
backend: {
|
||||
loadPath: '/static/translations/{%lng%}.json'
|
||||
},
|
||||
interpolation: {
|
||||
prefix: '{%',
|
||||
suffix: '%}'
|
||||
}
|
||||
})
|
||||
const i18n = new VueI18Next(i18next)
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
})
|
||||
|
|
|
@ -36,6 +36,10 @@ export default new Vuex.Store({
|
|||
key: 'instance',
|
||||
paths: ['instance.events', 'instance.instanceUrl']
|
||||
}),
|
||||
createPersistedState({
|
||||
key: 'ui',
|
||||
paths: ['ui.currentLanguage']
|
||||
}),
|
||||
createPersistedState({
|
||||
key: 'radios',
|
||||
paths: ['radios'],
|
||||
|
|
|
@ -3,6 +3,7 @@ import axios from 'axios'
|
|||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
currentLanguage: 'en_US',
|
||||
lastDate: new Date(),
|
||||
maxMessages: 100,
|
||||
messageDisplayDuration: 10000,
|
||||
|
@ -13,6 +14,9 @@ export default {
|
|||
}
|
||||
},
|
||||
mutations: {
|
||||
currentLanguage: (state, value) => {
|
||||
state.currentLanguage = value
|
||||
},
|
||||
computeLastDate: (state) => {
|
||||
state.lastDate = new Date()
|
||||
},
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="$t('Instance settings')">
|
||||
<div class="main pusher" v-title="$gettext('Instance settings')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui text container">
|
||||
<div :class="['ui', {'loading': isLoading}, 'form']"></div>
|
||||
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
<div class="four wide column">
|
||||
<div class="ui sticky vertical secondary menu">
|
||||
<div class="header item">{{ $t('Sections') }}</div>
|
||||
<div class="header item">{{ $gettext('Sections') }}</div>
|
||||
<a :class="['menu', {active: group.id === current}, 'item']"
|
||||
@click.prevent="scrollTo(group.id)"
|
||||
:href="'#' + group.id"
|
||||
|
@ -71,9 +71,18 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
groups () {
|
||||
// somehow, extraction fails if in the return block directly
|
||||
let instanceLabel = this.$gettext('Instance information')
|
||||
let usersLabel = this.$gettext('Users')
|
||||
let importsLabel = this.$gettext('Imports')
|
||||
let playlistsLabel = this.$gettext('Playlists')
|
||||
let federationLabel = this.$gettext('Federation')
|
||||
let subsonicLabel = this.$gettext('Subsonic')
|
||||
let statisticsLabel = this.$gettext('Statistics')
|
||||
let errorLabel = this.$gettext('Error reporting')
|
||||
return [
|
||||
{
|
||||
label: this.$t('Instance information'),
|
||||
label: instanceLabel,
|
||||
id: 'instance',
|
||||
settings: [
|
||||
'instance__name',
|
||||
|
@ -82,7 +91,7 @@ export default {
|
|||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Users'),
|
||||
label: usersLabel,
|
||||
id: 'users',
|
||||
settings: [
|
||||
'users__registration_enabled',
|
||||
|
@ -91,21 +100,21 @@ export default {
|
|||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Imports'),
|
||||
label: importsLabel,
|
||||
id: 'imports',
|
||||
settings: [
|
||||
'providers_youtube__api_key'
|
||||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Playlists'),
|
||||
label: playlistsLabel,
|
||||
id: 'playlists',
|
||||
settings: [
|
||||
'playlists__max_tracks'
|
||||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Federation'),
|
||||
label: federationLabel,
|
||||
id: 'federation',
|
||||
settings: [
|
||||
'federation__enabled',
|
||||
|
@ -116,14 +125,14 @@ export default {
|
|||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Subsonic'),
|
||||
label: subsonicLabel,
|
||||
id: 'subsonic',
|
||||
settings: [
|
||||
'subsonic__enabled'
|
||||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Statistics'),
|
||||
label: statisticsLabel,
|
||||
id: 'statistics',
|
||||
settings: [
|
||||
'instance__nodeinfo_enabled',
|
||||
|
@ -132,7 +141,7 @@ export default {
|
|||
]
|
||||
},
|
||||
{
|
||||
label: this.$t('Error reporting'),
|
||||
label: errorLabel,
|
||||
id: 'reporting',
|
||||
settings: [
|
||||
'raven__front_enabled',
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<div class="ui secondary pointing menu">
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'manage.library.files'}">{{ $t('Files') }}</router-link>
|
||||
:to="{name: 'manage.library.files'}">{{ $gettext('Files') }}</router-link>
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'manage.library.requests'}">
|
||||
{{ $t('Import requests') }}
|
||||
{{ $gettext('Import requests') }}
|
||||
<div
|
||||
:class="['ui', {'teal': $store.state.ui.notifications.importRequests > 0}, 'label']"
|
||||
:title="$t('Pending import requests')">
|
||||
:title="$gettext('Pending import requests')">
|
||||
{{ $store.state.ui.notifications.importRequests }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-title="'Files'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Library files') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Library files') }}</h2>
|
||||
<div class="ui hidden divider"></div>
|
||||
<library-files-table :show-library="true"></library-files-table>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-title="$t('Import requests')">
|
||||
<div v-title="$gettext('Import requests')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Import requests') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Import requests') }}</h2>
|
||||
<div class="ui hidden divider"></div>
|
||||
<library-requests-table></library-requests-table>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="$t('Manage users')">
|
||||
<div class="main pusher" v-title="$gettext('Manage users')">
|
||||
<div class="ui secondary pointing menu">
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'manage.users.users.list'}">{{ $t('Users') }}</router-link>
|
||||
:to="{name: 'manage.users.users.list'}">{{ $gettext('Users') }}</router-link>
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'manage.users.invitations.list'}">{{ $t('Invitations') }}</router-link>
|
||||
:to="{name: 'manage.users.invitations.list'}">{{ $gettext('Invitations') }}</router-link>
|
||||
</div>
|
||||
<router-view :key="$route.fullPath"></router-view>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-title="$t('Invitations')">
|
||||
<div v-title="$gettext('Invitations')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Invitations') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Invitations') }}</h2>
|
||||
<invitation-form></invitation-form>
|
||||
<div class="ui hidden divider"></div>
|
||||
<invitations-table></invitations-table>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Name') }}
|
||||
{{ $gettext('Name') }}
|
||||
</td>
|
||||
<td>
|
||||
{{ object.name }}
|
||||
|
@ -27,7 +27,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Email address') }}
|
||||
{{ $gettext('Email address') }}
|
||||
</td>
|
||||
<td>
|
||||
{{ object.email }}
|
||||
|
@ -35,7 +35,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Sign-up') }}
|
||||
{{ $gettext('Sign-up') }}
|
||||
</td>
|
||||
<td>
|
||||
<human-date :date="object.date_joined"></human-date>
|
||||
|
@ -43,17 +43,17 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Last activity') }}
|
||||
{{ $gettext('Last activity') }}
|
||||
</td>
|
||||
<td>
|
||||
<human-date v-if="object.last_activity" :date="object.last_activity"></human-date>
|
||||
<template v-else>{{ $t('N/A') }}</template>
|
||||
<template v-else>{{ $gettext('N/A') }}</template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Account active') }}
|
||||
<span :data-tooltip="$t('Determine if the user account is active or not. Inactive users cannot login or user the service.')"><i class="question circle icon"></i></span>
|
||||
{{ $gettext('Account active') }}
|
||||
<span :data-tooltip="$gettext('Determine if the user account is active or not. Inactive users cannot login or user the service.')"><i class="question circle icon"></i></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
|
@ -66,7 +66,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Permissions') }}
|
||||
{{ $gettext('Permissions') }}
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
|
@ -82,7 +82,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<button @click="fetchData" class="ui basic button">{{ $t('Refresh') }}</button>
|
||||
<button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -145,19 +145,19 @@ export default {
|
|||
return [
|
||||
{
|
||||
'code': 'upload',
|
||||
'label': this.$t('Upload')
|
||||
'label': this.$gettext('Upload')
|
||||
},
|
||||
{
|
||||
'code': 'library',
|
||||
'label': this.$t('Library')
|
||||
'label': this.$gettext('Library')
|
||||
},
|
||||
{
|
||||
'code': 'federation',
|
||||
'label': this.$t('Federation')
|
||||
'label': this.$gettext('Federation')
|
||||
},
|
||||
{
|
||||
'code': 'settings',
|
||||
'label': this.$t('Settings')
|
||||
'label': this.$gettext('Settings')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-title="$t('Users')">
|
||||
<div v-title="$gettext('Users')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Users') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Users') }}</h2>
|
||||
<div class="ui hidden divider"></div>
|
||||
<users-table></users-table>
|
||||
</div>
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="$t('Confirm your email')">
|
||||
<div class="main pusher" v-title="$gettext('Confirm your email')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2>{{ $t('Confirm your email') }}</h2>
|
||||
<h2>{{ $gettext('Confirm your email') }}</h2>
|
||||
<form v-if="!success" class="ui form" @submit.prevent="submit()">
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while confirming your email') }}</div>
|
||||
<div class="header">{{ $gettext('Error while confirming your email') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Confirmation code') }}</label>
|
||||
<label>{{ $gettext('Confirmation code') }}</label>
|
||||
<input type="text" required v-model="key" />
|
||||
</div>
|
||||
<router-link :to="{path: '/login'}">
|
||||
{{ $t('Back to login') }}
|
||||
{{ $gettext('Back to login') }}
|
||||
</router-link>
|
||||
<button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
|
||||
{{ $t('Confirm your email') }}</button>
|
||||
{{ $gettext('Confirm your email') }}</button>
|
||||
</form>
|
||||
<div v-else class="ui positive message">
|
||||
<div class="header">{{ $t('Email confirmed') }}</div>
|
||||
<p>{{ $t('Your email address was confirmed, you can now use the service without limitations.') }}</p>
|
||||
<div class="header">{{ $gettext('Email confirmed') }}</div>
|
||||
<p>{{ $gettext('Your email address was confirmed, you can now use the service without limitations.') }}</p>
|
||||
<router-link :to="{name: 'login'}">
|
||||
{{ $t('Proceed to login') }}
|
||||
{{ $gettext('Proceed to login') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="$t('Reset your password')">
|
||||
<div class="main pusher" v-title="$gettext('Reset your password')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2>{{ $t('Reset your password') }}</h2>
|
||||
<h2>{{ $gettext('Reset your password') }}</h2>
|
||||
<form class="ui form" @submit.prevent="submit()">
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while asking for a password reset') }}</div>
|
||||
<div class="header">{{ $gettext('Error while asking for a password reset') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>{{ $t('Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.') }}</p>
|
||||
<p>{{ $gettext('Use this form to request a password reset. We will send an email to the given address with instructions to reset your password.') }}</p>
|
||||
<div class="field">
|
||||
<label>{{ $t('Account\'s email') }}</label>
|
||||
<label>{{ $gettext('Account\'s email') }}</label>
|
||||
<input
|
||||
required
|
||||
ref="email"
|
||||
type="email"
|
||||
autofocus
|
||||
:placeholder="$t('Input the email address binded to your account')"
|
||||
:placeholder="$gettext('Input the email address binded to your account')"
|
||||
v-model="email">
|
||||
</div>
|
||||
<router-link :to="{path: '/login'}">
|
||||
{{ $t('Back to login') }}
|
||||
{{ $gettext('Back to login') }}
|
||||
</router-link>
|
||||
<button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
|
||||
{{ $t('Ask for a password reset') }}</button>
|
||||
{{ $gettext('Ask for a password reset') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
<template>
|
||||
<div class="main pusher" v-title="$t('Change your password')">
|
||||
<div class="main pusher" v-title="$gettext('Change your password')">
|
||||
<div class="ui vertical stripe segment">
|
||||
<div class="ui small text container">
|
||||
<h2>{{ $t('Change your password') }}</h2>
|
||||
<h2>{{ $gettext('Change your password') }}</h2>
|
||||
<form v-if="!success" class="ui form" @submit.prevent="submit()">
|
||||
<div v-if="errors.length > 0" class="ui negative message">
|
||||
<div class="header">{{ $t('Error while changing your password') }}</div>
|
||||
<div class="header">{{ $gettext('Error while changing your password') }}</div>
|
||||
<ul class="list">
|
||||
<li v-for="error in errors">{{ error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<template v-if="token && uid">
|
||||
<div class="field">
|
||||
<label>{{ $t('New password') }}</label>
|
||||
<label>{{ $gettext('New password') }}</label>
|
||||
<password-input v-model="newPassword" />
|
||||
</div>
|
||||
<router-link :to="{path: '/login'}">
|
||||
{{ $t('Back to login') }}
|
||||
{{ $gettext('Back to login') }}
|
||||
</router-link>
|
||||
<button :class="['ui', {'loading': isLoading}, 'right', 'floated', 'green', 'button']" type="submit">
|
||||
{{ $t('Update your password') }}</button>
|
||||
{{ $gettext('Update your password') }}</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>{{ $t('If the email address provided in the previous step is valid and binded to a user account, you should receive an email with reset instructions in the next couple of minutes.') }}</p>
|
||||
<p>{{ $gettext('If the email address provided in the previous step is valid and binded to a user account, you should receive an email with reset instructions in the next couple of minutes.') }}</p>
|
||||
</template>
|
||||
</form>
|
||||
<div v-else class="ui positive message">
|
||||
<div class="header">{{ $t('Password updated successfully') }}</div>
|
||||
<p>{{ $t('Your password has been updated successfully.') }}</p>
|
||||
<div class="header">{{ $gettext('Password updated successfully') }}</div>
|
||||
<p>{{ $gettext('Your password has been updated successfully.') }}</p>
|
||||
<router-link :to="{name: 'login'}">
|
||||
{{ $t('Proceed to login') }}
|
||||
{{ $gettext('Proceed to login') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
<div class="ui secondary pointing menu">
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'federation.libraries.list'}">{{ $t('Libraries') }}</router-link>
|
||||
:to="{name: 'federation.libraries.list'}">{{ $gettext('Libraries') }}</router-link>
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'federation.tracks.list'}">{{ $t('Tracks') }}</router-link>
|
||||
:to="{name: 'federation.tracks.list'}">{{ $gettext('Tracks') }}</router-link>
|
||||
<div class="ui secondary right menu">
|
||||
<router-link
|
||||
class="ui item"
|
||||
:to="{name: 'federation.followers.list'}">
|
||||
{{ $t('Followers') }}
|
||||
<div class="ui teal label" :title="$t('Pending requests')">{{ requestsCount }}</div>
|
||||
{{ $gettext('Followers') }}
|
||||
<div class="ui teal label" :title="$gettext('Pending requests')">{{ requestsCount }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -19,18 +19,18 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td >
|
||||
{{ $t('Follow status') }}
|
||||
<span :data-tooltip="$t('This indicate if the remote library granted you access')"><i class="question circle icon"></i></span>
|
||||
{{ $gettext('Follow status') }}
|
||||
<span :data-tooltip="$gettext('This indicate if the remote library granted you access')"><i class="question circle icon"></i></span>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="object.follow.approved === null">
|
||||
<i class="loading icon"></i> {{ $t('Pending approval') }}
|
||||
<i class="loading icon"></i> {{ $gettext('Pending approval') }}
|
||||
</template>
|
||||
<template v-else-if="object.follow.approved === true">
|
||||
<i class="check icon"></i> {{ $t('Following') }}
|
||||
<i class="check icon"></i> {{ $gettext('Following') }}
|
||||
</template>
|
||||
<template v-else-if="object.follow.approved === false">
|
||||
<i class="x icon"></i> {{ $t('Not following') }}
|
||||
<i class="x icon"></i> {{ $gettext('Not following') }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -38,8 +38,8 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Federation') }}
|
||||
<span :data-tooltip="$t('Use this flag to enable/disable federation with this library')"><i class="question circle icon"></i></span>
|
||||
{{ $gettext('Federation') }}
|
||||
<span :data-tooltip="$gettext('Use this flag to enable/disable federation with this library')"><i class="question circle icon"></i></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
|
@ -54,8 +54,8 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{{ $t('Auto importing') }}
|
||||
<span :data-tooltip="$t('When enabled, auto importing will automatically import new tracks published in this library')"><i class="question circle icon"></i></span>
|
||||
{{ $gettext('Auto importing') }}
|
||||
<span :data-tooltip="$gettext('When enabled, auto importing will automatically import new tracks published in this library')"><i class="question circle icon"></i></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="ui toggle checkbox">
|
||||
|
@ -82,19 +82,24 @@
|
|||
</tr>
|
||||
-->
|
||||
<tr>
|
||||
<td>{{ $t('Library size') }}</td>
|
||||
<td>{{ $gettext('Library size') }}</td>
|
||||
<td>
|
||||
<template v-if="object.tracks_count">
|
||||
{{ $t('{%count%} tracks', { count: object.tracks_count }) }}
|
||||
<translate
|
||||
translate-plural="%{ count } tracks"
|
||||
:translate-n="object.tracks_count"
|
||||
:translate-params="{count: object.tracks_count}">
|
||||
%{ count } track
|
||||
</translate>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('Unkwnown') }}
|
||||
{{ $gettext('Unkwnown') }}
|
||||
</template>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('Last fetched') }}</td>
|
||||
<td>{{ $gettext('Last fetched') }}</td>
|
||||
<td>
|
||||
<human-date v-if="object.fetched_date" :date="object.fetched_date"></human-date>
|
||||
<template v-else>Never</template>
|
||||
|
@ -102,10 +107,10 @@
|
|||
@click="scan"
|
||||
v-if="!scanTrigerred"
|
||||
:class="['ui', 'basic', {loading: isScanLoading}, 'button']">
|
||||
<i class="sync icon"></i> {{ $t('Trigger scan') }}
|
||||
<i class="sync icon"></i> {{ $gettext('Trigger scan') }}
|
||||
</button>
|
||||
<button v-else class="ui success button">
|
||||
<i class="check icon"></i> {{ $t('Scan triggered!') }}
|
||||
<i class="check icon"></i> {{ $gettext('Scan triggered!') }}
|
||||
</button>
|
||||
|
||||
</td>
|
||||
|
@ -115,10 +120,10 @@
|
|||
</table>
|
||||
</div>
|
||||
<div class="ui hidden divider"></div>
|
||||
<button @click="fetchData" class="ui basic button">{{ $t('Refresh') }}</button>
|
||||
<button @click="fetchData" class="ui basic button">{{ $gettext('Refresh') }}</button>
|
||||
</div>
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2>{{ $t('Tracks available in this library') }}</h2>
|
||||
<h2>{{ $gettext('Tracks available in this library') }}</h2>
|
||||
<library-track-table v-if="!isLoading" :filters="{library: id}"></library-track-table>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div v-title="'Followers'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Browsing followers') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Browsing followers') }}</h2>
|
||||
<p>
|
||||
{{ $t('Be careful when accepting follow requests, as it means the follower will have access to your entire library.') }}
|
||||
{{ $gettext('Be careful when accepting follow requests, as it means the follower will have access to your entire library.') }}
|
||||
</p>
|
||||
<div class="ui hidden divider"></div>
|
||||
<library-follow-table></library-follow-table>
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<template>
|
||||
<div v-title="'Libraries'">
|
||||
<div class="ui vertical stripe segment">
|
||||
<h2 class="ui header">{{ $t('Browsing libraries') }}</h2>
|
||||
<h2 class="ui header">{{ $gettext('Browsing libraries') }}</h2>
|
||||
<router-link
|
||||
class="ui basic green button"
|
||||
:to="{name: 'federation.libraries.scan'}">
|
||||
<i class="plus icon"></i>
|
||||
{{ $t('Add a new library') }}
|
||||
{{ $gettext('Add a new library') }}
|
||||
</router-link>
|
||||
<div class="ui hidden divider"></div>
|
||||
<div :class="['ui', {'loading': isLoading}, 'form']">
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<label>{{ $t('Search') }}</label>
|
||||
<label>{{ $gettext('Search') }}</label>
|
||||
<input class="search" type="text" v-model="query" placeholder="Enter an library domain name..."/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Ordering') }}</label>
|
||||
<label>{{ $gettext('Ordering') }}</label>
|
||||
<select class="ui dropdown" v-model="ordering">
|
||||
<option v-for="option in orderingOptions" :value="option[0]">
|
||||
{{ option[1] }}
|
||||
|
@ -24,14 +24,14 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Ordering direction') }}</label>
|
||||
<label>{{ $gettext('Ordering direction') }}</label>
|
||||
<select class="ui dropdown" v-model="orderingDirection">
|
||||
<option value="+">{{ $t('Ascending') }}</option>
|
||||
<option value="-">{{ $t('Descending') }}</option>
|
||||
<option value="+">{{ $gettext('Ascending') }}</option>
|
||||
<option value="-">{{ $gettext('Descending') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ $t('Results per page') }}</label>
|
||||
<label>{{ $gettext('Results per page') }}</label>
|
||||
<select class="ui dropdown" v-model="paginateBy">
|
||||
<option :value="parseInt(12)">12</option>
|
||||
<option :value="parseInt(25)">25</option>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue