mirror of
https://github.com/CzBiX/qb-web.git
synced 2026-04-25 11:20:55 +08:00
Add I18n
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
class="headline grey lighten-4"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-link-plus</v-icon>
|
||||
<span>Add Torrents</span>
|
||||
<span>{{ $t('title.add_torrents') }}</span>
|
||||
</v-card-title>
|
||||
<v-card-text class="pb-0">
|
||||
<v-form
|
||||
@@ -42,16 +42,16 @@
|
||||
multiple
|
||||
chips
|
||||
outlined
|
||||
label="Files"
|
||||
:label="$t('files')"
|
||||
/>
|
||||
<v-textarea
|
||||
v-show="!files.length"
|
||||
label="URLs"
|
||||
hint="One link per line"
|
||||
:placeholder="placeholder"
|
||||
label="URL"
|
||||
:hint="$t('dialog.add_torrents.hint')"
|
||||
:placeholder="$t('dialog.add_torrents.placeholder')"
|
||||
prepend-icon="mdi-link"
|
||||
append-outer-icon="mdi-attachment"
|
||||
:rules="[v => (!!files.length || !!v || 'URLs is required')]"
|
||||
:rules="[v => (!!files.length || !!v || $t('msg.item_is_required', { item: 'URL' }))]"
|
||||
:rows="$vuetify.breakpoint.xsOnly ? 1 : 3"
|
||||
required
|
||||
:autofocus="!phoneLayout"
|
||||
@@ -65,7 +65,7 @@
|
||||
<template v-if="showMore">
|
||||
<v-col cols="12" sm="6">
|
||||
<v-combobox
|
||||
label="Category"
|
||||
:label="$t('category', 1)"
|
||||
prepend-icon="mdi-folder"
|
||||
clearable
|
||||
hide-no-data
|
||||
@@ -77,7 +77,7 @@
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-file-tree"
|
||||
label="Create subfolder"
|
||||
:label="$t('label.create_subfolder')"
|
||||
:input-value="true"
|
||||
@change="setParams('root_path', $event)"
|
||||
/>
|
||||
@@ -86,14 +86,14 @@
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox
|
||||
v-model="autoStart"
|
||||
label="Start torrent"
|
||||
:label="$t('label.start_torrent')"
|
||||
prepend-icon="mdi-play-pause"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-progress-check"
|
||||
label="Skip hash check"
|
||||
:label="$t('label.skip_hash_check')"
|
||||
:input-value="params.skip_checking"
|
||||
@change="setParams('skip_checking', $event)"
|
||||
/>
|
||||
@@ -101,7 +101,7 @@
|
||||
<template v-if="showMore">
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox
|
||||
label="In sequential order"
|
||||
:label="$t('label.in_sequential_order')"
|
||||
prepend-icon="mdi-sort-descending"
|
||||
:ipnut-value="params.sequentialDownload"
|
||||
@change="setParams('sequentialDownload', $event.value)"
|
||||
@@ -110,7 +110,7 @@
|
||||
<v-col cols="12" sm="6">
|
||||
<v-checkbox
|
||||
prepend-icon="mdi-ray-start-end"
|
||||
label="First and last pieces first"
|
||||
:label="$t('label.first_and_last_pieces_first')"
|
||||
:input-value="params.firstLastPiecePrio"
|
||||
@change="setParams('firstLastPiecePrio', $event)"
|
||||
/>
|
||||
@@ -126,9 +126,9 @@
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn text @click="showMore = !showMore" v-text="showMore ? 'Less' : 'More'" />
|
||||
<v-btn text @click="showMore = !showMore" v-text="showMore ? $t('less') : $t('more')" />
|
||||
<v-spacer />
|
||||
<v-btn text @click="dialog = false">Cancel</v-btn>
|
||||
<v-btn text @click="dialog = false">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
@click="submit"
|
||||
@@ -136,7 +136,7 @@
|
||||
:disabled="!valid"
|
||||
:loading="submitting"
|
||||
>
|
||||
Submit
|
||||
{{ $t('submit') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -148,6 +148,8 @@
|
||||
import _ from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import api from '../Api';
|
||||
|
||||
const defaultParams = {
|
||||
@@ -160,10 +162,21 @@ const defaultParams = {
|
||||
firstLastPiecePrio: false,
|
||||
};
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
interface Data {
|
||||
tr: any,
|
||||
dialog: boolean,
|
||||
valid: boolean,
|
||||
files: any[],
|
||||
userParams: any,
|
||||
error: string | null,
|
||||
submitting: boolean,
|
||||
showMore: boolean,
|
||||
}
|
||||
|
||||
export default {
|
||||
data(): Data {
|
||||
return {
|
||||
placeholder: 'Upload torrents by drop them here,\nor click attachment button at right to select.',
|
||||
tr,
|
||||
dialog: false,
|
||||
valid: false,
|
||||
files: [],
|
||||
@@ -180,7 +193,7 @@ export default Vue.extend({
|
||||
}),
|
||||
...mapState({
|
||||
categories(state, getters) {
|
||||
return getters.allCategories.map(c => ({ text: c.name, value: c.key }));
|
||||
return getters.allCategories.map((c: any) => ({ text: c.name, value: c.key }));
|
||||
},
|
||||
}),
|
||||
params() {
|
||||
@@ -289,7 +302,7 @@ export default Vue.extend({
|
||||
this.$refs.form.validate();
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -5,23 +5,9 @@
|
||||
class="drawer"
|
||||
>
|
||||
<template v-for="item in items">
|
||||
<v-row
|
||||
v-if="item.heading"
|
||||
:key="item.heading"
|
||||
align="center"
|
||||
>
|
||||
<v-col cols="6">
|
||||
<v-subheader v-if="item.heading">
|
||||
{{ item.heading }}
|
||||
</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="6" class="text-center">
|
||||
<a href="#!" class="body-2 black--text">EDIT</a>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-list-group
|
||||
v-else-if="item.children"
|
||||
:key="item.text"
|
||||
v-if="item.children"
|
||||
:key="item.title"
|
||||
v-model="item.model"
|
||||
:prepend-icon="item.model ? item.icon : item['icon-alt']"
|
||||
append-icon=""
|
||||
@@ -29,7 +15,7 @@
|
||||
<template v-slot:activator>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
{{ item.text }}
|
||||
{{ item.title }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</template>
|
||||
@@ -43,7 +29,7 @@
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
{{ child.text }}
|
||||
{{ child.title }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
@@ -55,13 +41,13 @@
|
||||
:group="child"
|
||||
/>
|
||||
</template>
|
||||
<v-list-item v-else :key="item.text" @click="item.click ? item.click() : null">
|
||||
<v-list-item v-else :key="item.title" @click="item.click ? item.click() : null">
|
||||
<v-list-item-icon>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
{{ item.text }}
|
||||
{{ item.title }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
@@ -73,6 +59,9 @@
|
||||
import _ from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mapState, mapMutations, mapGetters } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { Torrent } from '@/types';
|
||||
import FilterGroup from './drawer/FilterGroup.vue';
|
||||
import api from '../Api';
|
||||
import { formatSize } from '../filters';
|
||||
@@ -80,47 +69,68 @@ import { SiteMap, StateType, AllStateTypes } from '../consts';
|
||||
|
||||
const stateList = [
|
||||
{
|
||||
title: 'Downloading',
|
||||
title: tr('state.downloading'),
|
||||
state: StateType.Downloading,
|
||||
icon: 'download',
|
||||
},
|
||||
{
|
||||
title: 'Seeding',
|
||||
title: tr('state.seeding'),
|
||||
state: StateType.Seeding,
|
||||
icon: 'upload',
|
||||
},
|
||||
{
|
||||
title: 'Completed',
|
||||
title: tr('state.completed'),
|
||||
state: StateType.Completed,
|
||||
icon: 'check',
|
||||
},
|
||||
{
|
||||
title: 'Resumed',
|
||||
title: tr('state.resumed'),
|
||||
state: StateType.Resumed,
|
||||
icon: 'play',
|
||||
},
|
||||
{
|
||||
title: 'Paused',
|
||||
title: tr('state.paused'),
|
||||
state: StateType.Paused,
|
||||
icon: 'pause',
|
||||
},
|
||||
{
|
||||
title: 'Active',
|
||||
title: tr('state.active'),
|
||||
state: StateType.Active,
|
||||
icon: 'filter',
|
||||
},
|
||||
{
|
||||
title: 'Inactive',
|
||||
title: tr('state.inactive'),
|
||||
state: StateType.Inactive,
|
||||
icon: 'filter-outline',
|
||||
},
|
||||
{
|
||||
title: 'Errored',
|
||||
title: tr('state.errored'),
|
||||
state: StateType.Errored,
|
||||
icon: 'alert',
|
||||
},
|
||||
];
|
||||
|
||||
interface MenuItem {
|
||||
icon: string,
|
||||
'icon-alt'?: string,
|
||||
title: string,
|
||||
model?: boolean,
|
||||
select?: string,
|
||||
click?: () => void,
|
||||
children?: MenuChildrenItem[],
|
||||
}
|
||||
|
||||
interface MenuChildrenItem extends MenuItem {
|
||||
key: string | null,
|
||||
append?: string,
|
||||
}
|
||||
|
||||
interface Data {
|
||||
tr: any,
|
||||
basicItems: MenuItem[],
|
||||
endItems: MenuItem[],
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterGroup,
|
||||
@@ -130,21 +140,20 @@ export default {
|
||||
value: Object,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
basicItems: null,
|
||||
endItems: null,
|
||||
};
|
||||
},
|
||||
data(): Data {
|
||||
const basicItems = [
|
||||
{ icon: 'mdi-settings', title: tr('settings'), click: () => alert(tr('todo')) },
|
||||
];
|
||||
const endItems = [
|
||||
{ icon: 'mdi-delta', title: tr('logs'), click: () => this.updateOptions('showLogs', true) },
|
||||
{ icon: 'mdi-history', title: tr('label.switch_to_old_ui'), click: this.switchUi },
|
||||
];
|
||||
|
||||
created() {
|
||||
this.basicItems = [
|
||||
{ icon: 'mdi-settings', text: 'Settings', click: () => alert('TODO') },
|
||||
];
|
||||
this.endItems = [
|
||||
{ icon: 'mdi-delta', text: 'Logs', click: () => this.updateOptions('showLogs', true) },
|
||||
{ icon: 'mdi-history', text: 'Switch to old UI', click: this.switchUi },
|
||||
];
|
||||
return {
|
||||
tr,
|
||||
basicItems,
|
||||
endItems,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
@@ -161,7 +170,7 @@ export default {
|
||||
return _.concat(this.basicItems, this.endItems);
|
||||
}
|
||||
|
||||
const filterGroups = [];
|
||||
const filterGroups: MenuItem[] = [];
|
||||
const totalSize = formatSize(_.sumBy(this.allTorrents, 'size'));
|
||||
|
||||
const states = stateList.map((item) => {
|
||||
@@ -179,12 +188,12 @@ export default {
|
||||
filterGroups.push({
|
||||
icon: 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
title: 'State',
|
||||
title: tr('state._'),
|
||||
model: false,
|
||||
select: 'state',
|
||||
children: [
|
||||
{
|
||||
icon: 'mdi-filter-remove', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
icon: 'mdi-filter-remove', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
},
|
||||
...states,
|
||||
],
|
||||
@@ -192,7 +201,7 @@ export default {
|
||||
|
||||
const categories: any[] = [{
|
||||
key: '',
|
||||
name: 'Uncategorized',
|
||||
name: tr('uncategorized'),
|
||||
}].concat(this.allCategories).map((category) => {
|
||||
let value = this.torrentGroupByCategory[category.key];
|
||||
if (_.isUndefined(value)) {
|
||||
@@ -208,12 +217,12 @@ export default {
|
||||
filterGroups.push({
|
||||
icon: 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
title: 'Categories',
|
||||
title: tr('category', 0),
|
||||
model: !this.$vuetify.breakpoint.xsOnly,
|
||||
select: 'category',
|
||||
children: [
|
||||
{
|
||||
icon: 'mdi-folder-open', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
icon: 'mdi-folder-open', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
},
|
||||
...categories,
|
||||
],
|
||||
@@ -222,7 +231,7 @@ export default {
|
||||
const sites: any[] = _.sortBy(Object.entries(this.torrentGroupBySite).map(([key, value]) => {
|
||||
const size = formatSize(_.sumBy(value, 'size'));
|
||||
const site = (SiteMap as any)[key];
|
||||
const title = `${site ? site.name : (key || 'Others')} (${value.length})`;
|
||||
const title = `${site ? site.name : (key || tr('others'))} (${value.length})`;
|
||||
const icon = _.defaultTo(site ? site.icon : null, 'mdi-server');
|
||||
const append = `[${size}]`;
|
||||
return {
|
||||
@@ -232,18 +241,18 @@ export default {
|
||||
filterGroups.push({
|
||||
icon: 'mdi-menu-up',
|
||||
'icon-alt': 'mdi-menu-down',
|
||||
title: 'Sites',
|
||||
title: tr('sites'),
|
||||
model: false,
|
||||
select: 'site',
|
||||
children: [
|
||||
{
|
||||
icon: 'mdi-server', title: `All (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
icon: 'mdi-server', title: `${tr('all')} (${this.allTorrents.length})`, key: null, append: `[${totalSize}]`,
|
||||
},
|
||||
...sites,
|
||||
],
|
||||
});
|
||||
|
||||
return _.concat(this.basicItems, [{ filterGroups }], this.endItems);
|
||||
return _.concat(this.basicItems, [{filterGroups}] as any, this.endItems);
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -27,21 +27,23 @@ import Vue from 'vue';
|
||||
import {
|
||||
computed, ref, watch, Ref,
|
||||
} from '@vue/composition-api';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { DialogType, DialogConfig } from '@/store/types';
|
||||
import { useMutations, useState } from '@/store';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
const BUTTONS = {
|
||||
[DialogType.Alert]: [
|
||||
['Close', false],
|
||||
[tr('close'), false],
|
||||
],
|
||||
[DialogType.YesNo]: [
|
||||
['No', false],
|
||||
['Yes', true],
|
||||
[tr('no'), false],
|
||||
[tr('yes'), true],
|
||||
],
|
||||
[DialogType.OkCancel]: [
|
||||
['Cancel', false],
|
||||
['OK', true],
|
||||
[tr('cancel'), false],
|
||||
[tr('ok'), true],
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -11,13 +11,15 @@
|
||||
color="info"
|
||||
@click="clickBtn"
|
||||
>
|
||||
{{ config.btnText ? config.btnText : 'Close' }}
|
||||
{{ config.btnText ? config.btnText : $t('close') }}
|
||||
</v-btn>
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useMutations, useState } from '@/store';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import { timeout } from '@/utils';
|
||||
|
||||
export default {
|
||||
@@ -45,6 +47,7 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
tr,
|
||||
config,
|
||||
changed,
|
||||
clickBtn,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-dialog v-model="value" persistent width="25em">
|
||||
<v-card>
|
||||
<v-toolbar dark color="primary">
|
||||
<v-toolbar-title>Login</v-toolbar-title>
|
||||
<v-toolbar-title>{{ $t('login') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
@@ -15,8 +15,8 @@
|
||||
<v-text-field
|
||||
v-model="params.username"
|
||||
prepend-icon="mdi-account"
|
||||
label="Username"
|
||||
:rules="[v => !!v || 'Username is required']"
|
||||
:label="$t('username')"
|
||||
:rules="[v => !!v || $t('msg.item_is_required', { item: $t('username') })]"
|
||||
autofocus
|
||||
required
|
||||
/>
|
||||
@@ -25,9 +25,9 @@
|
||||
prepend-icon="mdi-lock"
|
||||
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
||||
@click:append="showPassword = !showPassword"
|
||||
label="Password"
|
||||
:label="$t('password')"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
:rules="[v => !!v || 'Password is required']"
|
||||
:rules="[v => !!v || $t('msg.item_is_required', { item: $t('password') })]"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
:disabled="!valid || submitting"
|
||||
:loading="submitting"
|
||||
>
|
||||
Submit
|
||||
{{ $t('submit') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -55,6 +55,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import api from '../Api';
|
||||
|
||||
export default Vue.extend({
|
||||
@@ -63,6 +65,7 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tr,
|
||||
valid: false,
|
||||
submitting: false,
|
||||
showPassword: false,
|
||||
|
||||
@@ -10,31 +10,31 @@
|
||||
height="40px"
|
||||
class="elevation-2"
|
||||
>
|
||||
<v-btn icon @click="confirmDelete" title="Delete" :disabled="!hasSelected">
|
||||
<v-btn icon @click="confirmDelete" :title="$t('delete')" :disabled="!hasSelected">
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-divider vertical inset />
|
||||
<v-btn icon @click="resumeTorrents" title="Resume" :disabled="!hasSelected">
|
||||
<v-btn icon @click="resumeTorrents" :title="$t('resume')" :disabled="!hasSelected">
|
||||
<v-icon>mdi-play</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="pauseTorrents" title="Pause" :disabled="!hasSelected">
|
||||
<v-btn icon @click="pauseTorrents" :title="$t('pause')" :disabled="!hasSelected">
|
||||
<v-icon>mdi-pause</v-icon>
|
||||
</v-btn>
|
||||
<v-divider vertical inset />
|
||||
<v-btn icon @click="showInfo()" title="Info"
|
||||
<v-btn icon @click="showInfo()" :title="$t('info')"
|
||||
:disabled="!hasSelected || selectedRows.length > 5"
|
||||
>
|
||||
<v-icon>mdi-alert-circle</v-icon>
|
||||
</v-btn>
|
||||
<v-menu offset-y>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn icon v-on="on" title="Category" :disabled="!hasSelected">
|
||||
<v-btn icon v-on="on" :title="$t('category')" :disabled="!hasSelected">
|
||||
<v-icon>mdi-folder</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list class="category-actions">
|
||||
<v-subheader @click.stop="">
|
||||
Set category
|
||||
{{ $t('title.set_category') }}
|
||||
</v-subheader>
|
||||
<v-list-item
|
||||
v-for="(item, i) in allCategories"
|
||||
@@ -57,7 +57,7 @@
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
Reset
|
||||
{{ $t('reset') }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
@@ -65,13 +65,13 @@
|
||||
</v-menu>
|
||||
<template v-if="!$vuetify.breakpoint.xsOnly">
|
||||
<v-divider vertical inset />
|
||||
<v-btn icon @click="reannounceTorrents" title="Reannounce">
|
||||
<v-btn icon @click="reannounceTorrents" :title="$t('reannounce')">
|
||||
<v-icon>mdi-bullhorn</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="editTracker" title="Edit tracker">
|
||||
<v-btn icon @click="editTracker" :title="$t('title.edit_tracker')">
|
||||
<v-icon>mdi-server</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="recheckTorrents" title="Recheck" :disabled="selectedRows.length == 0">
|
||||
<v-btn icon @click="recheckTorrents" :title="$t('recheck')" :disabled="selectedRows.length == 0">
|
||||
<v-icon>mdi-backup-restore</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
@@ -159,6 +159,8 @@
|
||||
import Vue from 'vue';
|
||||
import { mapState, mapGetters, mapMutations } from 'vuex';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import ConfirmDeleteDialog from './dialogs/ConfirmDeleteDialog.vue';
|
||||
import ConfirmSetCategoryDialog from './dialogs/ConfirmSetCategoryDialog.vue';
|
||||
import EditTrackerDialog from './dialogs/EditTrackerDialog.vue';
|
||||
@@ -256,17 +258,17 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
const headers = [
|
||||
{ text: 'Name', value: 'name' },
|
||||
{ text: 'Size', value: 'size' },
|
||||
{ text: 'Progress', value: 'progress' },
|
||||
{ text: 'Status', value: 'state' },
|
||||
{ text: 'Seeds', value: 'num_complete' },
|
||||
{ text: 'Peers', value: 'num_incomplete' },
|
||||
{ text: 'DL Speed', value: 'dlspeed' },
|
||||
{ text: 'UP Speed', value: 'upspeed' },
|
||||
{ text: 'ETA', value: 'eta' },
|
||||
{ text: 'Ratio', value: 'ratio' },
|
||||
{ text: 'Added', value: 'added_on' },
|
||||
{ text: tr('name'), value: 'name' },
|
||||
{ text: tr('size'), value: 'size' },
|
||||
{ text: tr('progress'), value: 'progress' },
|
||||
{ text: tr('status'), value: 'state' },
|
||||
{ text: tr('seeds'), value: 'num_complete' },
|
||||
{ text: tr('peers'), value: 'num_incomplete' },
|
||||
{ text: tr('dl_speed'), value: 'dlspeed' },
|
||||
{ text: tr('up_speed'), value: 'upspeed' },
|
||||
{ text: tr('eta'), value: 'eta' },
|
||||
{ text: tr('ratio'), value: 'ratio' },
|
||||
{ text: tr('added_on'), value: 'added_on' },
|
||||
];
|
||||
|
||||
const footerProps = {
|
||||
@@ -274,6 +276,7 @@ export default Vue.extend({
|
||||
};
|
||||
|
||||
return {
|
||||
tr,
|
||||
headers,
|
||||
selectedRows: [],
|
||||
toDelete: [],
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
class="headline grey lighten-4"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-delete</v-icon>
|
||||
<span>Delete torrents</span>
|
||||
<span>{{ $t('title.delete_torrents') }}</span>
|
||||
</v-card-title>
|
||||
<v-card-text class="pb-0">
|
||||
Are you sure to delete selected torrents from transfer list?
|
||||
{{ $t('dialog.delete_torrents.msg') }}
|
||||
<ol class="torrents pt-6">
|
||||
<li v-for="(row, i) in torrents" :key="i">
|
||||
{{ row.name }}
|
||||
@@ -18,26 +18,26 @@
|
||||
<v-checkbox
|
||||
v-model="deleteFiles"
|
||||
prepend-icon="mdi-file-cancel"
|
||||
label="Also delete files"
|
||||
:label="$t('label.also_delete_files')"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-if="sameNamedTorrents.length > 0"
|
||||
v-model="deleteSameNamed"
|
||||
prepend-icon="mdi-file-multiple"
|
||||
class="mt-0"
|
||||
:label="`Also delete ${sameNamedTorrents.length} same named torrents`"
|
||||
:label="$t('dialog.delete_torrents', sameNamedTorrents.length)"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">Cancel</v-btn>
|
||||
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
@click="submit"
|
||||
color="warning"
|
||||
:disabled="submitting"
|
||||
:loading="submitting"
|
||||
>
|
||||
Delete
|
||||
{{ $t('delete') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -48,6 +48,8 @@
|
||||
import _ from 'lodash';
|
||||
import Vue, { PropType } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import api from '@/Api';
|
||||
import { findSameNamedTorrents } from '@/utils';
|
||||
import { Torrent } from '../../types';
|
||||
@@ -58,6 +60,7 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tr,
|
||||
deleteFiles: false,
|
||||
deleteSameNamed: false,
|
||||
submitting: false,
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
class="headline grey lighten-4"
|
||||
>
|
||||
<v-icon class="mr-2">mdi-folder</v-icon>
|
||||
<span>Set category</span>
|
||||
<span>{{ $t('title.set_category') }}</span>
|
||||
</v-card-title>
|
||||
<v-card-text class="pb-0">
|
||||
<template v-if="category">
|
||||
Are you sure to move selected torrents to category {{ category }}?
|
||||
{{ $t('dialog.set_category.move', { category }) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
Are you sure to reset category of selected torrents?
|
||||
{{ $t('dialog.set_category.reset') }}
|
||||
</template>
|
||||
<ol class="torrents pt-6">
|
||||
<li v-for="(row, i) in torrents" :key="i">
|
||||
@@ -25,19 +25,19 @@
|
||||
v-model="moveSameNamed"
|
||||
prepend-icon="mdi-file-multiple"
|
||||
class="mt-0"
|
||||
:label="`Also move ${sameNamedTorrents.length} same named torrents`"
|
||||
:label="$t('dialog.set_category.also.move_same_name_torrents', sameNamedTorrents.length)"
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn text @click="closeDialog">Cancel</v-btn>
|
||||
<v-btn text @click="closeDialog">{{ $t('cancel') }}</v-btn>
|
||||
<v-btn
|
||||
@click="submit"
|
||||
color="warning"
|
||||
:disabled="submitting"
|
||||
:loading="submitting"
|
||||
>
|
||||
Submit
|
||||
{{ $t('submit') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -48,6 +48,8 @@
|
||||
import _ from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { tr } from '@/locale';
|
||||
import api from '@/Api';
|
||||
import { findSameNamedTorrents } from '@/utils';
|
||||
|
||||
|
||||
100
src/locale/en.ts
Normal file
100
src/locale/en.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
export default {
|
||||
lang: 'English',
|
||||
|
||||
close: 'Close',
|
||||
no: 'No',
|
||||
yes: 'Yes',
|
||||
cancel: 'Cancel',
|
||||
ok: 'OK',
|
||||
|
||||
submit: 'Submit',
|
||||
edit: 'Edit',
|
||||
delete: 'Delete',
|
||||
todo: 'To Do',
|
||||
resume: 'Resume',
|
||||
pause: 'Pause',
|
||||
info: 'Info',
|
||||
reset: 'Reset',
|
||||
login: 'Login',
|
||||
|
||||
reannounce: 'Reannounce',
|
||||
recheck: 'Recheck',
|
||||
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
|
||||
name: 'Name',
|
||||
size: 'Size',
|
||||
progress: 'progress',
|
||||
status: 'Status',
|
||||
seeds: 'Seeds',
|
||||
peers: 'Peers',
|
||||
dl_speed: 'DL Speed',
|
||||
up_speed: 'UP Speed',
|
||||
eta: 'ETA',
|
||||
ratio: 'Ratio',
|
||||
added_on: 'Added On',
|
||||
|
||||
settings: 'Settings',
|
||||
logs: 'Logs',
|
||||
|
||||
all: 'All',
|
||||
category: 'Category |||| Categories',
|
||||
uncategorized: 'Uncategorized',
|
||||
others: 'Others',
|
||||
sites: 'Sites',
|
||||
files: 'Files',
|
||||
less: 'Less',
|
||||
more: 'More',
|
||||
|
||||
title: {
|
||||
add_torrents: 'Add Torrents',
|
||||
delete_torrents: 'Delete Torrents',
|
||||
set_category: 'Set category',
|
||||
edit_tracker: 'Edit tracker',
|
||||
},
|
||||
|
||||
label: {
|
||||
switch_to_old_ui: 'Switch to old UI',
|
||||
create_subfolder: 'Create subfolder',
|
||||
start_torrent: 'Start torrent',
|
||||
skip_hash_check: 'Skip hash check',
|
||||
in_sequential_order: 'In sequential order',
|
||||
first_and_last_pieces_first: 'First and last pieces first',
|
||||
|
||||
also_delete_files: 'Also delete files',
|
||||
},
|
||||
|
||||
msg: {
|
||||
item_is_required: '%{item} is required',
|
||||
},
|
||||
|
||||
dialog: {
|
||||
add_torrents: {
|
||||
placeholder: 'Upload torrents by drop them here,\nor click attachment button at right to select.',
|
||||
hint: 'One link per line',
|
||||
},
|
||||
delete_torrents: {
|
||||
msg: 'Are you sure to delete selected torrents from transfer list?',
|
||||
also_delete_same_name_torrents: 'Also delete one same named torrent |||| Also delete %{smart_count} same named torrents',
|
||||
},
|
||||
set_category: {
|
||||
move: 'Are you sure to move selected torrents to category %{category}?',
|
||||
reset: 'Are you sure to reset category of selected torrents?',
|
||||
also_move_same_name_torrents: 'Also move one same named torrent |||| Also move %{smart_count} same named torrents',
|
||||
}
|
||||
},
|
||||
|
||||
state: {
|
||||
_: 'State',
|
||||
|
||||
downloading: 'Downloading',
|
||||
seeding: 'Seeding',
|
||||
completed: 'Completed',
|
||||
resumed: 'Resumed',
|
||||
paused: 'Paused',
|
||||
active: 'Active',
|
||||
inactive: 'Inactive',
|
||||
errored: 'Errored',
|
||||
}
|
||||
}
|
||||
39
src/locale/index.ts
Normal file
39
src/locale/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { isString } from 'lodash';
|
||||
import Polyglot from 'node-polyglot';
|
||||
import en from './en';
|
||||
|
||||
const polyglot = new Polyglot({
|
||||
phrases: en,
|
||||
});
|
||||
|
||||
const locales: any = {
|
||||
en: 'English',
|
||||
'zh-CN': '中文',
|
||||
};
|
||||
|
||||
function updateLocale() {
|
||||
const { languages } = navigator;
|
||||
|
||||
let locale;
|
||||
for (const code of languages) {
|
||||
if (code in locales) {
|
||||
locale = code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!locale || locale === 'en') {
|
||||
return;
|
||||
}
|
||||
|
||||
polyglot.locale(locale);
|
||||
|
||||
const { default: translation } = require('./' + locale);
|
||||
polyglot.extend(translation);
|
||||
}
|
||||
|
||||
updateLocale();
|
||||
|
||||
export default polyglot;
|
||||
export const tr = polyglot.t.bind(polyglot);
|
||||
export { updateLocale };
|
||||
100
src/locale/zh-CN.ts
Normal file
100
src/locale/zh-CN.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
export default {
|
||||
lang: '中文',
|
||||
|
||||
close: '关闭',
|
||||
no: '否',
|
||||
yes: '是',
|
||||
cancel: '取消',
|
||||
ok: '确定',
|
||||
|
||||
submit: '提交',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
todo: '待办',
|
||||
resume: '恢复',
|
||||
pause: '暂停',
|
||||
info: '信息',
|
||||
reset: '重置',
|
||||
login: '登录',
|
||||
|
||||
reannounce: '重新通告',
|
||||
recheck: '重新检查',
|
||||
|
||||
username: '用户名',
|
||||
password: '密码',
|
||||
|
||||
name: '名称',
|
||||
size: '大小',
|
||||
progress: '进度',
|
||||
status: '状态',
|
||||
seeds: '做种',
|
||||
peers: '用户',
|
||||
dl_speed: '下载速度',
|
||||
up_speed: '上传速度',
|
||||
eta: '剩余时间',
|
||||
ratio: '比率',
|
||||
added_on: '添加时间',
|
||||
|
||||
settings: '设置',
|
||||
logs: '日志',
|
||||
|
||||
all: '全部',
|
||||
category: '分类',
|
||||
uncategorized: '未分类',
|
||||
others: '其他',
|
||||
sites: '站点',
|
||||
files: '文件',
|
||||
less: '更少',
|
||||
more: '更多',
|
||||
|
||||
title: {
|
||||
add_torrents: '添加种子',
|
||||
delete_torrents: '删除种子',
|
||||
set_category: '设置分类',
|
||||
edit_tracker: '编辑 Tracker',
|
||||
},
|
||||
|
||||
label: {
|
||||
switch_to_old_ui: '切换到原版 UI',
|
||||
create_subfolder: '创建子文件夹',
|
||||
start_torrent: '开始种子',
|
||||
skip_hash_check: '跳过哈希校验',
|
||||
in_sequential_order: '按顺序下载',
|
||||
first_and_last_pieces_first: '先下载首尾文件块',
|
||||
|
||||
also_delete_files: '同时删除文件',
|
||||
},
|
||||
|
||||
msg: {
|
||||
'item_is_required': '%{item}不能为空',
|
||||
},
|
||||
|
||||
dialog: {
|
||||
add_torrents: {
|
||||
placeholder: '将种子拖到这里上传,\n或者点击右边的附件图标来选择。',
|
||||
hint: '每行一个链接',
|
||||
},
|
||||
delete_torrents: {
|
||||
msg: '你确定要删除选中的种子吗?',
|
||||
also_delete_same_name_torrents: '同时删除 %{smart_count} 个同名的种子',
|
||||
},
|
||||
set_category: {
|
||||
move: '你确定要移动选中的种子到分类 %{category} 吗?',
|
||||
reset: '你确定重置选中的种子的分类吗?',
|
||||
also_move_same_name_torrents: '同时移动 %{smart_count} 个同名的种子',
|
||||
}
|
||||
},
|
||||
|
||||
state: {
|
||||
_: '状态',
|
||||
|
||||
downloading: '下载',
|
||||
seeding: '做种',
|
||||
completed: '完成',
|
||||
resumed: '恢复',
|
||||
paused: '暂停',
|
||||
active: '活动',
|
||||
inactive: '空闲',
|
||||
errored: '错误',
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import Vue from 'vue';
|
||||
import vuetify from './plugins/vuetify';
|
||||
import './plugins/i18n';
|
||||
import './plugins/composition-api';
|
||||
import vuetify from './plugins/vuetify';
|
||||
|
||||
import store from './store';
|
||||
// import router from './router';
|
||||
import './filters';
|
||||
import './directives';
|
||||
import './locale';
|
||||
|
||||
import App from './App.vue';
|
||||
|
||||
import 'roboto-fontface/css/roboto/roboto-fontface.css';
|
||||
|
||||
10
src/plugins/i18n.ts
Normal file
10
src/plugins/i18n.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Vue from 'vue';
|
||||
import { tr } from '@/locale';
|
||||
|
||||
class I18n {
|
||||
static install() {
|
||||
Vue.prototype.$t = tr;
|
||||
}
|
||||
}
|
||||
|
||||
Vue.use(I18n);
|
||||
@@ -1,14 +1,19 @@
|
||||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify/lib';
|
||||
// import zhHans from 'vuetify/src/locale/zh-Hans';
|
||||
import i18n from '@/locale';
|
||||
|
||||
Vue.use(Vuetify);
|
||||
|
||||
let locale = i18n.locale();
|
||||
locale = locale === 'zh-CN' ? 'zh-Hans' : locale.split('-', 1)[0];
|
||||
|
||||
const { default: translation } = require('vuetify/src/locale/' + locale);
|
||||
|
||||
export default new Vuetify({
|
||||
// lang: {
|
||||
// locales: { zhHans },
|
||||
// current: 'zh-Hans',
|
||||
// },
|
||||
lang: {
|
||||
locales: { [locale]: translation },
|
||||
current: locale,
|
||||
},
|
||||
icons: {
|
||||
iconfont: 'mdi',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user