Repository: agalwood/Motrix Branch: master Commit: 7012040fec92 Files: 475 Total size: 896.6 KB Directory structure: gitextract_m9s_ek03/ ├── .babelrc ├── .editorconfig ├── .electron-vue/ │ ├── build.js │ ├── dev-client.js │ ├── dev-runner.js │ ├── webpack.main.config.js │ ├── webpack.renderer.config.js │ └── webpack.web.config.js ├── .eslintignore ├── .eslintrc.js ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 1_bug_report.yml │ │ ├── 2_bug_report_cn.yml │ │ ├── feature_request.md │ │ └── feature_request_cn.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── lock.yml │ └── workflows/ │ ├── codeql-analysis.yml │ └── release.yml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING-CN.md ├── CONTRIBUTING.md ├── LICENSE ├── README-CN.md ├── README.md ├── app-update.yml ├── appveyor.yml ├── build/ │ ├── afterPackHook.js │ ├── afterSignHook.js │ ├── background.tiff │ ├── icon.icns │ └── torrent.icns ├── electron-builder.json ├── extra/ │ ├── README.md │ ├── darwin/ │ │ ├── arm64/ │ │ │ └── engine/ │ │ │ ├── aria2.conf │ │ │ └── aria2c │ │ └── x64/ │ │ └── engine/ │ │ ├── aria2.conf │ │ └── aria2c │ ├── linux/ │ │ ├── arm64/ │ │ │ └── engine/ │ │ │ ├── aria2.conf │ │ │ └── aria2c │ │ ├── armv7l/ │ │ │ └── engine/ │ │ │ ├── aria2.conf │ │ │ └── aria2c │ │ └── x64/ │ │ └── engine/ │ │ ├── aria2.conf │ │ └── aria2c │ └── win32/ │ ├── ia32/ │ │ └── engine/ │ │ └── aria2.conf │ └── x64/ │ └── engine/ │ └── aria2.conf ├── jsconfig.json ├── package.json ├── src/ │ ├── index.ejs │ ├── main/ │ │ ├── Application.js │ │ ├── Launcher.js │ │ ├── configs/ │ │ │ ├── engine.js │ │ │ ├── page.js │ │ │ └── protocol.js │ │ ├── core/ │ │ │ ├── AutoLaunchManager.js │ │ │ ├── ConfigManager.js │ │ │ ├── Context.js │ │ │ ├── EnergyManager.js │ │ │ ├── Engine.js │ │ │ ├── EngineClient.js │ │ │ ├── ExceptionHandler.js │ │ │ ├── Logger.js │ │ │ ├── ProtocolManager.js │ │ │ ├── UPnPManager.js │ │ │ └── UpdateManager.js │ │ ├── index.dev.js │ │ ├── index.js │ │ ├── menus/ │ │ │ ├── darwin.json │ │ │ ├── linux.json │ │ │ ├── touchBar.json │ │ │ ├── tray.json │ │ │ └── win32.json │ │ ├── pages/ │ │ │ ├── about.html │ │ │ └── index.html │ │ ├── ui/ │ │ │ ├── DockManager.js │ │ │ ├── Locale.js │ │ │ ├── MenuManager.js │ │ │ ├── ThemeManager.js │ │ │ ├── TouchBarManager.js │ │ │ ├── TrayManager.js │ │ │ └── WindowManager.js │ │ └── utils/ │ │ ├── index.js │ │ └── menu.js │ ├── renderer/ │ │ ├── api/ │ │ │ ├── Api.js │ │ │ └── index.js │ │ ├── assets/ │ │ │ └── .gitkeep │ │ ├── components/ │ │ │ ├── About/ │ │ │ │ ├── AboutPanel.vue │ │ │ │ ├── AppInfo.vue │ │ │ │ └── Copyright.vue │ │ │ ├── Aside/ │ │ │ │ └── Index.vue │ │ │ ├── Browser/ │ │ │ │ └── index.vue │ │ │ ├── CommandManager/ │ │ │ │ ├── index.js │ │ │ │ └── instance.js │ │ │ ├── DragSelect/ │ │ │ │ └── Index.vue │ │ │ ├── Dragger/ │ │ │ │ └── Index.vue │ │ │ ├── Icons/ │ │ │ │ ├── Icon.vue │ │ │ │ ├── arrow-down.js │ │ │ │ ├── arrow-up.js │ │ │ │ ├── audio.js │ │ │ │ ├── delete.js │ │ │ │ ├── dice.js │ │ │ │ ├── document.js │ │ │ │ ├── folder.js │ │ │ │ ├── image.js │ │ │ │ ├── inbox.js │ │ │ │ ├── info-circle.js │ │ │ │ ├── info-square.js │ │ │ │ ├── link.js │ │ │ │ ├── magnet.js │ │ │ │ ├── menu-about.js │ │ │ │ ├── menu-add.js │ │ │ │ ├── menu-preference.js │ │ │ │ ├── menu-task.js │ │ │ │ ├── more.js │ │ │ │ ├── node.js │ │ │ │ ├── preference-advanced.js │ │ │ │ ├── preference-basic.js │ │ │ │ ├── preference-lab.js │ │ │ │ ├── purge.js │ │ │ │ ├── refresh.js │ │ │ │ ├── speedometer.js │ │ │ │ ├── sync.js │ │ │ │ ├── task-history.js │ │ │ │ ├── task-pause-line.js │ │ │ │ ├── task-pause.js │ │ │ │ ├── task-restart.js │ │ │ │ ├── task-start-line.js │ │ │ │ ├── task-start.js │ │ │ │ ├── task-stop-line.js │ │ │ │ ├── task-stop.js │ │ │ │ ├── trash.js │ │ │ │ ├── video.js │ │ │ │ ├── win-close.js │ │ │ │ ├── win-maximize.js │ │ │ │ └── win-minimize.js │ │ │ ├── Locale/ │ │ │ │ └── index.js │ │ │ ├── Logo/ │ │ │ │ ├── Logo.vue │ │ │ │ └── LogoMini.vue │ │ │ ├── Main.vue │ │ │ ├── Msg/ │ │ │ │ └── index.js │ │ │ ├── Native/ │ │ │ │ ├── DynamicTray.vue │ │ │ │ ├── EngineClient.vue │ │ │ │ ├── Ipc.vue │ │ │ │ ├── SelectDirectory.vue │ │ │ │ ├── ShowInFolder.vue │ │ │ │ └── TitleBar.vue │ │ │ ├── Preference/ │ │ │ │ ├── Advanced.vue │ │ │ │ ├── Basic.vue │ │ │ │ ├── HistoryDirectory.vue │ │ │ │ ├── Index.vue │ │ │ │ ├── Lab.vue │ │ │ │ └── ThemeSwitcher.vue │ │ │ ├── Speedometer/ │ │ │ │ └── Speedometer.vue │ │ │ ├── Subnav/ │ │ │ │ ├── PreferenceSubnav.vue │ │ │ │ ├── SubnavSwitcher.vue │ │ │ │ └── TaskSubnav.vue │ │ │ ├── Task/ │ │ │ │ ├── AddTask.vue │ │ │ │ ├── Index.vue │ │ │ │ ├── SelectTorrent.vue │ │ │ │ ├── TaskActions.vue │ │ │ │ ├── TaskItem.vue │ │ │ │ ├── TaskItemActions.vue │ │ │ │ ├── TaskList.vue │ │ │ │ ├── TaskProgress.vue │ │ │ │ ├── TaskProgressInfo.vue │ │ │ │ └── TaskStatus.vue │ │ │ ├── TaskDetail/ │ │ │ │ ├── Index.vue │ │ │ │ ├── TaskActivity.vue │ │ │ │ ├── TaskFiles.vue │ │ │ │ ├── TaskGeneral.vue │ │ │ │ ├── TaskPeers.vue │ │ │ │ └── TaskTrackers.vue │ │ │ ├── TaskGraphic/ │ │ │ │ ├── Atom.vue │ │ │ │ └── Index.vue │ │ │ └── Theme/ │ │ │ ├── Dark/ │ │ │ │ └── Variables.scss │ │ │ ├── Dark.scss │ │ │ ├── Default.scss │ │ │ ├── Index.scss │ │ │ ├── Light/ │ │ │ │ └── Variables.scss │ │ │ └── Variables.scss │ │ ├── pages/ │ │ │ └── index/ │ │ │ ├── App.vue │ │ │ ├── commands.js │ │ │ └── main.js │ │ ├── router/ │ │ │ └── index.js │ │ ├── store/ │ │ │ ├── index.js │ │ │ └── modules/ │ │ │ ├── app.js │ │ │ ├── index.js │ │ │ ├── preference.js │ │ │ └── task.js │ │ ├── utils/ │ │ │ ├── native.js │ │ │ └── task.js │ │ └── workers/ │ │ └── tray.worker.js │ └── shared/ │ ├── aria2/ │ │ ├── index.js │ │ └── lib/ │ │ ├── Aria2.js │ │ ├── Deferred.js │ │ ├── JSONRPCClient.js │ │ ├── JSONRPCError.js │ │ ├── debug.js │ │ └── promiseEvent.js │ ├── colors.json │ ├── configKeys.js │ ├── constants.js │ ├── keymap.json │ ├── locales/ │ │ ├── LocaleManager.js │ │ ├── all.js │ │ ├── app.js │ │ ├── ar/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── bg/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── ca/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── de/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── el/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── en-US/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── es/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── fa/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── fr/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── hu/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── id/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── index.js │ │ ├── it/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── ja/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── ko/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── nb/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── nl/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── pl/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── pt-BR/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── ro/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── ru/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── th/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── tr/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── uk/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── vi/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ ├── zh-CN/ │ │ │ ├── about.js │ │ │ ├── app.js │ │ │ ├── edit.js │ │ │ ├── help.js │ │ │ ├── index.js │ │ │ ├── menu.js │ │ │ ├── preferences.js │ │ │ ├── subnav.js │ │ │ ├── task.js │ │ │ └── window.js │ │ └── zh-TW/ │ │ ├── about.js │ │ ├── app.js │ │ ├── edit.js │ │ ├── help.js │ │ ├── index.js │ │ ├── menu.js │ │ ├── preferences.js │ │ ├── subnav.js │ │ ├── task.js │ │ └── window.js │ ├── ua.js │ └── utils/ │ ├── curl.js │ ├── index.js │ ├── rename.js │ ├── tracker.js │ └── tray.js └── static/ └── .gitkeep ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "comments": false, "env": { "main": { "presets": ["@babel/preset-env"] }, "renderer": { "presets": [ "@babel/preset-env" ], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] }, "web": { "presets": ["@babel/preset-env"], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] } }, "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime" ] } ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .electron-vue/build.js ================================================ 'use strict' process.env.NODE_ENV = 'production' const { say } = require('cfonts') const chalk = require('chalk') const del = require('del') const Webpack = require('webpack') const Multispinner = require('@motrix/multispinner') const mainConfig = require('./webpack.main.config') const rendererConfig = require('./webpack.renderer.config') const webConfig = require('./webpack.web.config') const doneLog = chalk.bgGreen.white(' DONE ') + ' ' const errorLog = chalk.bgRed.white(' ERROR ') + ' ' const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' const isCI = process.env.CI || false if (process.env.BUILD_TARGET === 'clean') { clean() } else if (process.env.BUILD_TARGET === 'web') { web() } else { build() } function clean () { del.sync(['release/*', '!.gitkeep']) console.log(`\n${doneLog}\n`) process.exit() } function build () { greeting() del.sync(['dist/electron/*', '!.gitkeep']) const tasks = ['main', 'renderer'] const m = new Multispinner(tasks, { preText: 'building', postText: 'process' }) let results = '' m.on('success', () => { process.stdout.write('\x1B[2J\x1B[0f') console.log(`\n\n${results}`) console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) process.exit() }) pack(mainConfig).then(result => { results += result + '\n\n' m.success('main') }).catch(err => { m.error('main') console.log(`\n ${errorLog}failed to build main process`) console.error(`\n${err}\n`) process.exit(1) }) pack(rendererConfig).then(result => { results += result + '\n\n' m.success('renderer') }).catch(err => { m.error('renderer') console.log(`\n ${errorLog}failed to build renderer process`) console.error(`\n${err}\n`) process.exit(1) }) } function pack (config) { return new Promise((resolve, reject) => { config.mode = 'production' Webpack(config, (err, stats) => { if (err) { reject(err.stack || err) } else if (stats.hasErrors()) { let err = '' stats.toString({ chunks: false, colors: true }) .split(/\r?\n/) .forEach(line => { err += ` ${line}\n` }) reject(err) } else { resolve(stats.toString({ chunks: false, colors: true })) } }) }) } function web () { deleteSync(['dist/web/*', '!.gitkeep']) webConfig.mode = 'production' Webpack(webConfig, (err, stats) => { if (err || stats.hasErrors()) console.log(err) console.log(stats.toString({ chunks: false, colors: true })) process.exit() }) } function greeting () { const cols = process.stdout.columns let text = '' if (cols > 85) { text = 'lets-build' } else if (cols > 60) { text = 'lets-|build' } else { text = false } if (text && !isCI) { say(text, { colors: ['magentaBright'], font: 'simple3d', space: false }) } else console.log(chalk.magentaBright.bold('\n lets-build')) console.log() } ================================================ FILE: .electron-vue/dev-client.js ================================================ const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') hotClient.subscribe(event => { /** * Reload browser when HTMLWebpackPlugin emits a new index.html * * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. * https://github.com/SimulatedGREG/electron-vue/issues/437 * https://github.com/jantimon/html-webpack-plugin/issues/680 */ // if (event.action === 'reload') { // window.location.reload() // } /** * Notify `mainWindow` when `main` process is compiling, * giving notice for an expected reload of the `electron` process */ if (event.action === 'compiling') { document.body.innerHTML += `
Compiling Main Process...
` } }) ================================================ FILE: .electron-vue/dev-runner.js ================================================ 'use strict' const path = require('node:path') const { spawn } = require('node:child_process') const { say } = require('cfonts') const electron = require('electron') const chalk = require('chalk') const Webpack = require('webpack') const WebpackDevServer = require('webpack-dev-server') const mainConfig = require('./webpack.main.config') const rendererConfig = require('./webpack.renderer.config') let electronProcess = null let manualRestart = false function logStats (proc, data) { let log = '' log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) log += '\n\n' if (typeof data === 'object') { data.toString({ colors: true, chunks: false }).split(/\r?\n/).forEach(line => { log += ' ' + line + '\n' }) } else { log += ` ${data}\n` } log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' console.log(log) } function startRenderer () { return new Promise(async (resolve, reject) => { rendererConfig.entry.index = rendererConfig.entry.index rendererConfig.mode = 'development' const compiler = Webpack(rendererConfig) const devServerOptions = { ...rendererConfig.devServer, port: 9080, static: { directory: path.resolve(__dirname, "../"), }, }; const server = new WebpackDevServer(devServerOptions, compiler) await server.start() resolve() }) } function startMain () { return new Promise((resolve, reject) => { mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) mainConfig.mode = 'development' const compiler = Webpack(mainConfig) compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { logStats('Main', chalk.white.bold('compiling...')) // hotMiddleware.publish({ action: 'compiling' }) done() }) compiler.watch({}, (err, stats) => { if (err) { console.log(err) return } logStats('Main', stats) if (electronProcess && electronProcess.kill) { manualRestart = true process.kill(electronProcess.pid) electronProcess = null startElectron() setTimeout(() => { manualRestart = false }, 5000) } resolve() }) }) } function startElectron () { electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')]) electronProcess.stdout.on('data', data => { electronLog(data, 'blue') }) electronProcess.stderr.on('data', data => { electronLog(data, 'red') }) electronProcess.on('close', () => { if (!manualRestart) process.exit() }) } function electronLog (data, color) { let log = '' data = data.toString().split(/\r?\n/) data.forEach(line => { log += ` ${line}\n` }) if (/[0-9A-z]+/.test(log)) { console.log( chalk[color].bold('┏ Electron -------------------') + '\n\n' + log + chalk[color].bold('┗ ----------------------------') + '\n' ) } } function greeting () { const cols = process.stdout.columns let text = '' if (cols > 104) { text = 'motrix-dev' } else if (cols > 76) { text = 'motrix-|dev' } else { text = false } if (text) { say(text, { colors: ['magentaBright'], font: 'simple3d', space: false }) } else console.log(chalk.magentaBright.bold('\n motrix-dev')) console.log(chalk.blue(' getting ready...') + '\n') } function init () { greeting() Promise.all([startRenderer(), startMain()]) .then(() => { startElectron() }) .catch(err => { console.error(err) }) } init() ================================================ FILE: .electron-vue/webpack.main.config.js ================================================ 'use strict' process.env.BABEL_ENV = 'main' const path = require('node:path') const Webpack = require('webpack') const ESLintPlugin = require('eslint-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') const { dependencies } = require('../package.json') const { appId } = require('../electron-builder.json') const devMode = process.env.NODE_ENV !== 'production' let mainConfig = { entry: { main: path.join(__dirname, '../src/main/index.js') }, externals: [ ...Object.keys(dependencies || {}) ], module: { rules: [ { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, { test: /\.node$/, use: 'node-loader' } ] }, node: { __dirname: devMode, __filename: devMode }, output: { filename: '[name].js', libraryTarget: 'commonjs2', path: path.join(__dirname, '../dist/electron') }, plugins: [ new Webpack.NoEmitOnErrorsPlugin(), new ESLintPlugin({ formatter: require('eslint-friendly-formatter') }) ], resolve: { alias: { '@': path.join(__dirname, '../src/main'), '@shared': path.join(__dirname, '../src/shared') }, extensions: ['.js', '.json', '.node'] }, target: 'electron-main', optimization: { minimize: !devMode, minimizer: [ new TerserPlugin({ extractComments: false, }) ], }, } /** * Adjust mainConfig for development settings */ if (devMode) { mainConfig.plugins.push( new Webpack.DefinePlugin({ '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`, 'appId': `"${appId}"` }) ) } /** * Adjust mainConfig for production settings */ if (!devMode) { mainConfig.plugins.push( new Webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"', 'appId': `"${appId}"` }) ) } module.exports = mainConfig ================================================ FILE: .electron-vue/webpack.renderer.config.js ================================================ 'use strict' process.env.BABEL_ENV = 'renderer' const path = require('node:path') const Webpack = require('webpack') const { VueLoaderPlugin } = require('vue-loader') const CopyWebpackPlugin = require('copy-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const ESLintPlugin = require('eslint-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const TerserPlugin = require('terser-webpack-plugin') const { dependencies } = require('../package.json') const devMode = process.env.NODE_ENV !== 'production' /** * List of node_modules to include in webpack bundle * * Required for specific packages like Vue UI libraries * that provide pure *.vue files that need compiling * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals */ let whiteListedModules = ['vue'] let rendererConfig = { entry: { index: path.join(__dirname, '../src/renderer/pages/index/main.js') }, externals: [ ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) ], module: { rules: [ { test: /\.worker\.js$/, use: { loader: 'worker-loader', options: { filename: '[name].js' } } }, { test: /\.scss$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', { loader: 'sass-loader', options: { implementation: require('sass'), additionalData: '@import "@/components/Theme/Variables.scss";', sassOptions: { includePaths:[__dirname, 'src'] } }, } ] }, { test: /\.sass$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', { loader: 'sass-loader', options: { implementation: require('sass'), indentedSyntax: true, additionalData: '@import "@/components/Theme/Variables.scss";', sassOptions: { includePaths:[__dirname, 'src'] } }, } ] }, { test: /\.less$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'less-loader' ] }, { test: /\.css$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, { test: /\.node$/, use: 'node-loader' }, { test: /\.vue$/, use: { loader: 'vue-loader', options: { extractCSS: process.env.NODE_ENV === 'production', loaders: { sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', scss: 'vue-style-loader!css-loader!sass-loader', less: 'vue-style-loader!css-loader!less-loader' } } } }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, type: 'asset/inline' }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, type: 'asset/resource' }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, type: 'asset/inline' } ] }, node: { __dirname: devMode, __filename: devMode }, plugins: [ new VueLoaderPlugin(), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), new HtmlWebpackPlugin({ title: 'Motrix', filename: 'index.html', chunks: ['index'], template: path.resolve(__dirname, '../src/index.ejs'), // minify: { // collapseWhitespace: true, // removeAttributeQuotes: true, // removeComments: true // }, isBrowser: false, isDev: process.env.NODE_ENV !== 'production', nodeModules: devMode ? path.resolve(__dirname, '../node_modules') : false }), new Webpack.HotModuleReplacementPlugin(), new Webpack.NoEmitOnErrorsPlugin(), new ESLintPlugin({ extensions: ['js', 'vue'], formatter: require('eslint-friendly-formatter') }) ], output: { filename: '[name].js', libraryTarget: 'commonjs2', path: path.join(__dirname, '../dist/electron'), globalObject: 'this', publicPath: '' }, resolve: { alias: { '@': path.join(__dirname, '../src/renderer'), '@shared': path.join(__dirname, '../src/shared'), 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['.js', '.vue', '.json', '.css', '.node'] }, target: 'electron-renderer', optimization: { minimize: !devMode, minimizer: [ new TerserPlugin({ extractComments: false, }), new CssMinimizerPlugin(), ], }, } /** * Adjust rendererConfig for development settings */ if (devMode) { rendererConfig.devtool = 'eval-cheap-module-source-map' rendererConfig.plugins.push( new Webpack.DefinePlugin({ '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` }) ) } /** * Adjust rendererConfig for production settings */ if (!devMode) { rendererConfig.plugins.push( new CopyWebpackPlugin({ patterns: [{ from: path.join(__dirname, '../static'), to: path.join(__dirname, '../dist/electron/static'), globOptions: { ignore: [ '.*' ] } }] }), new Webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }), new Webpack.LoaderOptionsPlugin({ minimize: false }) ) } module.exports = rendererConfig ================================================ FILE: .electron-vue/webpack.web.config.js ================================================ 'use strict' process.env.BABEL_ENV = 'web' const path = require('node:path') const { dependencies } = require('../package.json') const Webpack = require('webpack') const { VueLoaderPlugin } = require('vue-loader') const CopyWebpackPlugin = require('copy-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const ESLintPlugin = require('eslint-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const TerserPlugin = require('terser-webpack-plugin') const devMode = process.env.NODE_ENV !== 'production' /** * List of node_modules to include in webpack bundle * * Required for specific packages like Vue UI libraries * that provide pure *.vue files that need compiling * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals */ let whiteListedModules = ['vue'] let webConfig = { entry: { index: path.join(__dirname, '../src/renderer/pages/index/main.js') }, externals: [ ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) ], module: { rules: [ { test: /\.worker\.js$/, use: { loader: 'worker-loader', options: { filename: '[name].js' } } }, { test: /\.scss$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', { loader: 'sass-loader', options: { implementation: require('sass'), additionalData: '@import "@/components/Theme/Variables.scss"', sassOptions: { includePaths:[__dirname, 'src'] } }, } ] }, { test: /\.sass$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', { loader: 'sass-loader', options: { implementation: require('sass'), indentedSyntax: true, additionalData: '@import "@/components/Theme/Variables.scss"', sassOptions: { includePaths:[__dirname, 'src'] } }, } ] }, { test: /\.less$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'less-loader' ] }, { test: /\.css$/, use: [ devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /\.js$/, use: 'babel-loader', include: [ path.resolve(__dirname, '../src/renderer') ], exclude: /node_modules/ }, { test: /\.vue$/, use: { loader: 'vue-loader', options: { extractCSS: true, loaders: { sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', scss: 'vue-style-loader!css-loader!sass-loader', less: 'vue-style-loader!css-loader!less-loader' } } } }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, type: 'asset/inline' }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, type: 'asset/inline' } ] }, plugins: [ new VueLoaderPlugin(), new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[id].css' }), new HtmlWebpackPlugin({ title: 'Motrix', filename: 'index.html', chunks: ['index'], template: path.resolve(__dirname, '../src/index.ejs'), // minify: { // collapseWhitespace: true, // removeAttributeQuotes: true, // removeComments: true // }, isBrowser: true, isDev: process.env.NODE_ENV !== 'production', nodeModules: devMode ? path.resolve(__dirname, '../node_modules') : false }), new Webpack.DefinePlugin({ 'process.env.IS_WEB': 'true' }), new Webpack.HotModuleReplacementPlugin(), new Webpack.NoEmitOnErrorsPlugin(), new ESLintPlugin({ extensions: ['js', 'vue'], formatter: require('eslint-friendly-formatter') }) ], output: { filename: '[name].js', path: path.join(__dirname, '../dist/web'), globalObject: 'this', publicPath: '' }, resolve: { alias: { '@': path.join(__dirname, '../src/renderer'), '@shared': path.join(__dirname, '../src/shared'), 'vue$': 'vue/dist/vue.esm.js' }, extensions: ['.js', '.vue', '.json', '.css'] }, target: 'web', optimization: { minimize: !devMode, minimizer: [ new TerserPlugin({ extractComments: false, }), new CssMinimizerPlugin(), ], }, } /** * Adjust webConfig for development settings */ if (devMode) { webConfig.devtool = 'eval-cheap-module-source-map' } /** * Adjust webConfig for production settings */ if (!devMode) { webConfig.plugins.push( new CopyWebpackPlugin({ patterns: [{ from: path.join(__dirname, '../static'), to: path.join(__dirname, '../dist/electron/static'), globOptions: { ignore: [ '.*' ] } }] }), new Webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }), new Webpack.LoaderOptionsPlugin({ minimize: true }) ) } module.exports = webConfig ================================================ FILE: .eslintignore ================================================ src/renderer/components/Icons/*.js src/shared/locales/* !src/shared/locales/all.js !src/shared/locales/app.js !src/shared/locales/index.js !src/shared/locales/LocalManager.js ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, env: { browser: true, node: true }, extends: [ 'plugin:vue/essential', '@vue/standard' ], parserOptions: { parser: 'babel-eslint' }, globals: { appId: true, __static: true }, rules: { 'no-console': 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', indent: ['error', 2], 'vue/script-indent': ['error', 2, { baseIndent: 1 }] }, overrides: [ { files: ['*.vue'], rules: { indent: 'off' } } ] } ================================================ FILE: .github/ISSUE_TEMPLATE/1_bug_report.yml ================================================ name: 🐛 [NEW] Bug Report description: File a bug report here title: "[BUG]: " labels: ["bug"] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report 🤗 Make sure there aren't any open/closed issues for this topic 😃 - type: textarea id: bug-description attributes: label: Description of the bug description: Give us a brief description of what happened and what should have happened validations: required: true - type: textarea id: app-version attributes: label: Motrix Version description: Please provide detailed version information and installation method, such as macOS Apple silicon dmg, Windows Universal installation file, etc. validations: required: true - type: textarea attributes: label: Environment description: | Run this command in your project's root folder and paste the result: ```sh npx envinfo --system --binaries --browsers ``` add `| pbcopy` if you're in macOS for easy copy paste. Alternatively, you can manually gather the version information from your environment. validations: required: true - type: textarea id: steps-to-reproduce attributes: label: Steps To Reproduce description: Steps to reproduce the behavior. placeholder: | 1. Go to '...' 2. Click on '...' 3. Scroll down to '...' 4. See error More info: A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is **required**, otherwise the issue might be closed without further notice. [**Why & How?**](https://antfu.me/posts/why-reproductions-are-required) validations: required: true - type: textarea id: additional-information attributes: label: Additional Information description: | Provide any additional information such as logs, screenshots, likes, scenarios in which the bug occurs so that it facilitates resolving the issue. - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/agalwood/Motrix/blob/master/CODE_OF_CONDUCT.md) required: true - label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. required: true - label: Check that this is a concrete bug. For Q&A, please open a [GitHub Discussion](https://github.com/agalwood/Motrix/discussions) instead. required: true - label: The provided reproduction is a [minimal reproducible](https://stackoverflow.com/help/minimal-reproducible-example) of the bug. required: true ================================================ FILE: .github/ISSUE_TEMPLATE/2_bug_report_cn.yml ================================================ name: 🐛 [新] Bug 报告 description: 在这里提交 Bug 报告 title: "[BUG]: " labels: ["bug"] body: - type: markdown attributes: value: | 感谢您抽出时间来填写这份 Bug 报告 🤗 请确保此问题没有已存在的开放/关闭问题 😃 - type: textarea id: bug-description attributes: label: Bug 描述 description: 给我们一个简短的描述,说明发生了什么以及应该发生什么。 validations: required: true - type: textarea id: app-version attributes: label: Motrix 版本 description: 请提供详细的版本信息以及安装的方式,如 macOS Apple silicon dmg、Windows Universal 安装文件等 validations: required: true - type: textarea attributes: label: 环境 description: | 在项目的根目录下运行以下命令,并将结果粘贴到下方: ```sh npx envinfo --system --binaries --browsers ``` 在 macOS 中,如果您需要轻松复制粘贴,可以添加 `| pbcopy`。 或者,您也可以手动收集您的环境版本信息。 validations: required: true - type: textarea id: steps-to-reproduce attributes: label: 复现步骤 description: 复现该问题的步骤。 placeholder: | 1. 前往 '...' 2. 点击 '...' 3. 滚动到 '...' 4. 查看错误 更多信息:[最小复现例子](https://stackoverflow.com/help/minimal-reproducible-example) 是必需的,否则该问题可能会被关闭而没有进一步的通知。[**为什么 & 如何?**](https://antfu.me/posts/why-reproductions-are-required) validations: required: true - type: textarea id: additional-information attributes: label: 额外信息 description: | 提供任何额外的信息,例如日志、截图、喜欢、发生该 Bug 的场景,以便有助于解决问题。 - type: checkboxes id: checkboxes attributes: label: 验证 description: 在提交问题之前,请确保您完成了以下操作 options: - label: 遵循我们的[行为准则](https://github.com/agalwood/Motrix/blob/master/CODE_OF_CONDUCT.md) required: true - label: 确认是否已经有一个报告了相同的 Bug,以避免创建重复的问题。 required: true - label: 确认此问题是一个具体的 Bug。若要进行问答,请开启 [GitHub 讨论](https://github.com/agalwood/Motrix/discussions)。 required: true - label: 提供的复现是该 Bug 的 [最小复现例子](https://stackoverflow.com/help/minimal-reproducible-example)。 required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement ✨ assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request_cn.md ================================================ --- name: 新功能请求 about: 你期望 Motrix 未来添加的新功能 title: '' labels: enhancement ✨ assignees: '' --- **请描述一下你的新功能请求是否与已知问题有关?** 简明扼要地描述了问题所在。 **描述你想要的解决方案** 简明扼要地描述你想要的解决方案。 **描述你考虑过的替代方案** 简明扼要地描述你考虑过的任何替代解决方案或功能。 **更多信息** 补充有关该新功能的其他信息。 ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Description ## Related Issues ### Checklist: * [ ] Have you checked to ensure there aren't other open [Pull Requests](../../../pulls) for the same update/change? * [ ] Have you linted your code locally prior to submission? * [ ] Have you successfully ran app with your changes locally? ================================================ FILE: .github/lock.yml ================================================ # Configuration for Lock Threads - https://github.com/dessant/lock-threads # Number of days of inactivity before a closed issue or pull request is locked daysUntilLock: 60 # Skip issues and pull requests created before a given timestamp. Timestamp must # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable skipCreatedBefore: false # Issues and pull requests with these labels will be ignored. Set to `[]` to disable exemptLabels: [] # Label to add before locking, such as `outdated`. Set to `false` to disable lockLabel: false # Comment to post before locking. Set to `false` to disable lockComment: > This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. # Assign `resolved` as the reason for locking. Set to `false` to disable setLockReason: true # Limit to only `issues` or `pulls` # only: issues # Optionally, specify configuration settings just for `issues` or `pulls` # issues: # exemptLabels: # - help-wanted # lockLabel: outdated # pulls: # daysUntilLock: 30 # Repository to extend settings from # _extends: repo ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '23 8 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'javascript' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 ================================================ FILE: .github/workflows/release.yml ================================================ name: Build/release on: push: branches: [ master ] pull_request: branches: [ master ] jobs: release: runs-on: ${{ matrix.os }} # Platforms to build on/for strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] steps: - name: Check out Git repository uses: actions/checkout@v3 - name: Install Node.js, NPM and Yarn uses: actions/setup-node@v3 with: node-version: 18 - name: Install Snapcraft uses: samuelmeuli/action-snapcraft@v2 # Only install Snapcraft on Ubuntu if: startsWith(matrix.os, 'ubuntu') env: # Snapcraft SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }} - name: Test Snapcraft if: startsWith(matrix.os, 'ubuntu') run: snapcraft --help - name: Build/release Electron app uses: motrixapp/action-electron-builder@v2 with: build_script_name: 'build:github' # GitHub token, automatically provided to the action # (No need to define this secret in the repo settings) github_token: ${{ secrets.github_token }} # macOS code signing certificate mac_certs: ${{ secrets.mac_certs }} mac_certs_password: ${{ secrets.mac_certs_password }} # If the commit is tagged with a version (e.g. "v1.0.0"), # release the app after building release: ${{ vars.skip_publish != 'true' }} env: # Snapcraft SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }} # macOS notarization TEAM_ID: ${{ secrets.team_id }} APPLE_ID: ${{ secrets.apple_id }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.apple_app_specific_password }} ================================================ FILE: .gitignore ================================================ !.gitkeep .DS_Store .env .idea/ .vs/ .vscode/ *.log node_modules/ thumbs.db # npm package .npmrc npm-debug.log.* # Eslint Cache .eslintcache* # electron builder *.provisionprofile build/*.plist dist/electron/* dist/web/* # release release/* ================================================ FILE: .travis.yml ================================================ sudo: required dist: trusty language: c jobs: include: - os: osx osx_image: xcode11.3 - os: linux env: CC=clang CXX=clang++ npm_config_clang=1 compiler: clang cache: directories: - node_modules - $HOME/.cache/electron - $HOME/.cache/electron-builder addons: apt: packages: - libgnome-keyring-dev - icnsutils - rpm before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi install: - nvm install 12.14.1 - source ~/.bashrc - npm install -g xvfb-maybe - npm install script: - npm run release before_cache: - rm -rf $HOME/.cache/electron-builder/wine branches: only: - master ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at agalwood.net@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: CONTRIBUTING-CN.md ================================================ # Motrix 贡献指南 开始贡献之前,确保你已经理解了 [GitHub 的协作流程](https://guides.github.com/introduction/flow/)。 ## 🌍 翻译指南 首先你要确定一个语言的英文简写作为 **locale**,如 en-US,这个 locale 值请严格参考 [Electron 的 Locales 文档](https://www.electronjs.org/docs/api/app#appgetlocale) 和 [Chromium 源代码](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc)。 Motrix 的国际化分两部分: - Element UI - 菜单和主界面 ### Element UI Element UI 的国际化由 [Element 社区](http://element.eleme.io/#/en-US/component/i18n)提供,找到 **locale** 对应的语言包文件「两者 locale 命名可能不一致」,在 `src/shared/locales/all.js` 中引入,如 ```javascript import eleLocaleEn from 'element-ui/lib/locale/lang/en' import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN' ``` ### 菜单和主界面 Motrix 使用 i18next 作为翻译支持库,所以你可能需要简单了解一下它的[使用方法](https://www.i18next.com/overview/getting-started)。 配置文件按照语言 (**locale**) 划分目录:`src/shared/locales`,如:`src/shared/locales/en-US` 和 `src/shared/locales/zh-CN`。 目录里面有按业务模块划分的语言文件 菜单模块经过重构之后,国际化已经打散到了以下文件里了,不再需要再复制 `src/main/menus` 里的配置。 - about.js - app.js - edit.js - help.js - index.js - menu.js - preferences.js - subnav.js - task.js - window.js ================================================ FILE: CONTRIBUTING.md ================================================ # Motrix Contributing Guide Before you start contributing, make sure you already understand [GitHub flow](https://guides.github.com/introduction/flow/). ## 🌍 Translation Guide First you need to determine the English abbreviation of a language as **locale**, such as en-US, this locale value should strictly refer to the [Electron's Documentation](https://www.electronjs.org/docs/api/app#appgetlocale) and [Chromium Source Code](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/l10n/l10n_util.cc). The internationalization of Motrix is divided into two parts: - Element UI - Menu & Main Interface ### Element UI The internationalization of Element UI is provided by the [Element community](http://element.eleme.io/#/en-US/component/i18n), then find the language pack file corresponding to **locale** (both locale naming may be inconsistent), which is import in `src/shared/locales/all.js`, such as ```javascript import eleLocaleEn from 'element-ui/lib/locale/lang/en' import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN' ``` ### Menu & Main Interface Motrix uses the [i18next](https://www.i18next.com/overview/getting-started) library for internationalization, so you need a quick look at how to use it. The configuration files are divided by **locale**: `src/shared/locales`, such as `src/shared/locales/en-US` and `src/shared/locales/zh-CN`. There are language files in the directory according to the business module. After the menu module is refactored, the internationalization of the menu has been dispersed into the following files, and there is no need to copy the configuration in `src/main/menus`. - about.js - app.js - edit.js - help.js - index.js - menu.js - preferences.js - subnav.js - task.js - window.js ================================================ FILE: LICENSE ================================================ The MIT License Copyright 2018-present Dr_rOot Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README-CN.md ================================================ # Motrix

Motrix App Icon

## 一款全能的下载工具 [![GitHub release](https://img.shields.io/github/v/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) ![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667) [English](./README.md) | 简体中文 我是个兴趣使然的桌面应用开发者🤓,利用搬砖之余开发了 Motrix。 Motrix 是一款全能的下载工具,支持下载 HTTP、FTP、BT、磁力链等资源。它的界面简洁易用,希望大家喜欢 👻。 ✈️ 去 [官网](https://motrix.app/zh-CN) 逛逛 | 📖 查看 [帮助手册](http://motrix.app/support/issues) ## 💽 安装稳定版 [GitHub](https://github.com/agalwood/Motrix/releases) 和 [官网](https://motrix.app/zh-CN) 提供了已经编译好的稳定版安装包,当然你也可以自己克隆代码编译打包。 ### Windows 建议使用安装包(Motrix-Setup-x.y.z.exe)安装 Motrix 以确保完整的体验,例如关联 torrent 文件,捕获磁力链等。 如果你在 Windows 是用包管理工具来管理应用,如 [Chocolatey](https://chocolatey.org)、[scoop](https://github.com/lukesampson/scoop),你可以使用它们安装 Motrix。 #### Chocolatey 感谢 [@Yato](https://github.com/iYato) 持续维护着 [Motrix Chocolatey](https://community.chocolatey.org/packages/motrix) 包。要安装 Motrix,请从 `命令行` 或 `PowerShell` 中运行以下命令: ```bash # 安装 choco install motrix # 升级 choco upgrade motrix ``` #### scoop 如果你更喜欢便携版,你可以使用 [scoop](https://github.com/lukesampson/scoop)(需要 Windows 7+,天朝用户可能需要设置 Git 代理)安装最新便携版本的 Motrix。 ```bash scoop bucket add extras scoop install motrix ``` ### macOS macOS 用户可以使用 `brew` 安装 Motrix,感谢 [@Mitscherlich](https://github.com/Mitscherlich) 的 [PR](https://github.com/Homebrew/homebrew-cask/pull/59494)。 ```bash brew update && brew install motrix ``` #### 自动更新 Motrix v1.8.0+ 版本更改了应用 BundleID ( `net.agalwood.Motrix` => `app.motrix.native` ), Motrix v1.6.11 的自动更新会因为签名不一致而失败。[Motrix 安装助手](https://github.com/motrixapp/motrix-install-assistant)将帮助您安装最新的 Motrix 应用程序。

Motrix Install Assistant Icon

### Linux 你可以下载 `AppImage` (适用于所有 Linux 发行版)或 `snap` 来安装 Motrix,更多 Linux 安装包格式请查看 [GitHub/release](https://github.com/agalwood/Motrix/releases) 。 Motrix 在 Linux 中首次启动可能需要使用 `sudo` 运行,因为可能没有创建下载会话文件的权限 (`/var/cache/aria2.session`)。 如果你想自己通过编译源码来安装,请阅读 **编译打包** 部分。 #### AppImage 最新版的 Motrix AppImage 需要自己手动进执行桌面集成。请查看 [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) 的文档进行操作。 > 桌面集成 > electron-builder v21 之后,桌面集成不再是 AppImage 文件的一部分。 > 推荐使用 [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) 集成 AppImage。 Deepin 20 Beta 用户安装 Motrix 失败的问题,请按照以下方法处理: 打开`终端`,黏贴运行如下命令之后再次安装 Motrix。 ```bash sudo apt --fix-broken install ``` #### Snap Motrix 已经上架 [Snapcraft](https://snapcraft.io/motrix) ,Ubuntu 用户推荐从 Snap 商店下载。 v1.5.10 提示 系统托盘可能无法正常显示指示器,导致退出应用程序不方便。 请取消勾选 偏好设置——基本设置——隐藏应用程序菜单(仅限Windows和Linux),点击保存并应用。然后点击 "文件 "菜单中的 "退出",退出应用程序。 请更新到 v1.5.12 及以上版本,可以使用键盘组合快捷键 Ctrl + q 快速退出应用。 #### AUR 对于 Arch Linux 用户,可以使用 [aur](https://aur.archlinux.org/packages/motrix/) 安装 Motrix,感谢维护者 [@weearc](https://github.com/weearc)。 运行以下命令进行安装: ```bash yay -S motrix ``` #### Flatpak 感谢 [@proletarius101](https://github.com/proletarius101) 的 [PR](https://github.com/flathub/flathub/pull/2334),Motrix 已经上架 [Flathub](https://flathub.org/apps/details/net.agalwood.Motrix),喜欢 Flatpak 的 Linux 用户可以尝试。 ```bash # 安装 flatpak install flathub net.agalwood.Motrix # 运行 flatpak run net.agalwood.Motrix ``` ## ✨ 特性 - 🕹 简洁明了的图形操作界面 - 🦄 支持BT和磁力链任务 - ☑️ 支持选择性下载BT部分文件 - 📡 每天自动更新 Tracker 服务器列表 - 🔌 UPnP & NAT-PMP 端口映射 - 🎛 最高支持 10 个任务同时下载 - 🚀 单任务最高支持 64 线程下载 - 🚥 设置上传/下载限速 - 🕶 模拟用户代理UA - 🔔 下载完成后通知 - 💻 支持触控栏快捷键 (Mac 专享) - 🤖 常驻系统托盘,操作更加便捷 - 📟 系统托盘速度仪表显示实时速度 (Mac 专享) - 🌑 深色模式 - 🗑 移除任务时可同时删除相关文件 - 🌍 国际化,[查看已可选的语言](#-国际化) - 🛠 更多特性开发中 ## 🖥 应用界面 ![motrix-screenshot-task-cn.png](https://cdn.nlark.com/yuque/0/2020/png/129147/1589782239990-fecb9065-19ac-4c35-938b-0be45621ca3a.png) ## ⌨️ 本地开发 ### 克隆代码 ```bash git clone git@github.com:agalwood/Motrix.git ``` ### 安装依赖 ```bash cd Motrix yarn ``` 天朝大陆用户建议使用淘宝的 npm 源 ```bash yarn config set registry 'https://registry.npmmirror.com' npm config set registry 'https://registry.npmmirror.com' export ELECTRON_MIRROR='https://npm.taobao.org/mirrors/electron/' export SASS_BINARY_SITE='https://npm.taobao.org/mirrors/node-sass' ``` > Error: Electron failed to install correctly, please delete node_modules/electron and try installing again `Electron` 下载安装失败的问题,解决方式请参考 https://github.com/electron/electron/issues/8466#issuecomment-571425574 ### 开发模式 ```bash yarn run dev ``` ### 编译打包 ```bash yarn run build ``` #### 编译 Apple Silicon 版本 ```bash yarn run build:applesilicon ``` 完成之后可以在项目的 `release` 目录看到编译打包好的应用文件 ## 🛠 技术栈 - [Electron](https://electronjs.org/) - [Vue](https://vuejs.org/) + [VueX](https://vuex.vuejs.org/) + [Element](https://element.eleme.io) - [Aria2](https://aria2.github.io/) ## ☑️ TODO 开发计划请移步 [Trello](https://trello.com/b/qNUzA0bv/motrix) 查看 ## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) 如果你有兴趣参与共同开发,欢迎 FORK 和 PR。 ## 🌍 国际化 欢迎大家将 Motrix 翻译成更多的语言版本 🧐,开工之前请先阅读一下 [翻译指南](./CONTRIBUTING-CN.md#-翻译指南)。 | Key | Name | Status | |-------|:--------------------|:-------------| | ar | Arabic | ✔️ [@hadialqattan](https://github.com/hadialqattan), [@AhmedElTabarani](https://github.com/AhmedElTabarani) | | bg | Българският език | ✔️ [@null-none](https://github.com/null-none) | | ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) | | de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) | | el | Ελληνικά | ✔️ [@Likecinema](https://github.com/Likecinema) | | en-US | English | ✔️ | | es | Español | ✔️ [@Chofito](https://github.com/Chofito)| | fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) | | fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) | | hu | Hungarian | ✔️ [@zalnaRs](https://github.com/zalnaRs) | | id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) | | it | Italiano | ✔️ [@blackcat-917](https://github.com/blackcat-917) | | ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) | | ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) | | nb | Norsk Bokmål | ✔️ [@rubjo](https://github.com/rubjo) | | nl | Nederlands | ✔️ [@nickbouwhuis](https://github.com/nickbouwhuis) | | pl | Polski | ✔️ [@KanarekLife](https://github.com/KanarekLife) | | pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) | | ro | Română | ✔️ [@alyn3d](https://github.com/alyn3d) | | ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) | | th | แบบไทย | ✔️ [@nxanywhere](https://github.com/nxanywhere) | | tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) | | uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) | | vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) | | zh-CN | 简体中文 | ✔️ | | zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) [@5idereal](https://github.com/5idereal) | ## 📜 开源许可 基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。 ================================================ FILE: README.md ================================================ # Motrix

Motrix App Icon

## A full-featured download manager [![GitHub release](https://img.shields.io/github/v/release/agalwood/Motrix.svg)](https://github.com/agalwood/Motrix/releases) ![Build/release](https://github.com/agalwood/Motrix/workflows/Build/release/badge.svg) ![Total Downloads](https://img.shields.io/github/downloads/agalwood/Motrix/total.svg) ![Support Platforms](https://camo.githubusercontent.com/a50c47295f350646d08f2e1ccd797ceca3840e52/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706c6174666f726d2d6d61634f5325323025374325323057696e646f77732532302537432532304c696e75782d6c69676874677265792e737667) English | [简体中文](./README-CN.md) Motrix is a full-featured download manager that supports downloading HTTP, FTP, BitTorrent, Magnet, etc. Motrix has a clean and easy to use interface. I hope you will like it 👻. ✈️ [Official Website](https://motrix.app) | 📖 [Manual](https://github.com/agalwood/Motrix/wiki) ## 💽 Installation Download from [GitHub Releases](https://github.com/agalwood/Motrix/releases) and install it. ### Windows It is recommended to install Motrix using the installation package (Motrix-Setup-x.y.z.exe) to ensure a complete experience, such as associating torrent files, capturing magnet links, etc. If you use package management tools to manage applications on Windows, such as [Chocolatey](https://chocolatey.org), [scoop](https://github.com/lukesampson/scoop). You can use them to install Motrix. #### Chocolatey Thanks to [@Yato](https://github.com/iYato) for continuing to maintain the [Motrix Chocolatey](https://community.chocolatey.org/packages/motrix) package. To install motrix, run the following command from the `command line` or from `PowerShell`: ```bash # Install choco install motrix # Upgrade choco upgrade motrix ``` #### scoop If you prefer the portable version, you can use [scoop](https://github.com/lukesampson/scoop) (need Windows 7+) to install Motrix. ```bash scoop bucket add extras scoop install motrix ``` ### macOS The macOS users can install Motrix using `brew`, thanks to [PR](https://github.com/Homebrew/homebrew-cask/pull/59494) of [@Mitscherlich](https://github.com/Mitscherlich). ```bash brew update && brew install motrix ``` #### Auto Update Since Motrix v1.8.0 and later versions changed the App BundleID ( `net.agalwood.Motrix` => `app.motrix.native` ), the automatic update of Motrix v1.6.11 will fail. [Motrix Install Assistant](https://github.com/motrixapp/motrix-install-assistant) will help you install the latest Motrix application.

Motrix Install Assistant Icon

### Linux You can download the `AppImage` (for all Linux distributions) or `snap` to install Motrix, see [GitHub/release](https://github.com/agalwood/Motrix/releases) for more Linux installation package formats. Motrix may need to run with `sudo` for the first time in Linux because there is no permission to create the download session file (`/var/cache/aria2.session`). If you want to build from source code, please read the **Build** section. #### AppImage The latest version of Motrix AppImage requires you to manually perform desktop integration. Please check the documentation of [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) . > Desktop Integration > Since electron-builder 21 desktop integration is not a part of produced AppImage file. > [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) is the recommended way to integrate AppImages. Deepin 20 Beta users failed to install Motrix, please follow the steps below: Open the `Terminal`, paste and run the following command to install Motrix again. ```bash sudo apt --fix-broken install ``` #### Snap Motrix has been listed on [Snapcraft](https://snapcraft.io/motrix) , Ubuntu users recommend downloading from the Snap Store. Tips for v1.5.10 The tray may not display the indicator normally, which makes it inconvenient to exit the application. Please unchecked Preferences--Basic Settings--Hide App Menu (Windows & Linux Only), click Save & Apply. Then click "Exit" in the File menu to exit the application. Please update to v1.5.12 and above, you can use the keyboard shortcut Ctrl + q to quickly exit the application. #### AUR For Arch Linux users, Motrix is available in [aur](https://aur.archlinux.org/packages/motrix/), thanks to the maintainer [@weearc](https://github.com/weearc). Run the following command to install: ```bash yay -S motrix ``` #### Flatpak Thanks to the [PR](https://github.com/flathub/flathub/pull/2334) of [@proletarius101](https://github.com/proletarius101), Motrix has been listed [Flathub](https://flathub.org/apps/details/net.agalwood.Motrix), Linux users who like the Flatpak can try it. ```bash # Install flatpak install flathub net.agalwood.Motrix # Run flatpak run net.agalwood.Motrix ``` ## ✨ Features - 🕹 Simple and clear user interface - 🦄 Supports BitTorrent & Magnet - ☑️ BitTorrent selective download - 📡 Update tracker list every day automatically - 🔌 UPnP & NAT-PMP Port Mapping - 🎛 Up to 10 concurrent download tasks - 🚀 Supports 64 threads in a single task - 🚥 Supports speed limit - 🕶 Mock User-Agent - 🔔 Download completed Notification - 💻 Ready for Touch Bar (Mac only) - 🤖 Resident system tray for quick operation - 📟 Tray speed meter displays real-time speed (Mac only) - 🌑 Dark mode - 🗑 Delete related files when removing tasks (optional) - 🌍 I18n, [View supported languages](#-internationalization). - 🛠 More features in development ## 🖥 User Interface ![motrix-screenshot-task-en.png](https://cdn.nlark.com/yuque/0/2020/png/129147/1589782238501-e7b39166-da58-4152-ae34-65a061cafa48.png) ## ⌨️ Development ### Clone Code ```bash git clone git@github.com:agalwood/Motrix.git ``` ### Install Dependencies ```bash cd Motrix yarn ``` > Error: Electron failed to install correctly, please delete node_modules/electron and try installing again `Electron` failed to install correctly, please refer to https://github.com/electron/electron/issues/8466#issuecomment-571425574 ### Dev Mode ```bash yarn run dev ``` ### Build Release ```bash yarn run build ``` #### Build for Apple Silicon ```bash yarn run build:applesilicon ``` After building, the application will be found in the project's `release` directory. ## 🛠 Technology Stack - [Electron](https://electronjs.org/) - [Vue](https://vuejs.org/) + [VueX](https://vuex.vuejs.org/) + [Element](https://element.eleme.io) - [Aria2](https://aria2.github.io/) ## ☑️ TODO Development Roadmap see: [Trello](https://trello.com/b/qNUzA0bv/motrix) ## 🤝 Contribute [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) If you are interested in participating in joint development, PR and Forks are welcome! ## 🌍 Internationalization Translations into versions for other languages are welcome 🧐! Please read the [translation guide](./CONTRIBUTING.md#-translation-guide) before starting translations. | Key | Name | Status | |-------|:--------------------|:-------------| | ar | Arabic | ✔️ [@hadialqattan](https://github.com/hadialqattan), [@AhmedElTabarani](https://github.com/AhmedElTabarani) | | bg | Българският език | ✔️ [@null-none](https://github.com/null-none) | | ca | Català | ✔️ [@marcizhu](https://github.com/marcizhu) | | de | Deutsch | ✔️ [@Schloemicher](https://github.com/Schloemicher) | | el | Ελληνικά | ✔️ [@Likecinema](https://github.com/Likecinema) | | en-US | English | ✔️ | | es | Español | ✔️ [@Chofito](https://github.com/Chofito)| | fa | فارسی | ✔️ [@Nima-Ra](https://github.com/Nima-Ra) | | fr | Français | ✔️ [@gpatarin](https://github.com/gpatarin) | | hu | Hungarian | ✔️ [@zalnaRs](https://github.com/zalnaRs) | | id | Indonesia | ✔️ [@aarestu](https://github.com/aarestu) | | it | Italiano | ✔️ [@blackcat-917](https://github.com/blackcat-917) | | ja | 日本語 | ✔️ [@hbkrkzk](https://github.com/hbkrkzk) | | ko | 한국어 | ✔️ [@KOZ39](https://github.com/KOZ39) | | nb | Norsk Bokmål | ✔️ [@rubjo](https://github.com/rubjo) | | nl | Nederlands | ✔️ [@nickbouwhuis](https://github.com/nickbouwhuis) | | pl | Polski | ✔️ [@KanarekLife](https://github.com/KanarekLife) | | pt-BR | Portuguese (Brazil) | ✔️ [@andrenoberto](https://github.com/andrenoberto) | | ro | Română | ✔️ [@alyn3d](https://github.com/alyn3d) | | ru | Русский | ✔️ [@bladeaweb](https://github.com/bladeaweb) | | th | แบบไทย | ✔️ [@nxanywhere](https://github.com/nxanywhere) | | tr | Türkçe | ✔️ [@abdullah](https://github.com/abdullah) | | uk | Українська | ✔️ [@bladeaweb](https://github.com/bladeaweb) | | vi | Tiếng Việt | ✔️ [@duythanhvn](https://github.com/duythanhvn) | | zh-CN | 简体中文 | ✔️ | | zh-TW | 繁體中文 | ✔️ [@Yukaii](https://github.com/Yukaii) [@5idereal](https://github.com/5idereal) | ## 📜 License [MIT](https://opensource.org/licenses/MIT) Copyright (c) 2018-present Dr_rOot ================================================ FILE: app-update.yml ================================================ provider: generic url: 'https://dl.motrix.app/releases/' ================================================ FILE: appveyor.yml ================================================ image: Visual Studio 2017 platform: - x64 cache: - node_modules - '%USERPROFILE%\.electron' init: - git config --global core.autocrlf input install: - ps: Install-Product node 12.14.1 x64 - git reset --hard HEAD - npm install - node --version build_script: - npm run release test: off branches: only: - master ================================================ FILE: build/afterPackHook.js ================================================ // Forked from https://github.com/samuelmeuli/mini-diary/blob/master/scripts/after-pack.js /** * Source: https://github.com/patrikx3/redis-ui/blob/master/src/build/after-pack.js * * Copyright (c) 2019 Patrik Laszlo / P3X / Corifeus and contributors. * * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ // TODO: Remove script once https://github.com/electron/electron/issues/17972 is solved by // `electron-builder` const fs = require('node:fs') const { spawn } = require('node:child_process') const { chdir } = require('node:process') const pkg = require('../package.json') const binName = `${pkg.name}`.toLowerCase() const exec = async function exec (cmd, args = []) { const child = spawn(cmd, args, { shell: true }) redirectOutputFor(child) await waitFor(child) } const redirectOutputFor = child => { const printStdout = data => { process.stdout.write(data.toString()) } const printStderr = data => { process.stderr.write(data.toString()) } child.stdout.on('data', printStdout) child.stderr.on('data', printStderr) child.once('close', () => { child.stdout.off('data', printStdout) child.stderr.off('data', printStderr) }) } const waitFor = async function (child) { return new Promise(resolve => { child.once('close', () => resolve()) }) } const linuxTargets = [ 'AppImage', 'deb', 'rpm', 'snap' ] module.exports = async function (context) { console.warn('after build; disable sandbox') const isLinux = context.targets.find( target => linuxTargets.includes(target) ) if (!isLinux) { return } const originalDir = process.cwd() const dirname = context.appOutDir chdir(dirname) await exec('mv', [binName, binName + '.bin']) const wrapperScript = `#!/bin/bash "\${BASH_SOURCE%/*}"/${binName}.bin "$@" --no-sandbox ` fs.writeFileSync(binName, wrapperScript) await exec('chmod', ['+x', binName]) chdir(originalDir) } ================================================ FILE: build/afterSignHook.js ================================================ require('dotenv').config() const { join } = require('node:path') const { notarize } = require('@electron/notarize') const { appId } = require('../electron-builder.json') exports.default = async function (context) { const { electronPlatformName, appOutDir } = context if (electronPlatformName !== 'darwin') { return } const skipNotarize = process.env.SKIP_NOTARIZE if (skipNotarize === 'true') { console.log('Skipping notarize') return } const appBundleId = appId const appName = context.packager.appInfo.productFilename const appPath = join(appOutDir, `${appName}.app`) try { await notarize({ tool: 'notarytool', appBundleId, appPath, teamId: process.env.TEAM_ID, appleId: process.env.APPLE_ID, appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD }) } catch (error) { console.error(error) } console.log(`Done notarizing ${appId}`) } ================================================ FILE: electron-builder.json ================================================ { "productName": "Motrix", "appId": "app.motrix.native", "afterPack": "./build/afterPackHook.js", "afterSign": "./build/afterSignHook.js", "fileAssociations": [ { "ext": "torrent", "mimeType": "application/x-bittorrent", "name": "Torrent", "role": "Viewer" } ], "asar": true, "directories": { "output": "release" }, "files": [ "dist/electron/**/*" ], "protocols": [ { "name": "Motrix Protocol", "schemes": [ "mo", "motrix" ] }, { "name": "Magnet Protocol", "schemes": [ "magnet" ] }, { "name": "Thunder Protocol", "schemes": [ "thunder" ] } ], "dmg": { "window": { "width": 540, "height": 380 }, "contents": [ { "x": 410, "y": 230, "type": "link", "path": "/Applications" }, { "x": 130, "y": 230, "type": "file" } ] }, "mac": { "target": [ { "target": "dmg", "arch": [ "x64", "arm64", "universal" ] }, { "target": "zip", "arch": [ "x64", "arm64", "universal" ] } ], "type": "development", "darkModeSupport": true, "hardenedRuntime": false, "notarize": false, "extraResources": { "from": "./extra/darwin/${arch}/", "to": "./", "filter": [ "**/*" ] }, "category": "public.app-category.utilities" }, "win": { "target": [ { "target": "nsis", "arch": [ "x64", "ia32" ] }, { "target": "appx", "arch": [ "x64", "ia32" ] }, { "target": "zip", "arch": [ "x64", "ia32" ] }, { "target": "portable", "arch": [ "x64", "ia32" ] } ], "extraResources": { "from": "./extra/win32/${arch}/", "to": "./", "filter": [ "**/*" ] } }, "nsis": { "artifactName": "${productName}-Setup-${version}.${ext}", "oneClick": false, "allowToChangeInstallationDirectory": true }, "appx": { "artifactName": "${productName}-${version}-${arch}.${ext}", "applicationId": "app.motrix.native", "identityName": "59744DrrOot.Motrix", "publisher": "CN=5BB4961D-30D8-4993-9ADF-05E1E1F5A395", "publisherDisplayName": "Dr_rOot" }, "portable": { "artifactName": "${productName}-${version}-${arch}.${ext}" }, "linux": { "category": "Network", "mimeTypes": [ "application/x-bittorrent", "x-scheme-handler/magnet" ], "target": [ { "target": "AppImage", "arch": [ "x64", "arm64", "armv7l" ] }, { "target": "deb", "arch": [ "x64", "arm64", "armv7l" ] }, { "target": "rpm", "arch": [ "x64" ] }, { "target": "snap", "arch": [ "x64" ] } ], "extraResources": { "from": "./extra/linux/${arch}/", "to": "./", "filter": [ "**/*" ] } }, "publish": [ { "provider": "generic", "url": "https://dl.motrix.app/releases/" }, { "provider": "github" } ] } ================================================ FILE: extra/README.md ================================================ # aria2 Source code: https://github.com/agalwood/aria2 ================================================ FILE: extra/darwin/arm64/engine/aria2.conf ================================================ ############################### # Motrix macOS Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=none # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/darwin/x64/engine/aria2.conf ================================================ ############################### # Motrix macOS Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=none # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/linux/arm64/engine/aria2.conf ================================================ ############################### # Motrix Linux Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=trunc # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/linux/armv7l/engine/aria2.conf ================================================ ############################### # Motrix Linux Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=trunc # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/linux/x64/engine/aria2.conf ================================================ ############################### # Motrix Linux Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=trunc # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/win32/ia32/engine/aria2.conf ================================================ ############################### # Motrix Windows Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=none # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: extra/win32/x64/engine/aria2.conf ================================================ ############################### # Motrix Windows Aria2 config file # # @see https://aria2.github.io/manual/en/html/aria2c.html # ############################### ################ RPC ################ # Enable JSON-RPC/XML-RPC server. enable-rpc=true # Add Access-Control-Allow-Origin header field with value * to the RPC response. rpc-allow-origin-all=true # Listen incoming JSON-RPC/XML-RPC requests on all network interfaces. rpc-listen-all=true ################ File system ################ # Save a control file(*.aria2) every SEC seconds. auto-save-interval=10 # Enable disk cache. disk-cache=64M # Specify file allocation method. file-allocation=falloc # No file allocation is made for files whose size is smaller than SIZE no-file-allocation-limit=64M # Save error/unfinished downloads to a file specified by --save-session option every SEC seconds. save-session-interval=10 ################ Task ################ # Exclude seed only downloads when counting concurrent active downloads bt-detach-seed-only=true # Verify the peer using certificates specified in --ca-certificate option. check-certificate=false # If aria2 receives "file not found" status from the remote HTTP/FTP servers NUM times # without getting a single byte, then force the download to fail. max-file-not-found=10 # Set number of tries. max-tries=0 # Set the seconds to wait between retries. When SEC > 0, aria2 will retry downloads when the HTTP server returns a 503 response. retry-wait=10 # Set the connect timeout in seconds to establish connection to HTTP/FTP/proxy server. After the connection is established, this option makes no effect and --timeout option is used instead. connect-timeout=10 # Set timeout in seconds. timeout=10 # aria2 does not split less than 2*SIZE byte range. min-split-size=1M # Send Accept: deflate, gzip request header. http-accept-gzip=true # Retrieve timestamp of the remote file from the remote HTTP/FTP server and if it is available, apply it to the local file. remote-time=true # Set interval in seconds to output download progress summary. Setting 0 suppresses the output. summary-interval=0 # Handle quoted string in Content-Disposition header as UTF-8 instead of ISO-8859-1, for example, the filename parameter, but not the extended version filename*. content-disposition-default-utf8=true ################ BT Task ################ # Enable Local Peer Discovery. bt-enable-lpd=true # Requires BitTorrent message payload encryption with arc4. # bt-force-encryption=true # If true is given, after hash check using --check-integrity option and file is complete, continue to seed file. bt-hash-check-seed=true # Specify the maximum number of peers per torrent. bt-max-peers=128 # Try to download first and last pieces of each file first. This is useful for previewing files. bt-prioritize-piece=head # Removes the unselected files when download is completed in BitTorrent. bt-remove-unselected-file=true # Seed previously downloaded files without verifying piece hashes. bt-seed-unverified=false # Set the connect timeout in seconds to establish connection to tracker. After the connection is established, this option makes no effect and --bt-tracker-timeout option is used instead. bt-tracker-connect-timeout=10 # Set timeout in seconds. bt-tracker-timeout=10 # Set host and port as an entry point to IPv4 DHT network. dht-entry-point=dht.transmissionbt.com:6881 # Set host and port as an entry point to IPv6 DHT network. dht-entry-point6=dht.transmissionbt.com:6881 # Enable IPv4 DHT functionality. It also enables UDP tracker support. enable-dht=true # Enable IPv6 DHT functionality. enable-dht6=true # Enable Peer Exchange extension. enable-peer-exchange=true # Specify the string used during the bitorrent extended handshake for the peer's client version. peer-agent=Transmission/3.00 # Specify the prefix of peer ID. peer-id-prefix=-TR3000- ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": [ "./src/renderer/*" ], "@shared/*": [ "./src/shared/*" ] } }, "exclude": ["node_modules", "dist"] } ================================================ FILE: package.json ================================================ { "name": "Motrix", "version": "1.8.19", "description": "A full-featured download manager", "homepage": "https://motrix.app", "author": { "name": "Dr_rOot", "email": "agalwood.net@gmail.com" }, "copyright": "Copyright© Dr_rOot", "license": "MIT", "main": "./dist/electron/main.js", "repository": { "type": "git", "url": "git@github.com:agalwood/Motrix.git" }, "scripts": { "release": "npm run build --publish onTagOrDraft", "build": "node .electron-vue/build.js && electron-builder", "build:applesilicon": "node .electron-vue/build.js && electron-builder --arm64 --mac", "build:github": "node .electron-vue/build.js", "build:dir": "node .electron-vue/build.js && electron-builder --dir", "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", "dev": "node .electron-vue/dev-runner.js", "dev:renderer": "webpack serve --node-env development --hot --color --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist", "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src", "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src", "pack": "npm run pack:main && npm run pack:renderer", "pack:main": "webpack --node-env production --progress --color --config .electron-vue/webpack.main.config.js", "pack:renderer": "webpack --node-env production --progress --color --config .electron-vue/webpack.renderer.config.js", "postinstall": "electron-builder install-app-deps && npm run lint:fix" }, "engines": { "node": ">=16.0.0" }, "dependencies": { "node-fetch": "^2.6.1", "ws": "^8.13.0" }, "devDependencies": { "@babel/core": "^7.21.8", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.21.4", "@babel/preset-env": "^7.21.5", "@babel/register": "^7.21.0", "@babel/runtime": "^7.21.5", "@bany/curl-to-json": "^1.2.7", "@electron/notarize": "^1.2.3", "@electron/osx-sign": "^1.0.4", "@electron/remote": "^2.0.9", "@motrix/multispinner": "^0.2.4", "@motrix/nat-api": "^0.3.4", "@panter/vue-i18next": "^0.15.2", "@vue/eslint-config-standard": "^6.1.0", "ajv": "^8.12.0", "axios": "^1.4.0", "babel-eslint": "^10.1.0", "babel-loader": "^9.1.2", "babel-plugin-component": "^1.1.1", "bittorrent-peerid": "^1.3.6", "blob-util": "^2.0.2", "cfonts": "^3.2.0", "chalk": "^4.1.2", "copy-webpack-plugin": "^11.0.0", "cross-env": "^7.0.3", "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^5.0.0", "del": "^6.1.1", "electron": "^22.3.9", "electron-builder": "^24.4.0", "electron-devtools-installer": "^3.2.0", "electron-is": "^3.0.0", "electron-log": "^4.4.8", "electron-store": "^8.1.0", "electron-updater": "^6.1.0", "element-ui": "^2.15.13", "eslint": "^7.32.0", "eslint-friendly-formatter": "^4.0.1", "eslint-plugin-import": "^2.27.5", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-vue": "^9.12.0", "eslint-webpack-plugin": "^4.0.1", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.1", "i18next": "^22.4.15", "lodash": "^4.17.21", "mini-css-extract-plugin": "2.7.5", "node-loader": "^2.0.0", "normalize.css": "^8.0.1", "parse-torrent": "^9.1.5", "randomatic": "^3.1.1", "sass": "1.62.1", "sass-loader": "^12.6.0", "style-loader": "^3.3.2", "terser-webpack-plugin": "^5.3.8", "vue": "^2.7.14", "vue-electron": "^1.0.6", "vue-loader": "^15.10.1", "vue-router": "^3.6.5", "vue-selectable": "^0.5.0", "vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.7.14", "vuex": "^3.6.2", "vuex-router-sync": "^5.0.0", "webpack": "^5.82.1", "webpack-cli": "^5.1.1", "webpack-dev-server": "^4.15.0", "webpack-hot-middleware": "^2.25.3", "webpack-merge": "^5.8.0", "worker-loader": "^3.0.8" } } ================================================ FILE: src/index.ejs ================================================ Motrix <% if (htmlWebpackPlugin.options.nodeModules) { %> <% } %>
<% if (!htmlWebpackPlugin.options.isBrowser && !htmlWebpackPlugin.options.isDev) { %> <% } %> ================================================ FILE: src/main/Application.js ================================================ import { EventEmitter } from 'node:events' import { readFile, unlink } from 'node:fs' import { extname, basename } from 'node:path' import { app, shell, dialog, ipcMain } from 'electron' import is from 'electron-is' import { isEmpty, isEqual } from 'lodash' import { APP_RUN_MODE, AUTO_SYNC_TRACKER_INTERVAL, AUTO_CHECK_UPDATE_INTERVAL, PROXY_SCOPES } from '@shared/constants' import { checkIsNeedRun } from '@shared/utils' import { convertTrackerDataToComma, fetchBtTrackerFromSource, reduceTrackerString } from '@shared/utils/tracker' import { showItemInFolder } from './utils' import logger from './core/Logger' import Context from './core/Context' import ConfigManager from './core/ConfigManager' import { setupLocaleManager } from './ui/Locale' import Engine from './core/Engine' import EngineClient from './core/EngineClient' import UPnPManager from './core/UPnPManager' import AutoLaunchManager from './core/AutoLaunchManager' import UpdateManager from './core/UpdateManager' import EnergyManager from './core/EnergyManager' import ProtocolManager from './core/ProtocolManager' import WindowManager from './ui/WindowManager' import MenuManager from './ui/MenuManager' import TouchBarManager from './ui/TouchBarManager' import TrayManager from './ui/TrayManager' import DockManager from './ui/DockManager' import ThemeManager from './ui/ThemeManager' export default class Application extends EventEmitter { constructor () { super() this.isReady = false this.init() } init () { this.initContext() this.initConfigManager() this.setupLogger() this.initLocaleManager() this.setupApplicationMenu() this.initWindowManager() this.initUPnPManager() this.startEngine() this.initEngineClient() this.initThemeManager() this.initTrayManager() this.initTouchBarManager() this.initDockManager() this.initAutoLaunchManager() this.initEnergyManager() this.initProtocolManager() this.initUpdaterManager() this.handleCommands() this.handleEvents() this.handleIpcMessages() this.handleIpcInvokes() this.emit('application:initialized') } initContext () { this.context = new Context() } initConfigManager () { this.configListeners = {} this.configManager = new ConfigManager() } offConfigListeners () { try { Object.keys(this.configListeners).forEach((key) => { this.configListeners[key]() }) } catch (e) { logger.warn('[Motrix] offConfigListeners===>', e) } this.configListeners = {} } setupLogger () { const { userConfig } = this.configManager const key = 'log-level' const logLevel = userConfig.get(key) logger.transports.file.level = logLevel this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) logger.transports.file.level = newValue }) } initLocaleManager () { this.locale = this.configManager.getLocale() this.localeManager = setupLocaleManager(this.locale) this.i18n = this.localeManager.getI18n() } setupApplicationMenu () { this.menuManager = new MenuManager() this.menuManager.setup(this.locale) } adjustMenu () { if (is.mas()) { const visibleStates = { 'app.check-for-updates': false, 'task.new-bt-task': false } this.menuManager.updateMenuStates(visibleStates, null, null) this.trayManager.updateMenuStates(visibleStates, null, null) } } startEngine () { const self = this try { this.engine = new Engine({ systemConfig: this.configManager.getSystemConfig(), userConfig: this.configManager.getUserConfig() }) this.engine.start() } catch (err) { const { message } = err dialog.showMessageBox({ type: 'error', title: this.i18n.t('app.system-error-title'), message: this.i18n.t('app.system-error-message', { message }) }).then(_ => { setTimeout(() => { self.quit() }, 100) }) } } async stopEngine () { logger.info('[Motrix] stopEngine===>') try { await this.engineClient.shutdown({ force: true }) logger.info('[Motrix] stopEngine.setImmediate===>') setImmediate(() => { this.engine.stop() }) } catch (err) { logger.warn('[Motrix] shutdown engine fail: ', err.message) } finally { // no finally } } initEngineClient () { const port = this.configManager.getSystemConfig('rpc-listen-port') const secret = this.configManager.getSystemConfig('rpc-secret') this.engineClient = new EngineClient({ port, secret }) } initAutoLaunchManager () { this.autoLaunchManager = new AutoLaunchManager() } initEnergyManager () { this.energyManager = new EnergyManager() } initTrayManager () { this.trayManager = new TrayManager({ theme: this.configManager.getUserConfig('tray-theme'), systemTheme: this.themeManager.getSystemTheme(), speedometer: this.configManager.getUserConfig('tray-speedometer'), runMode: this.configManager.getUserConfig('run-mode') }) this.watchTraySpeedometerEnabledChange() this.trayManager.on('mouse-down', ({ focused }) => { this.sendCommandToAll('application:update-tray-focused', { focused }) }) this.trayManager.on('mouse-up', ({ focused }) => { this.sendCommandToAll('application:update-tray-focused', { focused }) }) this.trayManager.on('drop-files', (files = []) => { this.handleFile(files[0]) }) this.trayManager.on('drop-text', (text) => { this.handleProtocol(text) }) } watchTraySpeedometerEnabledChange () { const { userConfig } = this.configManager const key = 'tray-speedometer' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) this.trayManager.handleSpeedometerEnableChange(newValue) }) } initDockManager () { this.dockManager = new DockManager({ runMode: this.configManager.getUserConfig('run-mode') }) } watchOpenAtLoginChange () { const { userConfig } = this.configManager const key = 'open-at-login' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) if (is.linux()) { return } if (newValue) { this.autoLaunchManager.enable() } else { this.autoLaunchManager.disable() } }) } watchProtocolsChange () { const { userConfig } = this.configManager const key = 'protocols' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) if (!newValue || isEqual(newValue, oldValue)) { return } logger.info('[Motrix] setup protocols client:', newValue) this.protocolManager.setup(newValue) }) } watchRunModeChange () { const { userConfig } = this.configManager const key = 'run-mode' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) this.trayManager.handleRunModeChange(newValue) if (newValue !== APP_RUN_MODE.TRAY) { this.dockManager.show() } else { this.dockManager.hide() // Hiding the dock icon will trigger the entire app to hide. this.show() } }) } watchProxyChange () { const { userConfig } = this.configManager const key = 'proxy' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) this.updateManager.setupProxy(newValue) const { enable, server, bypass, scope = [] } = newValue const system = enable && server && scope.includes(PROXY_SCOPES.DOWNLOAD) ? { 'all-proxy': server, 'no-proxy': bypass } : {} this.configManager.setSystemConfig(system) this.engineClient.call('changeGlobalOption', system) }) } watchLocaleChange () { const { userConfig } = this.configManager const key = 'locale' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) this.localeManager.changeLanguageByLocale(newValue) .then(() => { this.menuManager.handleLocaleChange(newValue) this.trayManager.handleLocaleChange(newValue) }) this.sendCommandToAll('application:update-locale', { locale: newValue }) }) } watchThemeChange () { const { userConfig } = this.configManager const key = 'theme' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) this.themeManager.updateSystemTheme(newValue) this.sendCommandToAll('application:update-theme', { theme: newValue }) }) } watchShowProgressBarChange () { const { userConfig } = this.configManager const key = 'show-progress-bar' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info(`[Motrix] detected ${key} value change event:`, newValue, oldValue) if (newValue) { this.bindProgressChange() } else { this.unbindProgressChange() } }) } initUPnPManager () { this.upnp = new UPnPManager() this.watchUPnPEnabledChange() this.watchUPnPPortsChange() const enabled = this.configManager.getUserConfig('enable-upnp') if (!enabled) { return } this.startUPnPMapping() } async startUPnPMapping () { const btPort = this.configManager.getSystemConfig('listen-port') const dhtPort = this.configManager.getSystemConfig('dht-listen-port') const promises = [ this.upnp.map(btPort), this.upnp.map(dhtPort) ] try { await Promise.allSettled(promises) } catch (e) { logger.warn('[Motrix] start UPnP mapping fail', e.message) } } async stopUPnPMapping () { const btPort = this.configManager.getSystemConfig('listen-port') const dhtPort = this.configManager.getSystemConfig('dht-listen-port') const promises = [ this.upnp.unmap(btPort), this.upnp.unmap(dhtPort) ] try { await Promise.allSettled(promises) } catch (e) { logger.warn('[Motrix] stop UPnP mapping fail', e) } } watchUPnPPortsChange () { const { systemConfig } = this.configManager const watchKeys = ['listen-port', 'dht-listen-port'] watchKeys.forEach((key) => { this.configListeners[key] = systemConfig.onDidChange(key, async (newValue, oldValue) => { logger.info('[Motrix] detected port change event:', key, newValue, oldValue) const enable = this.configManager.getUserConfig('enable-upnp') if (!enable) { return } const promises = [ this.upnp.unmap(oldValue), this.upnp.map(newValue) ] try { await Promise.allSettled(promises) } catch (e) { logger.info('[Motrix] change UPnP port mapping failed:', e) } }) }) } watchUPnPEnabledChange () { const { userConfig } = this.configManager const key = 'enable-upnp' this.configListeners[key] = userConfig.onDidChange(key, async (newValue, oldValue) => { logger.info('[Motrix] detected enable-upnp value change event:', newValue, oldValue) if (newValue) { this.startUPnPMapping() } else { await this.stopUPnPMapping() this.upnp.closeClient() } }) } async shutdownUPnPManager () { const enable = this.configManager.getUserConfig('enable-upnp') if (enable) { await this.stopUPnPMapping() } this.upnp.closeClient() } syncTrackers (source, proxy) { if (isEmpty(source)) { return } setTimeout(() => { fetchBtTrackerFromSource(source, proxy).then((data) => { logger.warn('[Motrix] auto sync tracker data:', data) if (!data || data.length === 0) { return } let tracker = convertTrackerDataToComma(data) tracker = reduceTrackerString(tracker) this.savePreference({ system: { 'bt-tracker': tracker }, user: { 'last-sync-tracker-time': Date.now() } }) }).catch((err) => { logger.warn('[Motrix] auto sync tracker failed:', err.message) }) }, 500) } autoSyncTrackers () { const enable = this.configManager.getUserConfig('auto-sync-tracker') const lastTime = this.configManager.getUserConfig('last-sync-tracker-time') const result = checkIsNeedRun(enable, lastTime, AUTO_SYNC_TRACKER_INTERVAL) logger.info('[Motrix] auto sync tracker checkIsNeedRun:', result) if (!result) { return } const source = this.configManager.getUserConfig('tracker-source') const proxy = this.configManager.getUserConfig('proxy', { enable: false }) this.syncTrackers(source, proxy) } autoResumeTask () { const enabled = this.configManager.getUserConfig('resume-all-when-app-launched') if (!enabled) { return } this.engineClient.call('unpauseAll') } initWindowManager () { this.windowManager = new WindowManager({ userConfig: this.configManager.getUserConfig() }) this.windowManager.on('window-resized', (data) => { this.storeWindowState(data) }) this.windowManager.on('window-moved', (data) => { this.storeWindowState(data) }) this.windowManager.on('window-closed', (data) => { this.storeWindowState(data) }) this.windowManager.on('enter-full-screen', (window) => { this.dockManager.show() }) this.windowManager.on('leave-full-screen', (window) => { const mode = this.configManager.getUserConfig('run-mode') if (mode === APP_RUN_MODE.TRAY) { this.dockManager.hide() } }) } storeWindowState (data = {}) { const enabled = this.configManager.getUserConfig('keep-window-state') if (!enabled) { return } const state = this.configManager.getUserConfig('window-state', {}) const { page, bounds } = data const newState = { ...state, [page]: bounds } this.configManager.setUserConfig('window-state', newState) } start (page, options = {}) { const win = this.showPage(page, options) win.once('ready-to-show', () => { this.isReady = true this.emit('ready') }) if (is.macOS()) { this.touchBarManager.setup(page, win) } } showPage (page, options = {}) { const { openedAtLogin } = options const autoHideWindow = this.configManager.getUserConfig('auto-hide-window') return this.windowManager.openWindow(page, { hidden: openedAtLogin || autoHideWindow }) } show (page = 'index') { this.windowManager.showWindow(page) } hide (page) { if (page) { this.windowManager.hideWindow(page) } else { this.windowManager.hideAllWindow() } } toggle (page = 'index') { this.windowManager.toggleWindow(page) } closePage (page) { this.windowManager.destroyWindow(page) } stop () { try { const promises = [ this.stopEngine(), this.shutdownUPnPManager(), this.energyManager.stopPowerSaveBlocker(), this.trayManager.destroy() ] return promises } catch (err) { logger.warn('[Motrix] stop error: ', err.message) } } async stopAllSettled () { await Promise.allSettled(this.stop()) } async quit () { await this.stopAllSettled() app.exit() } sendCommand (command, ...args) { if (!this.emit(command, ...args)) { const window = this.windowManager.getFocusedWindow() if (window) { this.windowManager.sendCommandTo(window, command, ...args) } } } sendCommandToAll (command, ...args) { if (!this.emit(command, ...args)) { this.windowManager.getWindowList().forEach(window => { this.windowManager.sendCommandTo(window, command, ...args) }) } } sendMessageToAll (channel, ...args) { this.windowManager.getWindowList().forEach(window => { this.windowManager.sendMessageTo(window, channel, ...args) }) } initThemeManager () { this.themeManager = new ThemeManager() this.themeManager.on('system-theme-change', (theme) => { this.trayManager.handleSystemThemeChange(theme) this.sendCommandToAll('application:update-system-theme', { theme }) }) } initTouchBarManager () { if (!is.macOS()) { return } this.touchBarManager = new TouchBarManager() } initProtocolManager () { const protocols = this.configManager.getUserConfig('protocols', {}) this.protocolManager = new ProtocolManager({ protocols }) } handleProtocol (url) { this.show() this.protocolManager.handle(url) } handleFile (filePath) { if (!filePath) { return } if (extname(filePath).toLowerCase() !== '.torrent') { return } this.show() const name = basename(filePath) readFile(filePath, (err, data) => { if (err) { logger.warn(`[Motrix] read file error: ${filePath}`, err.message) return } const dataURL = Buffer.from(data).toString('base64') this.sendCommandToAll('application:new-bt-task-with-file', { name, dataURL }) }) } initUpdaterManager () { if (is.mas()) { return } const enabled = this.configManager.getUserConfig('auto-check-update') const proxy = this.configManager.getSystemConfig('all-proxy') const lastTime = this.configManager.getUserConfig('last-check-update-time') const autoCheck = checkIsNeedRun(enabled, lastTime, AUTO_CHECK_UPDATE_INTERVAL) this.updateManager = new UpdateManager({ autoCheck, proxy }) this.handleUpdaterEvents() } handleUpdaterEvents () { this.updateManager.on('checking', (event) => { this.menuManager.updateMenuItemEnabledState('app.check-for-updates', false) this.trayManager.updateMenuItemEnabledState('app.check-for-updates', false) this.configManager.setUserConfig('last-check-update-time', Date.now()) }) this.updateManager.on('download-progress', (event) => { const win = this.windowManager.getWindow('index') win.setProgressBar(event.percent / 100) }) this.updateManager.on('update-not-available', (event) => { this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true) this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true) }) this.updateManager.on('update-downloaded', (event) => { this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true) this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true) const win = this.windowManager.getWindow('index') win.setProgressBar(1) }) this.updateManager.on('update-cancelled', (event) => { this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true) this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true) const win = this.windowManager.getWindow('index') win.setProgressBar(-1) }) this.updateManager.on('will-updated', async (event) => { this.windowManager.setWillQuit(true) await this.stopAllSettled() }) this.updateManager.on('update-error', (event) => { this.menuManager.updateMenuItemEnabledState('app.check-for-updates', true) this.trayManager.updateMenuItemEnabledState('app.check-for-updates', true) }) } async relaunch () { await this.stopAllSettled() app.relaunch() app.exit() } async resetSession () { await this.stopEngine() app.clearRecentDocuments() const sessionPath = this.context.get('session-path') setTimeout(() => { unlink(sessionPath, (err) => { logger.info('[Motrix] Removed the download seesion file:', err) }) this.engine.start() }, 3000) } savePreference (config = {}) { logger.info('[Motrix] save preference:', config) const { system, user } = config if (!isEmpty(system)) { console.info('[Motrix] main save system config: ', system) this.configManager.setSystemConfig(system) this.engineClient.changeGlobalOption(system) } if (!isEmpty(user)) { console.info('[Motrix] main save user config: ', user) this.configManager.setUserConfig(user) } } handleCommands () { this.on('application:save-preference', this.savePreference) this.on('application:update-tray', (tray) => { this.trayManager.updateTrayByImage(tray) }) this.on('application:relaunch', () => { this.relaunch() }) this.on('application:quit', () => { this.quit() }) this.on('application:show', ({ page }) => { this.show(page) }) this.on('application:hide', ({ page }) => { this.hide(page) }) this.on('application:reset-session', () => this.resetSession()) this.on('application:factory-reset', () => { this.offConfigListeners() this.configManager.reset() this.relaunch() }) this.on('application:check-for-updates', () => { this.updateManager.check() }) this.on('application:change-theme', (theme) => { this.themeManager.updateSystemTheme(theme) this.sendCommandToAll('application:update-theme', { theme }) }) this.on('application:change-locale', (locale) => { this.localeManager.changeLanguageByLocale(locale) .then(() => { this.menuManager.handleLocaleChange(locale) this.trayManager.handleLocaleChange(locale) }) }) this.on('application:toggle-dock', (visible) => { if (visible) { this.dockManager.show() } else { this.dockManager.hide() // Hiding the dock icon will trigger the entire app to hide. this.show() } }) this.on('application:auto-hide-window', (hide) => { if (hide) { this.windowManager.handleWindowBlur() } else { this.windowManager.unbindWindowBlur() } }) this.on('application:change-menu-states', (visibleStates, enabledStates, checkedStates) => { this.menuManager.updateMenuStates(visibleStates, enabledStates, checkedStates) this.trayManager.updateMenuStates(visibleStates, enabledStates, checkedStates) }) this.on('application:open-file', (event) => { dialog.showOpenDialog({ properties: ['openFile'], filters: [ { name: 'Torrent', extensions: ['torrent'] } ] }).then(({ canceled, filePaths }) => { if (canceled || filePaths.length === 0) { return } const [filePath] = filePaths this.handleFile(filePath) }) }) this.on('application:clear-recent-tasks', () => { app.clearRecentDocuments() }) this.on('application:setup-protocols-client', (protocols) => { if (is.dev() || is.mas() || !protocols) { return } logger.info('[Motrix] setup protocols client:', protocols) this.protocolManager.setup(protocols) }) this.on('application:open-external', (url) => { this.openExternal(url) }) this.on('application:reveal-in-folder', (data) => { const { gid, path } = data logger.info('[Motrix] application:reveal-in-folder===>', path) if (path) { showItemInFolder(path) } if (gid) { this.sendCommandToAll('application:show-task-detail', { gid }) } }) this.on('help:official-website', () => { const url = 'https://motrix.app/' this.openExternal(url) }) this.on('help:manual', () => { const url = 'https://motrix.app/manual' this.openExternal(url) }) this.on('help:release-notes', () => { const url = 'https://motrix.app/release' this.openExternal(url) }) this.on('help:report-problem', () => { const url = 'https://motrix.app/report' this.openExternal(url) }) } openExternal (url) { if (!url) { return } shell.openExternal(url) } handleConfigChange (configName) { this.sendCommandToAll('application:update-preference-config', { configName }) } handleEvents () { this.once('application:initialized', () => { this.autoSyncTrackers() this.autoResumeTask() this.adjustMenu() }) this.configManager.userConfig.onDidAnyChange(() => this.handleConfigChange('user')) this.configManager.systemConfig.onDidAnyChange(() => this.handleConfigChange('system')) this.watchOpenAtLoginChange() this.watchProtocolsChange() this.watchRunModeChange() this.watchShowProgressBarChange() this.watchProxyChange() this.watchLocaleChange() this.watchThemeChange() this.on('download-status-change', (downloading) => { this.trayManager.handleDownloadStatusChange(downloading) if (downloading) { this.energyManager.startPowerSaveBlocker() } else { this.energyManager.stopPowerSaveBlocker() } }) this.on('speed-change', (speed) => { this.dockManager.handleSpeedChange(speed) this.trayManager.handleSpeedChange(speed) }) this.on('task-download-complete', (task, path) => { this.dockManager.openDock(path) if (is.linux()) { return } app.addRecentDocument(path) }) if (this.configManager.userConfig.get('show-progress-bar')) { this.bindProgressChange() } } handleProgressChange (progress) { if (this.updateManager.isChecking) { return } if (!is.windows() && progress === 2) { progress = 0 } this.windowManager.getWindow('index').setProgressBar(progress) } bindProgressChange () { if (this.listeners('progress-change').length > 0) { return } this.on('progress-change', this.handleProgressChange) } unbindProgressChange () { if (this.listeners('progress-change').length === 0) { return } this.off('progress-change', this.handleProgressChange) this.windowManager.getWindow('index').setProgressBar(-1) } handleIpcMessages () { ipcMain.on('command', (event, command, ...args) => { logger.log('[Motrix] ipc receive command', command, ...args) this.emit(command, ...args) }) ipcMain.on('event', (event, eventName, ...args) => { logger.log('[Motrix] ipc receive event', eventName, ...args) this.emit(eventName, ...args) }) } handleIpcInvokes () { ipcMain.handle('get-app-config', async () => { const systemConfig = this.configManager.getSystemConfig() const userConfig = this.configManager.getUserConfig() const context = this.context.get() const result = { ...systemConfig, ...userConfig, ...context } return result }) } } ================================================ FILE: src/main/Launcher.js ================================================ import { EventEmitter } from 'node:events' import { app } from 'electron' import is from 'electron-is' import ExceptionHandler from './core/ExceptionHandler' import logger from './core/Logger' import Application from './Application' import { splitArgv, parseArgvAsUrl, parseArgvAsFile } from './utils' import { EMPTY_STRING } from '@shared/constants' export default class Launcher extends EventEmitter { constructor () { super() this.url = EMPTY_STRING this.file = EMPTY_STRING this.makeSingleInstance(() => { this.init() }) } makeSingleInstance (callback) { // Mac App Store Sandboxed App not support requestSingleInstanceLock if (is.mas()) { callback && callback() return } const gotSingleLock = app.requestSingleInstanceLock() if (!gotSingleLock) { app.quit() } else { app.on('second-instance', (event, argv, workingDirectory) => { global.application.showPage('index') if (!is.macOS() && argv.length > 1) { this.handleAppLaunchArgv(argv) } }) callback && callback() } } init () { this.exceptionHandler = new ExceptionHandler() this.openedAtLogin = is.macOS() ? app.getLoginItemSettings().wasOpenedAtLogin : false if (process.argv.length > 1) { this.handleAppLaunchArgv(process.argv) } logger.info('[Motrix] openedAtLogin:', this.openedAtLogin) this.handleAppEvents() } handleAppEvents () { this.handleRendererRemote() this.handleOpenUrl() this.handleOpenFile() this.handelAppReady() this.handleAppWillQuit() } handleRendererRemote () { app.on('browser-window-created', (_, window) => { require('@electron/remote/main').enable(window.webContents) }) } /** * handleOpenUrl * Event 'open-url' macOS only * "name": "Motrix Protocol", * "schemes": ["mo", "motrix"] */ handleOpenUrl () { if (is.mas() || !is.macOS()) { return } app.on('open-url', (event, url) => { logger.info(`[Motrix] open-url: ${url}`) event.preventDefault() this.url = url this.sendUrlToApplication() }) } /** * handleOpenFile * Event 'open-file' macOS only * handle open torrent file */ handleOpenFile () { if (!is.macOS()) { return } app.on('open-file', (event, path) => { logger.info(`[Motrix] open-file: ${path}`) event.preventDefault() this.file = path this.sendFileToApplication() }) } /** * handleAppLaunchArgv * For Windows, Linux * @param {array} argv */ handleAppLaunchArgv (argv) { logger.info('[Motrix] handleAppLaunchArgv:', argv) // args: array, extra: map const { args, extra } = splitArgv(argv) logger.info('[Motrix] split argv args:', args) logger.info('[Motrix] split argv extra:', extra) if (extra['--opened-at-login'] === '1') { this.openedAtLogin = true } const file = parseArgvAsFile(args) if (file) { this.file = file this.sendFileToApplication() } const url = parseArgvAsUrl(args) if (url) { this.url = url this.sendUrlToApplication() } } sendUrlToApplication () { if (this.url && global.application && global.application.isReady) { global.application.handleProtocol(this.url) this.url = EMPTY_STRING } } sendFileToApplication () { if (this.file && global.application && global.application.isReady) { global.application.handleFile(this.file) this.file = EMPTY_STRING } } handelAppReady () { app.on('ready', () => { global.application = new Application() const { openedAtLogin } = this global.application.start('index', { openedAtLogin }) global.application.on('ready', () => { this.sendUrlToApplication() this.sendFileToApplication() }) }) app.on('activate', () => { if (global.application) { logger.info('[Motrix] activate') global.application.showPage('index') } }) } handleAppWillQuit () { app.on('will-quit', () => { logger.info('[Motrix] will-quit') if (global.application) { logger.info('[Motrix] will-quit.application.stop') global.application.stop() } }) } } ================================================ FILE: src/main/configs/engine.js ================================================ export const engineBinMap = { darwin: 'aria2c', win32: 'aria2c.exe', linux: 'aria2c' } export const engineArchMap = { darwin: { x64: 'x64', arm64: 'arm64' }, win32: { ia32: 'ia32', x64: 'x64', arm64: 'x64' }, linux: { x64: 'x64', arm: 'armv7l', arm64: 'arm64' } } ================================================ FILE: src/main/configs/page.js ================================================ import is from 'electron-is' export default { index: { attrs: { title: 'Motrix', width: 1024, height: 768, minWidth: 478, minHeight: 420, transparent: is.macOS() }, bindCloseToHide: true, openDevTools: is.dev(), url: is.dev() ? 'http://localhost:9080' : require('path').join('file://', __dirname, '/index.html') } } ================================================ FILE: src/main/configs/protocol.js ================================================ /* eslint quote-props: ["error", "always"] */ export default { 'task-list': 'application:task-list', 'new-task': 'application:new-task', 'new-bt-task': 'application:new-bt-task', 'pause-all-task': 'application:pause-all-task', 'resume-all-task': 'application:resume-all-task', 'reveal-in-folder': 'application:reveal-in-folder', 'preferences': 'application:preferences', 'about': 'application:about' } ================================================ FILE: src/main/core/AutoLaunchManager.js ================================================ import { app } from 'electron' import { LOGIN_SETTING_OPTIONS } from '@shared/constants' export default class AutoLaunchManager { enable () { return new Promise((resolve, reject) => { const enabled = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin if (enabled) { resolve() } app.setLoginItemSettings({ ...LOGIN_SETTING_OPTIONS, openAtLogin: true }) resolve() }) } disable () { return new Promise((resolve, reject) => { app.setLoginItemSettings({ openAtLogin: false }) resolve() }) } isEnabled () { return new Promise((resolve, reject) => { const enabled = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin resolve(enabled) }) } } ================================================ FILE: src/main/core/ConfigManager.js ================================================ import { app } from 'electron' import is from 'electron-is' import Store from 'electron-store' import { getConfigBasePath, getDhtPath, getMaxConnectionPerServer, getUserDownloadsPath } from '../utils/index' import { APP_RUN_MODE, APP_THEME, EMPTY_STRING, ENGINE_RPC_PORT, IP_VERSION, LOGIN_SETTING_OPTIONS, NGOSANG_TRACKERS_BEST_IP_URL_CDN, NGOSANG_TRACKERS_BEST_URL_CDN, PROXY_SCOPES, PROXY_SCOPE_OPTIONS } from '@shared/constants' import { CHROME_UA } from '@shared/ua' import { separateConfig } from '@shared/utils' import { reduceTrackerString } from '@shared/utils/tracker' export default class ConfigManager { constructor () { this.systemConfig = {} this.userConfig = {} this.init() } init () { this.initUserConfig() this.initSystemConfig() } /** * Aria2 Configuration Priority * system.json > built-in aria2.conf * https://aria2.github.io/manual/en/html/aria2c.html * */ initSystemConfig () { this.systemConfig = new Store({ name: 'system', cwd: getConfigBasePath(), /* eslint-disable quote-props */ defaults: { 'all-proxy': EMPTY_STRING, 'allow-overwrite': false, 'auto-file-renaming': true, 'bt-exclude-tracker': EMPTY_STRING, 'bt-force-encryption': false, 'bt-load-saved-metadata': true, 'bt-save-metadata': true, 'bt-tracker': EMPTY_STRING, 'continue': true, 'dht-file-path': getDhtPath(IP_VERSION.V4), 'dht-file-path6': getDhtPath(IP_VERSION.V6), 'dht-listen-port': 26701, 'dir': getUserDownloadsPath(), 'enable-dht6': true, 'follow-metalink': true, 'follow-torrent': true, 'listen-port': 21301, 'max-concurrent-downloads': 5, 'max-connection-per-server': getMaxConnectionPerServer(), 'max-download-limit': 0, 'max-overall-download-limit': 0, 'max-overall-upload-limit': 0, 'no-proxy': EMPTY_STRING, 'pause-metadata': false, 'pause': true, 'rpc-listen-port': ENGINE_RPC_PORT, 'rpc-secret': EMPTY_STRING, 'seed-ratio': 2, 'seed-time': 2880, 'split': getMaxConnectionPerServer(), 'user-agent': CHROME_UA } /* eslint-enable quote-props */ }) this.fixSystemConfig() } initUserConfig () { this.userConfig = new Store({ name: 'user', cwd: getConfigBasePath(), // Schema need electron-store upgrade to 3.x.x, // but it will cause the application build to fail. // schema: { // theme: { // type: 'string', // enum: ['auto', 'light', 'dark'] // } // }, /* eslint-disable quote-props */ defaults: { 'auto-check-update': is.macOS(), 'auto-hide-window': false, 'auto-sync-tracker': true, 'enable-upnp': true, 'engine-max-connection-per-server': getMaxConnectionPerServer(), 'favorite-directories': [], 'hide-app-menu': is.windows() || is.linux(), 'history-directories': [], 'keep-seeding': false, 'keep-window-state': false, 'last-check-update-time': 0, 'last-sync-tracker-time': 0, 'locale': app.getLocale(), 'log-level': 'warn', 'new-task-show-downloading': true, 'no-confirm-before-delete-task': false, 'open-at-login': false, 'protocols': { 'magnet': true, 'thunder': false }, 'proxy': { 'enable': false, 'server': EMPTY_STRING, 'bypass': EMPTY_STRING, 'scope': PROXY_SCOPE_OPTIONS }, 'resume-all-when-app-launched': false, 'run-mode': APP_RUN_MODE.STANDARD, 'show-progress-bar': true, 'task-notification': true, 'theme': APP_THEME.AUTO, 'tracker-source': [ NGOSANG_TRACKERS_BEST_IP_URL_CDN, NGOSANG_TRACKERS_BEST_URL_CDN ], 'tray-theme': APP_THEME.AUTO, 'tray-speedometer': is.macOS(), 'update-channel': 'latest', 'window-state': {} } /* eslint-enable quote-props */ }) this.fixUserConfig() } fixSystemConfig () { // Remove aria2c unrecognized options const { others } = separateConfig(this.systemConfig.store) if (others && Object.keys(others).length > 0) { Object.keys(others).forEach(key => { this.systemConfig.delete(key) }) } const proxy = this.getUserConfig('proxy', { enable: false }) const { enable, server, bypass, scope = [] } = proxy if (enable && server && scope.includes(PROXY_SCOPES.DOWNLOAD)) { this.setSystemConfig('all-proxy', server) this.setSystemConfig('no-proxy', bypass) } // Fix spawn ENAMETOOLONG on Windows const tracker = reduceTrackerString(this.systemConfig.get('bt-tracker')) this.setSystemConfig('bt-tracker', tracker) } fixUserConfig () { // Fix the value of open-at-login when the user delete // the Motrix self-starting item through startup management. const openAtLogin = app.getLoginItemSettings(LOGIN_SETTING_OPTIONS).openAtLogin if (this.getUserConfig('open-at-login') !== openAtLogin) { this.setUserConfig('open-at-login', openAtLogin) } if (this.getUserConfig('tracker-source').length === 0) { this.setUserConfig('tracker-source', [ NGOSANG_TRACKERS_BEST_IP_URL_CDN, NGOSANG_TRACKERS_BEST_URL_CDN ]) } } getSystemConfig (key, defaultValue) { if (typeof key === 'undefined' && typeof defaultValue === 'undefined') { return this.systemConfig.store } return this.systemConfig.get(key, defaultValue) } getUserConfig (key, defaultValue) { if (typeof key === 'undefined' && typeof defaultValue === 'undefined') { return this.userConfig.store } return this.userConfig.get(key, defaultValue) } getLocale () { return this.getUserConfig('locale') || app.getLocale() } setSystemConfig (...args) { this.systemConfig.set(...args) } setUserConfig (...args) { this.userConfig.set(...args) } reset () { this.systemConfig.clear() this.userConfig.clear() } } ================================================ FILE: src/main/core/Context.js ================================================ import logger from './Logger' import { getEnginePath, getAria2BinPath, getAria2ConfPath, getSessionPath } from '../utils' const { platform, arch } = process export default class Context { constructor () { this.init() } getLogPath () { const { path } = logger.transports.file.getFile() return path } init () { // The key of Context cannot be the same as that of userConfig and systemConfig. this.context = { platform: platform, arch: arch, 'log-path': this.getLogPath(), 'session-path': getSessionPath(), 'engine-path': getEnginePath(platform, arch), 'aria2-bin-path': getAria2BinPath(platform, arch), 'aria2-conf-path': getAria2ConfPath(platform, arch) } logger.info('[Motrix] Context.init===>', this.context) } get (key) { if (typeof key === 'undefined') { return this.context } return this.context[key] } } ================================================ FILE: src/main/core/EnergyManager.js ================================================ import { powerSaveBlocker } from 'electron' import logger from './Logger' let psbId export default class EnergyManager { startPowerSaveBlocker () { logger.info('[Motrix] EnergyManager.startPowerSaveBlocker', psbId) if (psbId && powerSaveBlocker.isStarted(psbId)) { return } psbId = powerSaveBlocker.start('prevent-app-suspension') logger.info('[Motrix] start power save blocker:', psbId) } stopPowerSaveBlocker () { logger.info('[Motrix] EnergyManager.stopPowerSaveBlocker', psbId) if (typeof psbId === 'undefined' || !powerSaveBlocker.isStarted(psbId)) { return } powerSaveBlocker.stop(psbId) logger.info('[Motrix] stop power save blocker:', psbId) psbId = undefined } } ================================================ FILE: src/main/core/Engine.js ================================================ import { spawn } from 'node:child_process' import { existsSync, writeFile, unlink } from 'node:fs' import is from 'electron-is' import logger from './Logger' import { getI18n } from '../ui/Locale' import { getEnginePidPath, getAria2BinPath, getAria2ConfPath, getSessionPath, transformConfig } from '../utils/index' const { platform, arch } = process export default class Engine { // ChildProcess | null static instance = null constructor (options = {}) { this.options = options this.i18n = getI18n() this.systemConfig = options.systemConfig this.userConfig = options.userConfig } start () { const pidPath = getEnginePidPath() logger.info('[Motrix] Engie pid path:', pidPath) if (this.instance) { return } const binPath = this.getEngineBinPath() const args = this.getStartArgs() this.instance = spawn(binPath, args, { windowsHide: false, stdio: is.dev() ? 'pipe' : 'ignore' }) const pid = this.instance.pid.toString() this.writePidFile(pidPath, pid) this.instance.once('close', () => { try { unlink(pidPath, (err) => { if (err) { logger.warn(`[Motrix] Unlink engine process pid file failed: ${err}`) } }) } catch (err) { logger.warn(`[Motrix] Unlink engine process pid file failed: ${err}`) } }) if (is.dev()) { this.instance.stdout.on('data', (data) => { logger.log('[Motrix] engine stdout===>', data.toString()) }) this.instance.stderr.on('data', (data) => { logger.log('[Motrix] engine stderr===>', data.toString()) }) } } stop () { logger.info('[Motrix] engine.stop.instance') if (this.instance) { this.instance.kill() this.instance = null } } writePidFile (pidPath, pid) { writeFile(pidPath, pid, (err) => { if (err) { logger.error(`[Motrix] Write engine process pid failed: ${err}`) } }) } getEngineBinPath () { const result = getAria2BinPath(platform, arch) const binIsExist = existsSync(result) if (!binIsExist) { logger.error('[Motrix] engine bin is not exist:', result) throw new Error(this.i18n.t('app.engine-missing-message')) } return result } getStartArgs () { const confPath = getAria2ConfPath(platform, arch) const sessionPath = getSessionPath() const sessionIsExist = existsSync(sessionPath) let result = [`--conf-path=${confPath}`, `--save-session=${sessionPath}`] if (sessionIsExist) { result = [...result, `--input-file=${sessionPath}`] } const extraConfig = { ...this.systemConfig } const keepSeeding = this.userConfig['keep-seeding'] const seedRatio = this.systemConfig['seed-ratio'] if (keepSeeding || seedRatio === 0) { extraConfig['seed-ratio'] = 0 delete extraConfig['seed-time'] } console.log('extraConfig===>', extraConfig) const extra = transformConfig(extraConfig) result = [...result, ...extra] return result } isRunning (pid) { try { return process.kill(pid, 0) } catch (e) { return e.code === 'EPERM' } } restart () { this.stop() this.start() } } ================================================ FILE: src/main/core/EngineClient.js ================================================ 'use strict' import { Aria2 } from '@shared/aria2' import logger from './Logger' import { compactUndefined, formatOptionsForEngine } from '@shared/utils' import { ENGINE_RPC_HOST, ENGINE_RPC_PORT, EMPTY_STRING } from '@shared/constants' const defaults = { host: ENGINE_RPC_HOST, port: ENGINE_RPC_PORT, secret: EMPTY_STRING } export default class EngineClient { static instance = null static client = null constructor (options = {}) { this.options = { ...defaults, ...options } this.init() } init () { this.connect() } connect () { logger.info('[Motrix] main engine client connect', this.options) const { host, port, secret } = this.options this.client = new Aria2({ host, port, secret }) } async call (method, ...args) { return this.client.call(method, ...args).catch((err) => { logger.warn('[Motrix] call client fail:', err.message) }) } async changeGlobalOption (options) { logger.info('[Motrix] change engine global option:', options) const args = formatOptionsForEngine(options) return this.call('changeGlobalOption', args) } async shutdown (options = {}) { const { force = false } = options const { secret } = this.options const method = force ? 'forceShutdown' : 'shutdown' const args = compactUndefined([secret]) return this.call(method, ...args) } } ================================================ FILE: src/main/core/ExceptionHandler.js ================================================ import { app, dialog } from 'electron' import is from 'electron-is' import logger from './Logger' const defaults = { showDialog: !is.dev() } export default class ExceptionHandler { constructor (options) { this.options = { ...defaults, ...options } this.setup() } setup () { if (is.dev()) { return } const { showDialog } = this.options process.on('uncaughtException', (err) => { const { message, stack } = err logger.error(`[Motrix] Uncaught exception: ${message}`) logger.error(stack) if (showDialog && app.isReady()) { dialog.showErrorBox('Error: ', message) } }) } } ================================================ FILE: src/main/core/Logger.js ================================================ import { join } from 'node:path' import is from 'electron-is' import logger from 'electron-log' import { IS_PORTABLE, PORTABLE_EXECUTABLE_DIR } from '@shared/constants' const level = is.production() ? 'info' : 'silly' logger.transports.file.level = level if (IS_PORTABLE) { logger.transports.file.resolvePath = () => join(PORTABLE_EXECUTABLE_DIR, 'main.log') } logger.info('[Motrix] Logger init') logger.warn('[Motrix] Logger init') export default logger ================================================ FILE: src/main/core/ProtocolManager.js ================================================ import { EventEmitter } from 'node:events' import { app } from 'electron' import is from 'electron-is' import { parse } from 'querystring' import logger from './Logger' import protocolMap from '../configs/protocol' import { ADD_TASK_TYPE } from '@shared/constants' export default class ProtocolManager extends EventEmitter { constructor (options = {}) { super() this.options = options // package.json:build.protocols[].schemes[] // options.protocols: { 'magnet': true, 'thunder': false } this.protocols = { mo: true, motrix: true, ...options.protocols } this.init() } init () { const { protocols } = this this.setup(protocols) } setup (protocols = {}) { if (is.dev() || is.mas()) { return } Object.keys(protocols).forEach((protocol) => { const enabled = protocols[protocol] if (enabled) { if (!app.isDefaultProtocolClient(protocol)) { app.setAsDefaultProtocolClient(protocol) } } else { app.removeAsDefaultProtocolClient(protocol) } }) } handle (url) { logger.info(`[Motrix] protocol url: ${url}`) if ( url.toLowerCase().startsWith('ftp:') || url.toLowerCase().startsWith('http:') || url.toLowerCase().startsWith('https:') || url.toLowerCase().startsWith('magnet:') || url.toLowerCase().startsWith('thunder:') ) { return this.handleResourceProtocol(url) } if ( url.toLowerCase().startsWith('mo:') || url.toLowerCase().startsWith('motrix:') ) { return this.handleMoProtocol(url) } } handleResourceProtocol (url) { if (!url) { return } global.application.sendCommandToAll('application:new-task', { type: ADD_TASK_TYPE.URI, uri: url }) } handleMoProtocol (url) { const parsed = new URL(url) const { host, search } = parsed logger.info('[Motrix] protocol parsed:', parsed, host) const command = protocolMap[host] if (!command) { return } const query = search.startsWith('?') ? search.replace('?', '') : search const args = parse(query) global.application.sendCommandToAll(command, args) } } ================================================ FILE: src/main/core/UPnPManager.js ================================================ import NatAPI from '@motrix/nat-api' import logger from './Logger' let client = null const mappingStatus = {} export default class UPnPManager { constructor (options = {}) { this.options = { ...options } } init () { if (client) { return } client = new NatAPI({ autoUpdate: true }) } map (port) { this.init() return new Promise((resolve, reject) => { logger.info('[Motrix] UPnPManager port mapping: ', port) if (!port) { reject(new Error('[Motrix] port was not specified')) return } try { client.map(port, (err) => { if (err) { logger.warn(`[Motrix] UPnPManager map ${port} failed, error: `, err.message) reject(err.message) return } mappingStatus[port] = true logger.info(`[Motrix] UPnPManager port ${port} mapping succeeded`) resolve() }) } catch (err) { reject(err.message) } }) } unmap (port) { this.init() return new Promise((resolve, reject) => { logger.info('[Motrix] UPnPManager port unmapping: ', port) if (!port) { reject(new Error('[Motrix] port was not specified')) return } if (!mappingStatus[port]) { resolve() return } try { client.unmap(port, (err) => { if (err) { logger.warn(`[Motrix] UPnPManager unmap ${port} failed, error: `, err) reject(err.message) return } logger.info(`[Motrix] UPnPManager port ${port} unmapping succeeded`) mappingStatus[port] = false resolve() }) } catch (err) { reject(err.message) } }) } closeClient () { if (!client) { return } try { client.destroy(() => { client = null }) } catch (err) { logger.warn('[Motrix] close UPnP client fail', err) } } } ================================================ FILE: src/main/core/UpdateManager.js ================================================ import { EventEmitter } from 'node:events' import { resolve } from 'node:path' import { dialog } from 'electron' import is from 'electron-is' import { autoUpdater } from 'electron-updater' import { PROXY_SCOPES } from '@shared/constants' import logger from './Logger' import { getI18n } from '../ui/Locale' if (is.dev()) { autoUpdater.updateConfigPath = resolve(__dirname, '../../../app-update.yml') } export default class UpdateManager extends EventEmitter { constructor (options = {}) { super() this.options = options this.i18n = getI18n() this.isChecking = false this.updater = autoUpdater this.updater.autoDownload = false this.updater.autoInstallOnAppQuit = false this.updater.logger = logger logger.info('[Motrix] setup proxy:', this.options.proxy) this.setupProxy(this.options.proxy) this.autoCheckData = { checkEnable: this.options.autoCheck, userCheck: false } this.init() } setupProxy (proxy) { const { enable, server, scope = [] } = proxy if (!enable || !server || !scope.includes(PROXY_SCOPES.UPDATE_APP)) { this.updater.netSession.setProxy({ proxyRules: undefined }) return } const url = new URL(server) const { username, password, protocol = 'http:', host, port } = url const proxyRules = `${protocol}//${host}` logger.info(`[Motrix] setup proxy: ${proxyRules}`, username, password, protocol, host, port) this.updater.netSession.setProxy({ proxyRules }) if (server.includes('@')) { this.updater.signals.login((_authInfo, callback) => { callback(username, password) }) } } init () { // Event: error // Event: checking-for-update // Event: update-available // Event: update-not-available // Event: download-progress // Event: update-downloaded this.updater.on('checking-for-update', this.checkingForUpdate.bind(this)) this.updater.on('update-available', this.updateAvailable.bind(this)) this.updater.on('update-not-available', this.updateNotAvailable.bind(this)) this.updater.on('download-progress', this.updateDownloadProgress.bind(this)) this.updater.on('update-downloaded', this.updateDownloaded.bind(this)) this.updater.on('update-cancelled', this.updateCancelled.bind(this)) this.updater.on('error', this.updateError.bind(this)) if (this.autoCheckData.checkEnable && !this.isChecking) { this.autoCheckData.userCheck = false this.updater.checkForUpdates() } } check () { this.autoCheckData.userCheck = true this.updater.checkForUpdates() } checkingForUpdate () { this.isChecking = true this.emit('checking') } updateAvailable (event, info) { this.emit('update-available', info) dialog.showMessageBox({ type: 'info', title: this.i18n.t('app.check-for-updates-title'), message: this.i18n.t('app.update-available-message'), buttons: [this.i18n.t('app.yes'), this.i18n.t('app.no')], cancelId: 1 }).then(({ response }) => { if (response === 0) { this.updater.downloadUpdate() } else { this.emit('update-cancelled', info) } }) } updateNotAvailable (event, info) { this.isChecking = false this.emit('update-not-available', info) if (this.autoCheckData.userCheck) { dialog.showMessageBox({ title: this.i18n.t('app.check-for-updates-title'), message: this.i18n.t('app.update-not-available-message') }) } } /** * autoUpdater:download-progress * @param {Object} event * progress, * bytesPerSecond, * percent, * total, * transferred */ updateDownloadProgress (event) { this.emit('download-progress', event) } updateDownloaded (event, info) { this.emit('update-downloaded', info) this.updater.logger.log(`Update Downloaded: ${info}`) dialog.showMessageBox({ title: this.i18n.t('app.check-for-updates-title'), message: this.i18n.t('app.update-downloaded-message') }).then(_ => { this.isChecking = false this.emit('will-updated') setTimeout(() => { this.updater.quitAndInstall() }, 200) }) } updateCancelled () { this.isChecking = false } updateError (event, error) { this.isChecking = false this.emit('update-error', error) const msg = (error == null) ? this.i18n.t('app.update-error-message') : (error.stack || error).toString() this.updater.logger.warn(`[Motrix] update-error: ${msg}`) dialog.showErrorBox('Error', msg) } } ================================================ FILE: src/main/index.dev.js ================================================ /** * This file is used specifically and only for development. It installs * `electron-debug` & `vue-devtools`. There shouldn't be any need to * modify this file, but it can be used to extend your development * environment. */ /* eslint-disable */ // Install `vue-devtools` require('electron').app.whenReady().then(() => { let installExtension = require('electron-devtools-installer') installExtension.default(installExtension.VUEJS_DEVTOOLS) .then(() => {}) .catch(err => { console.log('Unable to install `vue-devtools`: \n', err) }) }) // Require `main` process to boot app require('./index') ================================================ FILE: src/main/index.js ================================================ import { app } from 'electron' import is from 'electron-is' import { initialize } from '@electron/remote/main' import Launcher from './Launcher' /** * initialize the main-process side of the remote module */ initialize() process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' if (process.env.NODE_ENV !== 'development') { global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\') } /** * Fix Windows notification func * appId defined in .electron-vue/webpack.main.config.js */ if (is.windows()) { app.setAppUserModelId(appId) } global.launcher = new Launcher() ================================================ FILE: src/main/menus/darwin.json ================================================ { "menu": [ { "id": "menu.app", "submenu": [ { "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.preferences", "command": "application:preferences" }, { "id": "app.check-for-updates", "command": "application:check-for-updates" }, { "id": "app.hide", "role": "hide" }, { "id": "app.hide-others", "role": "hideothers" }, { "id": "app.unhide", "role": "unhide" }, { "type": "separator" }, { "id": "app.quit", "role": "quit" } ] }, { "id": "menu.task", "submenu": [ { "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" }, { "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" }, { "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.task-list", "command": "application:task-list" }, { "id": "task.pause-task", "command": "application:pause-task" }, { "id": "task.resume-task", "command": "application:resume-task" }, { "id": "task.delete-task", "command": "application:delete-task" }, { "id": "task.move-task-up", "command": "application:move-task-up" }, { "id": "task.move-task-down", "command": "application:move-task-down" }, { "type": "separator" }, { "id": "task.pause-all-task", "command": "application:pause-all-task" }, { "id": "task.resume-all-task", "command": "application:resume-all-task" }, { "id": "task.select-all-task", "command": "application:select-all-task" }, { "type": "separator" }, { "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" } ] }, { "id": "menu.edit", "submenu": [ { "id": "edit.undo", "role": "undo" }, { "id": "edit.redo", "role": "redo" }, { "type": "separator" }, { "id": "edit.cut", "role": "cut" }, { "id": "edit.copy", "role": "copy" }, { "id": "edit.paste", "role": "paste" }, { "id": "edit.delete", "role": "delete" }, { "id": "edit.select-all", "role": "selectall" } ] }, { "role": "window", "id": "menu.window", "submenu": [ { "id": "window.reload", "role": "reload" }, { "id": "window.close", "role": "close" }, { "id": "window.minimize", "role": "minimize" }, { "id": "window.zoom", "role": "zoom" }, { "id": "window.toggle-fullscreen", "role": "togglefullscreen" }, { "type": "separator" }, { "id": "window.front", "role": "front" } ] }, { "role": "help", "id": "menu.help", "submenu": [ { "id": "help.official-website", "command": "help:official-website" }, { "id": "help.manual", "command": "help:manual" }, { "id": "help.release-notes", "command": "help:release-notes" }, { "type": "separator" }, { "id": "help.report-problem", "command": "help:report-problem" }, { "type": "separator" }, { "id": "help.toggle-dev-tools", "role": "toggledevtools" } ] } ] } ================================================ FILE: src/main/menus/linux.json ================================================ { "menu": [ { "id": "menu.file", "submenu": [ { "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.preferences", "command": "application:preferences" }, { "id": "app.check-for-updates", "command": "application:check-for-updates" }, { "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } }, { "type": "separator" }, { "id": "app.quit", "role": "quit" } ] }, { "id": "menu.task", "submenu": [ { "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" }, { "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" }, { "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.task-list", "command": "application:task-list" }, { "id": "task.pause-task", "command": "application:pause-task" }, { "id": "task.resume-task", "command": "application:resume-task" }, { "id": "task.delete-task", "command": "application:delete-task" }, { "id": "task.move-task-up", "command": "application:move-task-up" }, { "id": "task.move-task-down", "command": "application:move-task-down" }, { "type": "separator" }, { "id": "task.pause-all-task", "command": "application:pause-all-task" }, { "id": "task.resume-all-task", "command": "application:resume-all-task" }, { "id": "task.select-all-task", "command": "application:select-all-task" } ] }, { "id": "menu.edit", "submenu": [ { "id": "edit.undo", "role": "undo" }, { "id": "edit.redo", "role": "redo" }, { "type": "separator" }, { "id": "edit.cut", "role": "cut" }, { "id": "edit.copy", "role": "copy" }, { "id": "edit.paste", "role": "paste" }, { "id": "edit.delete", "role": "delete" }, { "id": "edit.select-all", "role": "selectall" } ] }, { "role": "window", "id": "menu.window", "submenu": [ { "id": "window.reload", "role": "reload" }, { "id": "window.close", "role": "close" }, { "id": "window.minimize", "role": "minimize" }, { "id": "window.zoom", "role": "zoom" }, { "id": "window.toggle-fullscreen", "role": "togglefullscreen" }, { "type": "separator" }, { "id": "window.front", "role": "front" } ] }, { "role": "help", "id": "menu.help", "submenu": [ { "id": "help.official-website", "command": "help:official-website" }, { "id": "help.manual", "command": "help:manual" }, { "id": "help.release-notes", "command": "help:release-notes" }, { "type": "separator" }, { "id": "help.report-problem", "command": "help:report-problem" }, { "type": "separator" }, { "id": "help.toggle-dev-tools", "role": "toggledevtools" } ] } ] } ================================================ FILE: src/main/menus/touchBar.json ================================================ [ { "type": "button", "icon": "new-task", "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" }, { "type": "spacer", "size": "small" }, { "type": "group", "id": "task.task-list", "items": [ { "type": "button", "icon": "task-active", "command": "application:task-list", "command-arg": { "status": "active" } }, { "type": "button", "icon": "task-waiting", "command": "application:task-list", "command-arg": { "status": "waiting" } }, { "type": "button", "icon": "task-stopped", "command": "application:task-list", "command-arg": { "status": "stopped" } } ] }, { "type": "spacer", "size": "large" }, { "type": "button", "icon": "preferences", "id": "app.preferences", "command": "application:preferences" }, { "type": "spacer", "size": "small" }, { "type": "button", "icon": "about", "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" } ] ================================================ FILE: src/main/menus/tray.json ================================================ [ { "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" }, { "id": "task.new-bt-task", "command": "application:new-bt-task", "command-arg": { "type": "torrent" }, "command-after": "application:show?page=index" }, { "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } }, { "id": "help.manual", "command": "help:manual" }, { "id": "app.check-for-updates", "command": "application:check-for-updates" }, { "type": "separator" }, { "id": "app.task-list", "command": "application:task-list", "command-before": "application:show?page=index" }, { "id": "app.preferences", "command": "application:preferences", "command-before": "application:show?page=index" }, { "id": "app.quit", "command": "application:quit" } ] ================================================ FILE: src/main/menus/win32.json ================================================ { "menu": [ { "id": "menu.file", "submenu": [ { "id": "app.about", "command": "application:about", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.preferences", "command": "application:preferences" }, { "id": "app.check-for-updates", "command": "application:check-for-updates" }, { "id": "app.show", "command": "application:show", "command-arg": { "page": "index" } }, { "type": "separator" }, { "id": "app.quit", "role": "quit" } ] }, { "id": "menu.task", "submenu": [ { "id": "task.new-task", "command": "application:new-task", "command-after": "application:show?page=index" }, { "id": "task.new-bt-task", "command": "application:new-bt-task", "command-after": "application:show?page=index" }, { "id": "task.open-file", "command": "application:open-file", "command-before": "application:show?page=index" }, { "type": "separator" }, { "id": "app.task-list", "command": "application:task-list" }, { "id": "task.pause-task", "command": "application:pause-task" }, { "id": "task.resume-task", "command": "application:resume-task" }, { "id": "task.delete-task", "command": "application:delete-task" }, { "id": "task.move-task-up", "command": "application:move-task-up" }, { "id": "task.move-task-down", "command": "application:move-task-down" }, { "type": "separator" }, { "id": "task.pause-all-task", "command": "application:pause-all-task" }, { "id": "task.resume-all-task", "command": "application:resume-all-task" }, { "id": "task.select-all-task", "command": "application:select-all-task" }, { "type": "separator" }, { "id": "task.clear-recent-tasks", "command": "application:clear-recent-tasks" } ] }, { "id": "menu.edit", "submenu": [ { "id": "edit.undo", "role": "undo" }, { "id": "edit.redo", "role": "redo" }, { "type": "separator" }, { "id": "edit.cut", "role": "cut" }, { "id": "edit.copy", "role": "copy" }, { "id": "edit.paste", "role": "paste" }, { "id": "edit.delete", "role": "delete" }, { "id": "edit.select-all", "role": "selectall" } ] }, { "role": "window", "id": "menu.window", "submenu": [ { "id": "window.reload", "role": "reload" }, { "id": "window.close", "role": "close" }, { "id": "window.minimize", "role": "minimize" }, { "id": "window.zoom", "role": "zoom" }, { "id": "window.toggle-fullscreen", "role": "togglefullscreen" }, { "type": "separator" }, { "id": "window.front", "role": "front" } ] }, { "role": "help", "id": "menu.help", "submenu": [ { "id": "help.official-website", "command": "help:official-website" }, { "id": "help.manual", "command": "help:manual" }, { "id": "help.release-notes", "command": "help:release-notes" }, { "type": "separator" }, { "id": "help.report-problem", "command": "help:report-problem" }, { "type": "separator" }, { "id": "help.toggle-dev-tools", "role": "toggledevtools" } ] } ] } ================================================ FILE: src/main/pages/about.html ================================================ Document ================================================ FILE: src/main/pages/index.html ================================================ Document ================================================ FILE: src/main/ui/DockManager.js ================================================ import is from 'electron-is' import { EventEmitter } from 'node:events' import { app } from 'electron' import { bytesToSize } from '@shared/utils' import { APP_RUN_MODE } from '@shared/constants' const enabled = is.macOS() export default class DockManager extends EventEmitter { constructor (options) { super() this.options = options const { runMode } = this.options if (runMode === APP_RUN_MODE.TRAY) { this.hide() } } show = enabled ? () => { if (app.dock.isVisible()) { return } return app.dock.show() } : () => {} hide = enabled ? () => { if (!app.dock.isVisible()) { return } app.dock.hide() } : () => {} // macOS setBadge not working // @see https://github.com/electron/electron/issues/25745#issuecomment-702826143 setBadge = enabled ? (text) => { app.dock.setBadge(text) } : (text) => {} handleSpeedChange = enabled ? (speed) => { const { downloadSpeed } = speed const text = downloadSpeed > 0 ? `${bytesToSize(downloadSpeed)}/s` : '' this.setBadge(text) } : (text) => {} openDock = enabled ? (path) => { app.dock.downloadFinished(path) } : (path) => {} } ================================================ FILE: src/main/ui/Locale.js ================================================ import resources from '@shared/locales/app' import LocaleManager from '@shared/locales/LocaleManager' const localeManager = new LocaleManager({ resources }) export const getLocaleManager = () => { return localeManager } export const setupLocaleManager = (locale) => { localeManager.changeLanguageByLocale(locale) return localeManager } export const getI18n = () => { return localeManager.getI18n() } export const getI18nTranslator = () => { return localeManager.getI18n().t } ================================================ FILE: src/main/ui/MenuManager.js ================================================ import { EventEmitter } from 'node:events' import { Menu } from 'electron' import keymap from '@shared/keymap' import { translateTemplate, flattenMenuItems, updateStates } from '../utils/menu' import { getI18n } from '../ui/Locale' export default class MenuManager extends EventEmitter { constructor (options) { super() this.options = options this.i18n = getI18n() this.keymap = keymap this.items = {} this.load() this.setup() } load () { const template = require(`../menus/${process.platform}.json`) this.template = template.menu } build () { const keystrokesByCommand = {} for (const item in this.keymap) { keystrokesByCommand[this.keymap[item]] = item } // Deepclone the menu template to refresh menu const template = JSON.parse(JSON.stringify(this.template)) const tpl = translateTemplate(template, keystrokesByCommand, this.i18n) const menu = Menu.buildFromTemplate(tpl) return menu } setup () { const menu = this.build() Menu.setApplicationMenu(menu) this.items = flattenMenuItems(menu) } handleLocaleChange (locale) { this.setup() } updateMenuStates (visibleStates, enabledStates, checkedStates) { updateStates(this.items, visibleStates, enabledStates, checkedStates) } updateMenuItemVisibleState (id, flag) { const visibleStates = { [id]: flag } this.updateMenuStates(visibleStates, null, null) } updateMenuItemEnabledState (id, flag) { const enabledStates = { [id]: flag } this.updateMenuStates(null, enabledStates, null) } } ================================================ FILE: src/main/ui/ThemeManager.js ================================================ import { EventEmitter } from 'node:events' import { nativeTheme } from 'electron' import { APP_THEME } from '@shared/constants' import logger from '../core/Logger' import { getSystemTheme } from '../utils' export default class ThemeManager extends EventEmitter { constructor (options = {}) { super() this.options = options this.init() } init () { this.systemTheme = getSystemTheme() this.handleEvents() } getSystemTheme () { return this.systemTheme } handleEvents () { nativeTheme.on('updated', () => { const theme = getSystemTheme() this.systemTheme = theme logger.info('[Motrix] nativeTheme updated===>', theme) this.emit('system-theme-change', theme) }) } updateSystemTheme (theme) { theme = theme === APP_THEME.AUTO ? 'system' : theme nativeTheme.themeSource = theme } } ================================================ FILE: src/main/ui/TouchBarManager.js ================================================ import { EventEmitter } from 'node:events' import { join } from 'node:path' import { TouchBar, nativeImage } from 'electron' import { handleCommand } from '../utils/menu' import logger from '../core/Logger' const { TouchBarButton, TouchBarLabel, TouchBarSpacer, TouchBarGroup } = TouchBar export default class TouchBarManager extends EventEmitter { constructor (options) { super() this.options = options this.bars = {} this.load() } load () { this.template = require('../menus/touchBar.json') } getClickFn (item) { let fn = () => {} if (item.command) { fn = () => { handleCommand(item) } } return fn } getIconImage (icon) { if (!icon) { return } const img = join(__static, `./icons/${icon}.png`) return nativeImage.createFromPath(img) } buildItem (type, options) { let result = null const { label, backgroundColor, textColor, size } = options switch (type) { case 'button': result = new TouchBarButton({ label, backgroundColor, icon: this.getIconImage(options.icon), click: this.getClickFn(options) }) break case 'label': result = new TouchBarLabel({ label, textColor }) break case 'spacer': result = new TouchBarSpacer({ size }) break case 'group': result = new TouchBarGroup({ items: new TouchBar({ items: options.items }) }) break default: result = null } return result } build (template) { const result = [] template.forEach(tpl => { const { id, type, ...rest } = tpl let options = { ...rest } if (type === 'group') { options = { type, items: this.build(options.items) } } const item = this.buildItem(type, options) result.push(item) }) return result } getTouchBarByPage (page) { let bar = this.bars[page] || null if (!bar) { try { const items = this.build(this.template) bar = new TouchBar({ items }) this.bars[page] = bar } catch (e) { logger.info('getTouchBarByPage fail', e) } } return bar } setup (page, window) { const bar = this.getTouchBarByPage(page) window.setTouchBar(bar) } } ================================================ FILE: src/main/ui/TrayManager.js ================================================ import { EventEmitter } from 'node:events' import { join } from 'node:path' import { Tray, Menu, nativeImage } from 'electron' import is from 'electron-is' import { APP_RUN_MODE, APP_THEME } from '@shared/constants' import { getInverseTheme } from '@shared/utils' import logger from '../core/Logger' import { getI18n } from './Locale' import { translateTemplate, flattenMenuItems, updateStates } from '../utils/menu' import { convertArrayBufferToBuffer } from '../utils/index' let tray = null const { platform } = process export default class TrayManager extends EventEmitter { constructor (options = {}) { super() this.options = options this.theme = options.theme || APP_THEME.AUTO this.systemTheme = options.systemTheme this.inverseSystemTheme = getInverseTheme(this.systemTheme) this.macOS = platform === 'darwin' this.speedometer = options.speedometer this.runMode = options.runMode this.i18n = getI18n() this.menu = null this.cache = {} this.uploadSpeed = 0 this.downloadSpeed = 0 this.status = false this.focused = false this.initialized = false this.init() } init () { if (tray || this.initialized || this.runMode === APP_RUN_MODE.HIDE_TRAY) { return } this.loadTemplate() this.loadImages() this.initTray() this.setupMenu() this.bindEvents() this.initialized = true } loadTemplate () { this.template = require('../menus/tray.json') } loadImages () { switch (platform) { case 'darwin': this.loadImagesForMacOS() break case 'win32': this.loadImagesForWindows() break case 'linux': this.loadImagesForLinux() break default: this.loadImagesForDefault() break } } loadImagesForMacOS () { this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-light-normal.png') } loadImagesForWindows () { this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-colorful-normal.png') this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-colorful-active.png') } loadImagesForLinux () { const { theme } = this if (theme === APP_THEME.AUTO) { this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-dark-normal.png') this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-dark-active.png') } else { this.normalIcon = this.getFromCacheOrCreateImage(`mo-tray-${theme}-normal.png`) this.activeIcon = this.getFromCacheOrCreateImage(`mo-tray-${theme}-active.png`) } } loadImagesForDefault () { this.normalIcon = this.getFromCacheOrCreateImage('mo-tray-light-normal.png') this.activeIcon = this.getFromCacheOrCreateImage('mo-tray-light-active.png') } getFromCacheOrCreateImage (key) { let file = this.getCache(key) if (file) { return file } file = nativeImage.createFromPath(join(__static, `./${key}`)) file.setTemplateImage(this.macOS) this.setCache(key, file) return file } getCache (key) { return this.cache[key] } setCache (key, value) { this.cache[key] = value } buildMenu () { const keystrokesByCommand = {} for (const item in this.keymap) { keystrokesByCommand[this.keymap[item]] = item } // Deepclone the menu template to refresh menu const template = JSON.parse(JSON.stringify(this.template)) const tpl = translateTemplate(template, keystrokesByCommand, this.i18n) this.menu = Menu.buildFromTemplate(tpl) this.items = flattenMenuItems(this.menu) } setupMenu () { this.buildMenu() this.updateContextMenu() } initTray () { const { icon } = this.getIcons() tray = new Tray(icon) // tray.setPressedImage(inverseIcon) if (!this.macOS) { tray.setToolTip('Motrix') } } bindEvents () { // All OS tray.on('click', this.handleTrayClick) // macOS, Windows // tray.on('double-click', this.handleTrayDbClick) tray.on('right-click', this.handleTrayRightClick) tray.on('mouse-down', this.handleTrayMouseDown) tray.on('mouse-up', this.handleTrayMouseUp) // macOS only tray.setIgnoreDoubleClickEvents(true) tray.on('drop-files', this.handleTrayDropFiles) tray.on('drop-text', this.handleTrayDropText) } unbindEvents () { // All OS tray.removeListener('click', this.handleTrayClick) // macOS, Windows tray.removeListener('right-click', this.handleTrayRightClick) tray.removeListener('mouse-down', this.handleTrayMouseDown) tray.removeListener('mouse-up', this.handleTrayMouseUp) // macOS only tray.removeListener('drop-files', this.handleTrayDropFiles) tray.removeListener('drop-text', this.handleTrayDropText) } handleTrayClick = (event) => { global.application.toggle() } handleTrayDbClick = (event) => { global.application.show() } handleTrayRightClick = (event) => { tray.popUpContextMenu(this.menu) } handleTrayMouseDown = (event) => { this.focused = true this.emit('mouse-down', { focused: true, theme: this.inverseSystemTheme }) this.renderTray() } handleTrayMouseUp = (event) => { this.focused = false this.emit('mouse-up', { focused: false, theme: this.theme }) this.renderTray() } handleTrayDropFiles = (event, files) => { this.emit('drop-files', files) } handleTrayDropText = (event, text) => { this.emit('drop-text', text) } toggleSpeedometer (enabled) { this.speedometer = enabled } async renderTray () { if (!tray || this.speedometer) { return } const { icon } = this.getIcons() tray.setImage(icon) // tray.setPressedImage(inverseIcon) this.updateContextMenu() } getIcons () { if (this.macOS) { return { icon: this.normalIcon } } const { focused, status, systemTheme } = this const icon = status ? this.activeIcon : this.normalIcon if (systemTheme === APP_THEME.DARK) { return { icon } } const inverseIcon = status ? this.inverseActiveIcon : this.inverseNormalIcon return { icon: focused ? inverseIcon : icon // inverseIcon: focused ? icon : inverseIcon } } updateContextMenu () { /** * Linux requires setContextMenu to be called * in order for the context menu to populate correctly */ if (!tray || process.platform !== 'linux') { return } tray.setContextMenu(this.menu) } updateMenuStates (visibleStates, enabledStates, checkedStates) { updateStates(this.items, visibleStates, enabledStates, checkedStates) this.updateContextMenu() } updateMenuItemVisibleState (id, flag) { const visibleStates = { [id]: flag } this.updateMenuStates(visibleStates, null, null) } updateMenuItemEnabledState (id, flag) { const enabledStates = { [id]: flag } this.updateMenuStates(null, enabledStates, null) } handleLocaleChange (locale) { this.setupMenu() } handleRunModeChange (mode) { this.runMode = mode if (mode === APP_RUN_MODE.HIDE_TRAY) { this.destroy() } else { this.init() } } handleSpeedometerEnableChange (enabled) { this.toggleSpeedometer(enabled) this.renderTray() } handleSystemThemeChange (systemTheme = APP_THEME.LIGHT) { if (!is.macOS()) { return } this.systemTheme = systemTheme this.inverseSystemTheme = getInverseTheme(systemTheme) this.loadImages() this.renderTray() } handleDownloadStatusChange (status) { this.status = status this.renderTray() } async handleSpeedChange ({ uploadSpeed, downloadSpeed }) { if (!this.speedometer) { return } this.uploadSpeed = uploadSpeed this.downloadSpeed = downloadSpeed await this.renderTray() } async updateTrayByImage (ab) { if (!tray) { return } const buffer = convertArrayBufferToBuffer(ab) const image = nativeImage.createFromBuffer(buffer, { scaleFactor: 2 }) image.setTemplateImage(this.macOS) tray.setImage(image) } destroy () { logger.info('[Motrix] TrayManager.destroy') if (tray) { this.unbindEvents() } tray.destroy() tray = null this.initialized = false } } ================================================ FILE: src/main/ui/WindowManager.js ================================================ import { join } from 'node:path' import { EventEmitter } from 'node:events' import { debounce } from 'lodash' import { app, shell, screen, BrowserWindow } from 'electron' import is from 'electron-is' import pageConfig from '../configs/page' import logger from '../core/Logger' const baseBrowserOptions = { titleBarStyle: 'hiddenInset', show: false, width: 1024, height: 768, backgroundColor: '#fff', webPreferences: { nodeIntegration: true } } // fix: BrowserWindow rendering bug under linux const defaultBrowserOptions = is.macOS() ? { ...baseBrowserOptions, vibrancy: 'ultra-dark', visualEffectState: 'active', backgroundColor: '#00000000' } : { ...baseBrowserOptions } export default class WindowManager extends EventEmitter { constructor (options = {}) { super() this.userConfig = options.userConfig || {} this.windows = {} this.willQuit = false this.handleBeforeQuit() this.handleAllWindowClosed() } setWillQuit (flag) { this.willQuit = flag } getPageOptions (page) { const result = pageConfig[page] || {} const hideAppMenu = this.userConfig['hide-app-menu'] if (hideAppMenu) { result.attrs.frame = false } // Optimized for small screen users const { width, height } = screen.getPrimaryDisplay().workAreaSize const widthScale = width >= 1280 ? 1 : 0.875 const heightScale = height >= 800 ? 1 : 0.875 result.attrs.width *= widthScale result.attrs.height *= heightScale // fix AppImage Dock Icon Missing // https://github.com/AppImage/AppImageKit/wiki/Bundling-Electron-apps if (is.linux()) { result.attrs.icon = join(__static, './512x512.png') } return result } getPageBounds (page) { const enabled = this.userConfig['keep-window-state'] const windowStateMap = this.userConfig['window-state'] || {} let result = null if (enabled) { result = windowStateMap[page] } return result } openWindow (page, options = {}) { const pageOptions = this.getPageOptions(page) const { hidden } = options const autoHideWindow = this.userConfig['auto-hide-window'] let window = this.windows[page] || null if (window) { window.show() window.focus() return window } window = new BrowserWindow({ ...defaultBrowserOptions, ...pageOptions.attrs, webPreferences: { enableRemoteModule: true, contextIsolation: false, nodeIntegration: true, nodeIntegrationInWorker: true } }) const bounds = this.getPageBounds(page) if (bounds) { window.setBounds(bounds) } if (is.dev() && pageOptions.openDevTools) { window.webContents.openDevTools() } window.webContents.setWindowOpenHandler(({ url }) => { shell.openExternal(url) return { action: 'deny' } }) if (pageOptions.url) { window.loadURL(pageOptions.url) } window.once('ready-to-show', () => { if (!hidden) { window.show() } }) window.on('enter-full-screen', () => { this.emit('enter-full-screen', window) }) window.on('leave-full-screen', () => { this.emit('leave-full-screen', window) }) this.handleWindowState(page, window) this.handleWindowClose(pageOptions, page, window) this.bindAfterClosed(page, window) this.addWindow(page, window) if (autoHideWindow) { this.handleWindowBlur() } return window } getWindow (page) { return this.windows[page] } getWindows () { return this.windows || {} } getWindowList () { return Object.values(this.getWindows()) } addWindow (page, window) { this.windows[page] = window } destroyWindow (page) { const win = this.getWindow(page) if (!win) { return } this.removeWindow(page) win.removeListener('closed') win.removeListener('move') win.removeListener('resize') win.destroy() } removeWindow (page) { this.windows[page] = null } bindAfterClosed (page, window) { window.on('closed', (event) => { this.removeWindow(page) }) } handleWindowState (page, window) { window.on('resize', debounce(() => { const bounds = window.getBounds() this.emit('window-resized', { page, bounds }) }, 500)) window.on('move', debounce(() => { const bounds = window.getBounds() this.emit('window-moved', { page, bounds }) }, 500)) } handleWindowClose (pageOptions, page, window) { window.on('close', (event) => { if (pageOptions.bindCloseToHide && !this.willQuit) { event.preventDefault() // @see https://github.com/electron/electron/issues/20263 if (window.isFullScreen()) { window.once('leave-full-screen', () => window.hide()) window.setFullScreen(false) } else { window.hide() } } const bounds = window.getBounds() this.emit('window-closed', { page, bounds }) }) } showWindow (page) { const window = this.getWindow(page) if (!window || (window.isVisible() && !window.isMinimized())) { return } window.show() } hideWindow (page) { const window = this.getWindow(page) if (!window || !window.isVisible()) { return } window.hide() } hideAllWindow () { this.getWindowList().forEach((window) => { window.hide() }) } toggleWindow (page) { const window = this.getWindow(page) if (!window) { return } if (!window.isVisible() || window.isFullScreen()) { window.show() } else { window.hide() } } getFocusedWindow () { return BrowserWindow.getFocusedWindow() } handleBeforeQuit () { app.on('before-quit', () => { this.setWillQuit(true) }) } onWindowBlur (event, window) { window.hide() } handleWindowBlur () { app.on('browser-window-blur', this.onWindowBlur) } unbindWindowBlur () { app.removeListener('browser-window-blur', this.onWindowBlur) } handleAllWindowClosed () { app.on('window-all-closed', (event) => { event.preventDefault() }) } sendCommandTo (window, command, ...args) { if (!window) { return } logger.info('[Motrix] send command to:', command, ...args) window.webContents.send('command', command, ...args) } sendMessageTo (window, channel, ...args) { if (!window) { return } window.webContents.send(channel, ...args) } } ================================================ FILE: src/main/utils/index.js ================================================ import { resolve } from 'node:path' import { access, constants, existsSync, lstatSync } from 'node:fs' import { app, nativeTheme, shell } from 'electron' import is from 'electron-is' import { APP_THEME, ENGINE_MAX_CONNECTION_PER_SERVER, IP_VERSION, IS_PORTABLE, PORTABLE_EXECUTABLE_DIR } from '@shared/constants' import { engineBinMap, engineArchMap } from '../configs/engine' import logger from '../core/Logger' export const getUserDataPath = () => { return IS_PORTABLE ? PORTABLE_EXECUTABLE_DIR : app.getPath('userData') } export const getSystemLogPath = () => { return app.getPath('logs') } export const getUserDownloadsPath = () => { return app.getPath('downloads') } export const getConfigBasePath = () => { const path = getUserDataPath() return path } export const getSessionPath = () => { return resolve(getUserDataPath(), './download.session') } export const getEnginePidPath = () => { return resolve(getUserDataPath(), './engine.pid') } export const getDhtPath = (protocol) => { const name = protocol === IP_VERSION.V6 ? 'dht6.dat' : 'dht.dat' return resolve(getUserDataPath(), `./${name}`) } export const getEngineBin = (platform) => { const result = engineBinMap[platform] || '' return result } export const getEngineArch = (platform, arch) => { if (!['darwin', 'win32', 'linux'].includes(platform)) { return '' } const result = engineArchMap[platform][arch] return result } export const getDevEnginePath = (platform, arch) => { const ah = getEngineArch(platform, arch) const base = `../../../extra/${platform}/${ah}/engine` const result = resolve(__dirname, base) return result } export const getProdEnginePath = () => { return resolve(app.getAppPath(), '../engine') } export const getEnginePath = (platform, arch) => { return is.dev() ? getDevEnginePath(platform, arch) : getProdEnginePath() } export const getAria2BinPath = (platform, arch) => { const base = getEnginePath(platform, arch) const binName = getEngineBin(platform) const result = resolve(base, `./${binName}`) return result } export const getAria2ConfPath = (platform, arch) => { const base = getEnginePath(platform, arch) return resolve(base, './aria2.conf') } export const transformConfig = (config) => { const result = [] for (const [k, v] of Object.entries(config)) { if (v !== '') { result.push(`--${k}=${v}`) } } return result } export const isRunningInDmg = () => { if (!is.macOS() || is.dev()) { return false } const appPath = app.getAppPath() const result = appPath.startsWith('/Volumes/') return result } export const moveAppToApplicationsFolder = (errorMsg = '') => { return new Promise((resolve, reject) => { try { const result = app.moveToApplicationsFolder() if (result) { resolve(result) } else { reject(new Error(errorMsg)) } } catch (err) { reject(err) } }) } export const splitArgv = (argv) => { const args = [] const extra = {} for (const arg of argv) { if (arg.startsWith('--')) { const kv = arg.split('=') const key = kv[0] const value = kv[1] || '1' extra[key] = value continue } args.push(arg) } return { args, extra } } export const parseArgvAsUrl = (argv) => { const arg = argv[1] if (!arg) { return } if (checkIsSupportedSchema(arg)) { return arg } } export const checkIsSupportedSchema = (url = '') => { const str = url.toLowerCase() if ( str.startsWith('ftp:') || str.startsWith('http:') || str.startsWith('https:') || str.startsWith('magnet:') || str.startsWith('thunder:') || str.startsWith('mo:') || str.startsWith('motrix:') ) { return true } else { return false } } export const isDirectory = (path) => { return existsSync(path) && lstatSync(path).isDirectory() } export const parseArgvAsFile = (argv) => { let arg = argv[1] if (!arg || isDirectory(arg)) { return } if (is.linux()) { arg = arg.replace('file://', '') } return arg } export const getMaxConnectionPerServer = () => { return ENGINE_MAX_CONNECTION_PER_SERVER } export const getSystemTheme = () => { let result = APP_THEME.LIGHT result = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT return result } export const convertArrayBufferToBuffer = (arrayBuffer) => { const buffer = Buffer.alloc(arrayBuffer.byteLength) const view = new Uint8Array(arrayBuffer) for (let i = 0; i < buffer.length; ++i) { buffer[i] = view[i] } return buffer } export const showItemInFolder = (fullPath) => { if (!fullPath) { return } fullPath = resolve(fullPath) access(fullPath, constants.F_OK, (err) => { if (err) { logger.warn(`[Motrix] ${fullPath} ${err ? 'does not exist' : 'exists'}`) return } shell.showItemInFolder(fullPath) }) } ================================================ FILE: src/main/utils/menu.js ================================================ import { parse } from 'querystring' export const concat = (template, submenu, submenuToAdd) => { submenuToAdd.forEach(sub => { let relativeItem = null if (sub.position) { switch (sub.position) { case 'first': submenu.unshift(sub) break case 'last': submenu.push(sub) break case 'before': relativeItem = findById(template, sub['relative-id']) if (relativeItem) { const array = relativeItem.__parent const index = array.indexOf(relativeItem) array.splice(index, 0, sub) } break case 'after': relativeItem = findById(template, sub['relative-id']) if (relativeItem) { const array = relativeItem.__parent const index = array.indexOf(relativeItem) array.splice(index + 1, 0, sub) } break default: submenu.push(sub) break } } else { submenu.push(sub) } }) } export const merge = (template, item) => { if (item.id) { const matched = findById(template, item.id) if (matched) { if (item.submenu && Array.isArray(item.submenu)) { if (!Array.isArray(matched.submenu)) { matched.submenu = [] } concat(template, matched.submenu, item.submenu) } } else { concat(template, template, [item]) } } else { template.push(item) } } function findById (template, id) { for (const i in template) { const item = template[i] if (item.id === id) { // Returned item need to have a reference to parent Array (.__parent). // This is required to handle `position` and `relative-id` item.__parent = template return item } else if (Array.isArray(item.submenu)) { const result = findById(item.submenu, id) if (result) { return result } } } return null } export const translateTemplate = (template, keystrokesByCommand, i18n) => { for (const i in template) { const item = template[i] if (item.command) { item.accelerator = acceleratorForCommand(item.command, keystrokesByCommand) } // If label is specified, label is used as the key of i18n.t(key), // which mainly solves the inaccurate translation of item.id. if (i18n) { if (item.label) { item.label = i18n.t(item.label) } else if (item.id) { item.label = i18n.t(item.id) } } item.click = () => { handleCommand(item) } if (item.submenu) { translateTemplate(item.submenu, keystrokesByCommand, i18n) } } return template } export const handleCommand = (item) => { handleCommandBefore(item) const args = item['command-arg'] ? [item.command, item['command-arg']] : [item.command] global.application.sendCommandToAll(...args) handleCommandAfter(item) } function handleCommandBefore (item) { if (!item['command-before']) { return } const [command, params] = item['command-before'].split('?') const args = parse(params) global.application.sendCommandToAll(command, args) } function handleCommandAfter (item) { if (!item['command-after']) { return } const [command, params] = item['command-after'].split('?') const args = parse(params) global.application.sendCommandToAll(command, args) } function acceleratorForCommand (command, keystrokesByCommand) { const keystroke = keystrokesByCommand[command] if (keystroke) { let modifiers = keystroke.split(/-(?=.)/) const key = modifiers.pop().toUpperCase() .replace('+', 'Plus') .replace('MINUS', '-') modifiers = modifiers.map((modifier) => { if (process.platform === 'darwin') { return modifier.replace(/cmdctrl/ig, 'Cmd') .replace(/shift/ig, 'Shift') .replace(/cmd/ig, 'Cmd') .replace(/ctrl/ig, 'Ctrl') .replace(/alt/ig, 'Alt') } else { return modifier.replace(/cmdctrl/ig, 'Ctrl') .replace(/shift/ig, 'Shift') .replace(/ctrl/ig, 'Ctrl') .replace(/alt/ig, 'Alt') } }) const keys = modifiers.concat([key]) return keys.join('+') } return null } export const flattenMenuItems = (menu) => { const flattenItems = {} menu.items.forEach(item => { if (item.id) { flattenItems[item.id] = item if (item.submenu) { Object.assign(flattenItems, flattenMenuItems(item.submenu)) } } }) return flattenItems } export const updateStates = (itemsById, visibleStates, enabledStates, checkedStates) => { if (visibleStates) { for (const command in visibleStates) { const item = itemsById[command] if (item) { item.visible = visibleStates[command] } } } if (enabledStates) { for (const command in enabledStates) { const item = itemsById[command] if (item) { item.enabled = enabledStates[command] } } } if (checkedStates) { for (const id in checkedStates) { const item = itemsById[id] if (item) { item.checked = checkedStates[id] } } } } ================================================ FILE: src/renderer/api/Api.js ================================================ import { ipcRenderer } from 'electron' import is from 'electron-is' import { isEmpty, clone } from 'lodash' import { Aria2 } from '@shared/aria2' import { separateConfig, compactUndefined, formatOptionsForEngine, mergeTaskResult, changeKeysToCamelCase, changeKeysToKebabCase } from '@shared/utils' import { ENGINE_RPC_HOST } from '@shared/constants' export default class Api { constructor (options = {}) { this.options = options this.init() } async init () { this.config = await this.loadConfig() this.client = this.initClient() this.client.open() } loadConfigFromLocalStorage () { // TODO const result = {} return result } async loadConfigFromNativeStore () { const result = await ipcRenderer.invoke('get-app-config') return result } async loadConfig () { let result = is.renderer() ? await this.loadConfigFromNativeStore() : this.loadConfigFromLocalStorage() result = changeKeysToCamelCase(result) return result } initClient () { const { rpcListenPort: port, rpcSecret: secret } = this.config const host = ENGINE_RPC_HOST return new Aria2({ host, port, secret }) } closeClient () { this.client.close() .then(() => { this.client = null }) .catch(err => { console.log('engine client close fail', err) }) } fetchPreference () { return new Promise((resolve) => { this.config = this.loadConfig() resolve(this.config) }) } savePreference (params = {}) { const kebabParams = changeKeysToKebabCase(params) if (is.renderer()) { return this.savePreferenceToNativeStore(kebabParams) } else { return this.savePreferenceToLocalStorage(kebabParams) } } savePreferenceToLocalStorage () { // TODO } savePreferenceToNativeStore (params = {}) { const { user, system, others } = separateConfig(params) const config = {} if (!isEmpty(user)) { console.info('[Motrix] save user config: ', user) config.user = user } if (!isEmpty(system)) { console.info('[Motrix] save system config: ', system) config.system = system this.updateActiveTaskOption(system) } if (!isEmpty(others)) { console.info('[Motrix] save config found illegal key: ', others) } ipcRenderer.send('command', 'application:save-preference', config) } getVersion () { return this.client.call('getVersion') } changeGlobalOption (options) { const args = formatOptionsForEngine(options) return this.client.call('changeGlobalOption', args) } getGlobalOption () { return new Promise((resolve) => { this.client.call('getGlobalOption') .then((data) => { resolve(changeKeysToCamelCase(data)) }) }) } getOption (params = {}) { const { gid } = params const args = compactUndefined([gid]) return new Promise((resolve) => { this.client.call('getOption', ...args) .then((data) => { resolve(changeKeysToCamelCase(data)) }) }) } updateActiveTaskOption (options) { this.fetchTaskList({ type: 'active' }) .then((data) => { if (isEmpty(data)) { return } const gids = data.map((task) => task.gid) this.batchChangeOption({ gids, options }) }) } changeOption (params = {}) { const { gid, options = {} } = params const engineOptions = formatOptionsForEngine(options) const args = compactUndefined([gid, engineOptions]) return this.client.call('changeOption', ...args) } getGlobalStat () { return this.client.call('getGlobalStat') } addUri (params) { const { uris, outs, options } = params const tasks = uris.map((uri, index) => { const engineOptions = formatOptionsForEngine(options) if (outs && outs[index]) { engineOptions.out = outs[index] } const args = compactUndefined([[uri], engineOptions]) return ['aria2.addUri', ...args] }) return this.client.multicall(tasks) } addTorrent (params) { const { torrent, options } = params const engineOptions = formatOptionsForEngine(options) const args = compactUndefined([torrent, [], engineOptions]) return this.client.call('addTorrent', ...args) } addMetalink (params) { const { metalink, options } = params const engineOptions = formatOptionsForEngine(options) const args = compactUndefined([metalink, engineOptions]) return this.client.call('addMetalink', ...args) } fetchDownloadingTaskList (params = {}) { const { offset = 0, num = 20, keys } = params const activeArgs = compactUndefined([keys]) const waitingArgs = compactUndefined([offset, num, keys]) return new Promise((resolve, reject) => { this.client.multicall([ ['aria2.tellActive', ...activeArgs], ['aria2.tellWaiting', ...waitingArgs] ]).then((data) => { console.log('[Motrix] fetch downloading task list data:', data) const result = mergeTaskResult(data) resolve(result) }).catch((err) => { console.log('[Motrix] fetch downloading task list fail:', err) reject(err) }) }) } fetchWaitingTaskList (params = {}) { const { offset = 0, num = 20, keys } = params const args = compactUndefined([offset, num, keys]) return this.client.call('tellWaiting', ...args) } fetchStoppedTaskList (params = {}) { const { offset = 0, num = 20, keys } = params const args = compactUndefined([offset, num, keys]) return this.client.call('tellStopped', ...args) } fetchActiveTaskList (params = {}) { const { keys } = params const args = compactUndefined([keys]) return this.client.call('tellActive', ...args) } fetchTaskList (params = {}) { const { type } = params switch (type) { case 'active': return this.fetchDownloadingTaskList(params) case 'waiting': return this.fetchWaitingTaskList(params) case 'stopped': return this.fetchStoppedTaskList(params) default: return this.fetchDownloadingTaskList(params) } } fetchTaskItem (params = {}) { const { gid, keys } = params const args = compactUndefined([gid, keys]) return this.client.call('tellStatus', ...args) } fetchTaskItemWithPeers (params = {}) { const { gid, keys } = params const statusArgs = compactUndefined([gid, keys]) const peersArgs = compactUndefined([gid]) return new Promise((resolve, reject) => { this.client.multicall([ ['aria2.tellStatus', ...statusArgs], ['aria2.getPeers', ...peersArgs] ]).then((data) => { console.log('[Motrix] fetchTaskItemWithPeers:', data) const result = data[0] && data[0][0] const peers = data[1] && data[1][0] result.peers = peers || [] console.log('[Motrix] fetchTaskItemWithPeers.result:', result) console.log('[Motrix] fetchTaskItemWithPeers.peers:', peers) resolve(result) }).catch((err) => { console.log('[Motrix] fetch downloading task list fail:', err) reject(err) }) }) } fetchTaskItemPeers (params = {}) { const { gid, keys } = params const args = compactUndefined([gid, keys]) return this.client.call('getPeers', ...args) } pauseTask (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('pause', ...args) } pauseAllTask (params = {}) { return this.client.call('pauseAll') } forcePauseTask (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('forcePause', ...args) } forcePauseAllTask (params = {}) { return this.client.call('forcePauseAll') } resumeTask (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('unpause', ...args) } resumeAllTask (params = {}) { return this.client.call('unpauseAll') } removeTask (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('remove', ...args) } forceRemoveTask (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('forceRemove', ...args) } saveSession (params = {}) { return this.client.call('saveSession') } purgeTaskRecord (params = {}) { return this.client.call('purgeDownloadResult') } removeTaskRecord (params = {}) { const { gid } = params const args = compactUndefined([gid]) return this.client.call('removeDownloadResult', ...args) } multicall (method, params = {}) { let { gids, options = {} } = params options = formatOptionsForEngine(options) const data = gids.map((gid, index) => { const _options = clone(options) const args = compactUndefined([gid, _options]) return [method, ...args] }) return this.client.multicall(data) } batchChangeOption (params = {}) { return this.multicall('aria2.changeOption', params) } batchRemoveTask (params = {}) { return this.multicall('aria2.remove', params) } batchResumeTask (params = {}) { return this.multicall('aria2.unpause', params) } batchPauseTask (params = {}) { return this.multicall('aria2.pause', params) } batchForcePauseTask (params = {}) { return this.multicall('aria2.forcePause', params) } } ================================================ FILE: src/renderer/api/index.js ================================================ import Api from './Api' const api = new Api() export default api ================================================ FILE: src/renderer/assets/.gitkeep ================================================ ================================================ FILE: src/renderer/components/About/AboutPanel.vue ================================================ ================================================ FILE: src/renderer/components/About/AppInfo.vue ================================================ ================================================ FILE: src/renderer/components/About/Copyright.vue ================================================ ================================================ FILE: src/renderer/components/Aside/Index.vue ================================================ ================================================ FILE: src/renderer/components/Browser/index.vue ================================================ ================================================ FILE: src/renderer/components/CommandManager/index.js ================================================ import { EventEmitter } from 'node:events' export default class CommandManager extends EventEmitter { constructor () { super() this.commands = {} } register (id, fn) { if (this.commands[id]) { console.log('[Motrix] Attempting to register an already-registered command: ' + id) return null } if (!id || !fn) { console.error('[Motrix] Attempting to register a command with a missing id, or command function.') return null } this.commands[id] = fn this.emit('commandRegistered', id) } unregister (id) { if (this.commands[id]) { delete this.commands[id] this.emit('commandUnregistered', id) } } execute (id, ...args) { const fn = this.commands[id] if (fn) { try { this.emit('beforeExecuteCommand', id) } catch (err) { console.error(err) } const result = fn(...args) return result } else { return false } } } ================================================ FILE: src/renderer/components/CommandManager/instance.js ================================================ import CommandManager from '.' export const commands = new CommandManager() ================================================ FILE: src/renderer/components/DragSelect/Index.vue ================================================ ================================================ FILE: src/renderer/components/Dragger/Index.vue ================================================ ================================================ FILE: src/renderer/components/Icons/Icon.vue ================================================ ================================================ FILE: src/renderer/components/Icons/arrow-down.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'arrow-down': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/arrow-up.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'arrow-up': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/audio.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'audio': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/delete.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'delete': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/dice.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'dice': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/document.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'document': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2', 'fill': 'none' } } }) ================================================ FILE: src/renderer/components/Icons/folder.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'folder': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/image.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'image': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/inbox.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'inbox': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '1.5' } } }) ================================================ FILE: src/renderer/components/Icons/info-circle.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'info-circle': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/info-square.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'info-square': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/link.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'link': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/magnet.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'magnet': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/menu-about.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'menu-about': { 'width': 24, 'height': 24, 'raw': ` ` } }) ================================================ FILE: src/renderer/components/Icons/menu-add.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'menu-add': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/menu-preference.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'menu-preference': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/menu-task.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'menu-task': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M14,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h13c0.6,0,1,0.4,1,1S14.6,13,14,13z' }, { 'd': 'M23,6H1C0.4,6,0,5.6,0,5s0.4-1,1-1h22c0.6,0,1,0.4,1,1S23.6,6,23,6z' }, { 'd': 'M23,20H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h22c0.6,0,1,0.4,1,1S23.6,20,23,20z' }] } }) ================================================ FILE: src/renderer/components/Icons/more.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'more': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M3,15 C2.17187086,15 1.46484668,14.7070342 0.87890625,14.1210938 C0.29296582,13.5351533 0,12.8281291 0,12 C0,11.1718709 0.29296582,10.4648467 0.87890625,9.87890625 C1.46484668,9.29296582 2.17187086,9 3,9 C3.82812914,9 4.53515332,9.29296582 5.12109375,9.87890625 C5.70703418,10.4648467 6,11.1718709 6,12 C6,12.8281291 5.70703418,13.5351533 5.12109375,14.1210938 C4.53515332,14.7070342 3.82812914,15 3,15 Z M3,10.5 C2.59374797,10.5 2.24218898,10.648436 1.9453125,10.9453125 C1.64843602,11.242189 1.5,11.593748 1.5,12 C1.5,12.406252 1.64843602,12.757811 1.9453125,13.0546875 C2.24218898,13.351564 2.59374797,13.5 3,13.5 C3.40625203,13.5 3.75781102,13.351564 4.0546875,13.0546875 C4.35156398,12.757811 4.5,12.406252 4.5,12 C4.5,11.593748 4.35156398,11.242189 4.0546875,10.9453125 C3.75781102,10.648436 3.40625203,10.5 3,10.5 Z M12,15 C11.1718709,15 10.4648467,14.7070342 9.87890625,14.1210938 C9.29296582,13.5351533 9,12.8281291 9,12 C9,11.1718709 9.29296582,10.4648467 9.87890625,9.87890625 C10.4648467,9.29296582 11.1718709,9 12,9 C12.8281291,9 13.5351533,9.29296582 14.1210938,9.87890625 C14.7070342,10.4648467 15,11.1718709 15,12 C15,12.8281291 14.7070342,13.5351533 14.1210938,14.1210938 C13.5351533,14.7070342 12.8281291,15 12,15 Z M12,10.5 C11.593748,10.5 11.242189,10.648436 10.9453125,10.9453125 C10.648436,11.242189 10.5,11.593748 10.5,12 C10.5,12.406252 10.648436,12.757811 10.9453125,13.0546875 C11.242189,13.351564 11.593748,13.5 12,13.5 C12.406252,13.5 12.757811,13.351564 13.0546875,13.0546875 C13.351564,12.757811 13.5,12.406252 13.5,12 C13.5,11.593748 13.351564,11.242189 13.0546875,10.9453125 C12.757811,10.648436 12.406252,10.5 12,10.5 Z M21,9 C21.8281291,9 22.5351533,9.29296582 23.1210938,9.87890625 C23.7070342,10.4648467 24,11.1718709 24,12 C24,12.8281291 23.7070342,13.5351533 23.1210938,14.1210938 C22.5351533,14.7070342 21.8281291,15 21,15 C20.1718709,15 19.4648467,14.7070342 18.8789062,14.1210938 C18.2929658,13.5351533 18,12.8281291 18,12 C18,11.1718709 18.2929658,10.4648467 18.8789062,9.87890625 C19.4648467,9.29296582 20.1718709,9 21,9 Z M21,13.5 C21.406252,13.5 21.757811,13.351564 22.0546875,13.0546875 C22.351564,12.757811 22.5,12.406252 22.5,12 C22.5,11.593748 22.351564,11.242189 22.0546875,10.9453125 C21.757811,10.648436 21.406252,10.5 21,10.5 C20.593748,10.5 20.242189,10.648436 19.9453125,10.9453125 C19.648436,11.242189 19.5,11.593748 19.5,12 C19.5,12.406252 19.648436,12.757811 19.9453125,13.0546875 C20.242189,13.351564 20.593748,13.5 21,13.5 Z' }] } }) ================================================ FILE: src/renderer/components/Icons/node.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'node': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/preference-advanced.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'preference-advanced': { 'width': 24, 'height': 24, 'polygons': [{ 'points': '10.853,9.439 6.707,5.293 8,4 4,0 0,4 4,8 5.293,6.707 9.189,10.603' }], 'paths': [{ 'd': 'M18.94,13.94C18.631,13.976,18.318,14,18,14c-0.305,0-0.608-0.018-0.912-0.053l-3.641,4.499 l4.518,4.518c1.381,1.381,3.619,1.381,5,0v0c1.381-1.381,1.381-3.619,0-5L18.94,13.94z' }, { 'd': 'M20.271,6.771l-3.042-3.042l3.208-3.208C19.692,0.189,18.869,0,18,0c-3.314,0-6,2.686-6,6 c0,0.594,0.089,1.166,0.25,1.708l-10.789,8.73c-0.891,0.787-1.423,1.919-1.459,3.106c-0.037,1.188,0.424,2.351,1.264,3.19 C2.082,23.551,3.167,24,4.321,24c1.239,0,2.421-0.532,3.241-1.461l8.73-10.789C16.834,11.911,17.406,12,18,12c3.314,0,6-2.686,6-6 c0-0.869-0.189-1.692-0.521-2.438L20.271,6.771z' }] } }) ================================================ FILE: src/renderer/components/Icons/preference-basic.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'preference-basic': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M6.5,11h11c3,0,5.5-2.5,5.5-5.5S20.5,0,17.5,0h-11C3.5,0,1,2.5,1,5.5S3.5,11,6.5,11z M6.5,2 C8.4,2,10,3.6,10,5.5S8.4,9,6.5,9S3,7.4,3,5.5S4.6,2,6.5,2z' }, { 'd': 'M17.5,13h-11c-3,0-5.5,2.5-5.5,5.5S3.5,24,6.5,24h11c3,0,5.5-2.5,5.5-5.5S20.5,13,17.5,13z M17.5,22c-1.9,0-3.5-1.6-3.5-3.5s1.6-3.5,3.5-3.5s3.5,1.6,3.5,3.5S19.4,22,17.5,22z' }] } }) ================================================ FILE: src/renderer/components/Icons/preference-lab.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'preference-lab': { 'width': 24, 'height': 24, 'polygons': [{ 'points': '9,11.675 5.855,16 18.145,16 15,11.675 15,5 17,5 17,0 7,0 7,5 9,5' }], 'paths': [{ 'd': 'M19.6,18H4.4l-0.898,1.235c-0.668,0.917-0.763,2.115-0.248,3.126S4.793,24,5.928,24h12.145 c1.135,0,2.159-0.628,2.674-1.639s0.42-2.209-0.248-3.126L19.6,18z' }] } }) ================================================ FILE: src/renderer/components/Icons/purge.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'purge': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/refresh.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'refresh': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/speedometer.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'speedometer': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M12,3.609375 C13.1562558,3.609375 14.2460886,3.82812281 15.2695312,4.265625 C16.2929739,4.70312719 17.18359,5.30077746 17.9414062,6.05859375 C18.6992225,6.81641004 19.2968728,7.70702613 19.734375,8.73046875 C20.1718772,9.75391137 20.390625,10.8437442 20.390625,12 C20.390625,12.2187511 20.3242194,12.3984368 20.1914062,12.5390625 C20.0585931,12.6796882 19.8750012,12.75 19.640625,12.75 C19.4218739,12.75 19.2421882,12.6796882 19.1015625,12.5390625 C18.9609368,12.3984368 18.890625,12.2187511 18.890625,12 C18.890625,11.0468702 18.7109393,10.1484417 18.3515625,9.3046875 C17.9921857,8.46093328 17.5039093,7.73047184 16.8867188,7.11328125 C16.2695282,6.49609066 15.5390667,6.0078143 14.6953125,5.6484375 C13.8515583,5.2890607 12.9531298,5.109375 12,5.109375 C11.5468727,5.109375 11.0859398,5.14843711 10.6171875,5.2265625 C10.1484352,5.30468789 9.71093953,5.43749906 9.3046875,5.625 C9.11718656,5.70312539 8.92578223,5.7070316 8.73046875,5.63671875 C8.53515527,5.5664059 8.39843789,5.43750094 8.3203125,5.25 C8.24218711,5.06249906 8.2382809,4.87109473 8.30859375,4.67578125 C8.3789066,4.48046777 8.50781156,4.34375039 8.6953125,4.265625 C9.22656516,4.04687391 9.76952848,3.88281305 10.3242187,3.7734375 C10.878909,3.66406195 11.4374972,3.609375 12,3.609375 Z M5.25,8.3203125 C5.43750094,8.39843789 5.57421832,8.53515527 5.66015625,8.73046875 C5.74609418,8.92578223 5.73437555,9.11718656 5.625,9.3046875 C5.43749906,9.71093953 5.30468789,10.1406227 5.2265625,10.59375 C5.14843711,11.0468773 5.109375,11.5156226 5.109375,12 C5.109375,12.2187511 5.0390632,12.3984368 4.8984375,12.5390625 C4.7578118,12.6796882 4.57812609,12.75 4.359375,12.75 C4.12499883,12.75 3.94140691,12.6796882 3.80859375,12.5390625 C3.67578059,12.3984368 3.609375,12.2187511 3.609375,12 C3.609375,11.4374972 3.66406195,10.878909 3.7734375,10.3242188 C3.88281305,9.76952848 4.04687391,9.22656516 4.265625,8.6953125 C4.34375039,8.50781156 4.48046777,8.3789066 4.67578125,8.30859375 C4.87109473,8.2382809 5.06249906,8.24218711 5.25,8.3203125 Z M12,0 C13.6562583,0 15.2109302,0.316403086 16.6640625,0.94921875 C18.1171948,1.58203441 19.3867133,2.44140082 20.4726562,3.52734375 C21.5585992,4.61328668 22.4179656,5.88280523 23.0507812,7.3359375 C23.6835969,8.78906977 24,10.3437417 24,12 C24,13.6562583 23.6835969,15.2109302 23.0507812,16.6640625 C22.4179656,18.1171948 21.5585992,19.3867133 20.4726562,20.4726562 C19.3867133,21.5585992 18.1171948,22.4179656 16.6640625,23.0507812 C15.2109302,23.6835969 13.6562583,24 12,24 C10.3437417,24 8.78906977,23.6835969 7.3359375,23.0507812 C5.88280523,22.4179656 4.61328668,21.5585992 3.52734375,20.4726562 C2.44140082,19.3867133 1.58203441,18.1171948 0.94921875,16.6640625 C0.316403086,15.2109302 0,13.6562583 0,12 C0,10.3437417 0.316403086,8.78906977 0.94921875,7.3359375 C1.58203441,5.88280523 2.44140082,4.61328668 3.52734375,3.52734375 C4.61328668,2.44140082 5.88280523,1.58203441 7.3359375,0.94921875 C8.78906977,0.316403086 10.3437417,0 12,0 Z M12,22.5 C13.4375072,22.5 14.7968686,22.222659 16.078125,21.6679688 C17.3593814,21.1132785 18.4726515,20.3593798 19.4179688,19.40625 C20.363286,18.4531202 21.1132785,17.3398501 21.6679688,16.0664062 C22.222659,14.7929624 22.5,13.4375072 22.5,12 C22.5,10.5624928 22.222659,9.20313141 21.6679688,7.921875 C21.1132785,6.64061859 20.3593798,5.52734848 19.40625,4.58203125 C18.4531202,3.63671402 17.3398501,2.88672152 16.0664062,2.33203125 C14.7929624,1.77734098 13.4375072,1.5 12,1.5 C10.5624928,1.5 9.20313141,1.77734098 7.921875,2.33203125 C6.64061859,2.88672152 5.52734848,3.64062023 4.58203125,4.59375 C3.63671402,5.54687977 2.88672152,6.66014988 2.33203125,7.93359375 C1.77734098,9.20703762 1.5,10.5624928 1.5,12 C1.5,13.4375072 1.77734098,14.7929624 2.33203125,16.0664062 C2.88672152,17.3398501 3.64062023,18.4531202 4.59375,19.40625 C5.54687977,20.3593798 6.66014988,21.1132785 7.93359375,21.6679688 C9.20703762,22.222659 10.5624928,22.5 12,22.5 Z M12,9 C12.8281291,9 13.5351533,9.29296582 14.1210938,9.87890625 C14.7070342,10.4648467 15,11.1718709 15,12 C15,12.8281291 14.7070342,13.5351533 14.1210938,14.1210938 C13.5351533,14.7070342 12.8281291,15 12,15 C11.1718709,15 10.4648467,14.7070342 9.87890625,14.1210938 C9.29296582,13.5351533 9,12.8281291 9,12 C9,11.7343737 9.03906211,11.4726575 9.1171875,11.2148438 C9.19531289,10.95703 9.3046868,10.7187511 9.4453125,10.5 L5.765625,6.8203125 C5.6249993,6.6796868 5.5546875,6.50781352 5.5546875,6.3046875 C5.5546875,6.10156148 5.6249993,5.92187578 5.765625,5.765625 C5.92187578,5.6249993 6.10156148,5.5546875 6.3046875,5.5546875 C6.50781352,5.5546875 6.6796868,5.6249993 6.8203125,5.765625 L10.5,9.4453125 C10.7187511,9.3046868 10.95703,9.19531289 11.2148438,9.1171875 C11.4726575,9.03906211 11.7343737,9 12,9 Z M12,13.5 C12.406252,13.5 12.757811,13.351564 13.0546875,13.0546875 C13.351564,12.757811 13.5,12.406252 13.5,12 C13.5,11.593748 13.351564,11.242189 13.0546875,10.9453125 C12.757811,10.648436 12.406252,10.5 12,10.5 C11.593748,10.5 11.242189,10.648436 10.9453125,10.9453125 C10.648436,11.242189 10.5,11.593748 10.5,12 C10.5,12.406252 10.648436,12.757811 10.9453125,13.0546875 C11.242189,13.351564 11.593748,13.5 12,13.5 Z' }] } }) ================================================ FILE: src/renderer/components/Icons/sync.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'sync': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/task-history.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-history': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M12,0C5.383,0,0,5.383,0,12s5.383,12,12,12s12-5.383,12-12S18.617,0,12,0z M19,13h-8V5h2v6h6V13z' }] } }) ================================================ FILE: src/renderer/components/Icons/task-pause-line.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-pause-line': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/task-pause.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-pause': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M9,1H3C2.447,1,2,1.447,2,2v20c0,0.553,0.447,1,1,1h6c0.553,0,1-0.447,1-1V2C10,1.447,9.553,1,9,1z' }, { 'd': 'M21,1h-6c-0.553,0-1,0.447-1,1v20c0,0.553,0.447,1,1,1h6c0.553,0,1-0.447,1-1V2C22,1.447,21.553,1,21,1z' }] } }) ================================================ FILE: src/renderer/components/Icons/task-restart.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-restart': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/task-start-line.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-start-line': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/task-start.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-start': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M20.555,11.168l-15-10c-0.307-0.204-0.702-0.224-1.026-0.05C4.203,1.292,4,1.631,4,2v20 c0,0.369,0.203,0.708,0.528,0.882C4.676,22.961,4.838,23,5,23c0.194,0,0.388-0.057,0.555-0.168l15-10C20.833,12.646,21,12.334,21,12 S20.833,11.354,20.555,11.168z' }] } }) ================================================ FILE: src/renderer/components/Icons/task-stop-line.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-stop-line': { 'width': 24, 'height': 24, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/task-stop.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'task-stop': { 'width': 24, 'height': 24, 'paths': [{ 'd': 'M22,1H2C1.447,1,1,1.447,1,2v20c0,0.553,0.447,1,1,1h20c0.553,0,1-0.447,1-1V2C23,1.447,22.553,1,22,1z' }] } }) ================================================ FILE: src/renderer/components/Icons/trash.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'trash': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/video.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'video': { 'width': 24, 'height': 24, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2' } } }) ================================================ FILE: src/renderer/components/Icons/win-close.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'win-close': { 'width': 12, 'height': 12, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '1' } } }) ================================================ FILE: src/renderer/components/Icons/win-maximize.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'win-maximize': { 'width': 12, 'height': 12, 'raw': ` `, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '1' } } }) ================================================ FILE: src/renderer/components/Icons/win-minimize.js ================================================ import Icon from '@/components/Icons/Icon' Icon.register({ 'win-minimize': { 'width': 12, 'height': 12, 'raw': ``, 'g': { 'stroke': 'currentColor', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '1' } } }) ================================================ FILE: src/renderer/components/Locale/index.js ================================================ import resources from '@shared/locales/all' import LocaleManager from '@shared/locales/LocaleManager' const localeManager = new LocaleManager({ resources }) export function getLocaleManager () { return localeManager } ================================================ FILE: src/renderer/components/Logo/Logo.vue ================================================ ================================================ FILE: src/renderer/components/Logo/LogoMini.vue ================================================ ================================================ FILE: src/renderer/components/Main.vue ================================================ ================================================ FILE: src/renderer/components/Msg/index.js ================================================ const queue = [] const maxLength = 5 export default { install: function (Vue, Message, defaultOption = {}) { Vue.prototype.$msg = new Proxy(Message, { get (obj, prop) { return (arg) => { if (!(arg instanceof Object)) { arg = { message: arg } } const task = { run () { obj[prop]({ ...defaultOption, ...arg, onClose (...data) { const currentTask = queue.pop() if (currentTask) { currentTask.run() } if (arg.onClose) { arg.onClose(...data) } } }) } } if (queue.length >= maxLength) { queue.pop() } queue.unshift(task) if (queue.length === 1) { queue.pop().run() } } } }) } } ================================================ FILE: src/renderer/components/Native/DynamicTray.vue ================================================ ================================================ FILE: src/renderer/components/Native/EngineClient.vue ================================================ ================================================ FILE: src/renderer/components/Native/Ipc.vue ================================================ ================================================ FILE: src/renderer/components/Native/SelectDirectory.vue ================================================ ================================================ FILE: src/renderer/components/Native/ShowInFolder.vue ================================================ ================================================ FILE: src/renderer/components/Native/TitleBar.vue ================================================ ================================================ FILE: src/renderer/components/Preference/Advanced.vue ================================================ ================================================ FILE: src/renderer/components/Preference/Basic.vue ================================================ ================================================ FILE: src/renderer/components/Preference/HistoryDirectory.vue ================================================ ================================================ FILE: src/renderer/components/Preference/Index.vue ================================================ ================================================ FILE: src/renderer/components/Preference/Lab.vue ================================================ ================================================ FILE: src/renderer/components/Preference/ThemeSwitcher.vue ================================================ ================================================ FILE: src/renderer/components/Speedometer/Speedometer.vue ================================================ ================================================ FILE: src/renderer/components/Subnav/PreferenceSubnav.vue ================================================ ================================================ FILE: src/renderer/components/Subnav/SubnavSwitcher.vue ================================================ ================================================ FILE: src/renderer/components/Subnav/TaskSubnav.vue ================================================ ================================================ FILE: src/renderer/components/Task/AddTask.vue ================================================ ================================================ FILE: src/renderer/components/Task/Index.vue ================================================ ================================================ FILE: src/renderer/components/Task/SelectTorrent.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskActions.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskItem.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskItemActions.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskList.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskProgress.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskProgressInfo.vue ================================================ ================================================ FILE: src/renderer/components/Task/TaskStatus.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/Index.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/TaskActivity.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/TaskFiles.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/TaskGeneral.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/TaskPeers.vue ================================================ ================================================ FILE: src/renderer/components/TaskDetail/TaskTrackers.vue ================================================ ================================================ FILE: src/renderer/components/TaskGraphic/Atom.vue ================================================ ================================================ FILE: src/renderer/components/TaskGraphic/Index.vue ================================================ ================================================ FILE: src/renderer/components/Theme/Dark/Variables.scss ================================================ /* Color -------------------------- */ $--dk-border-color-base: #555; $--dk-font-color-base: #eee; $--dk-action-color-base: #eee; /* App -------------------------- */ $--dk-app-background: transparent !default; $--dk-titlebar-actions-color: $--dk-action-color-base !default; $--dk-titlebar-close-active-color: #fff !default; $--dk-titlebar-actions-active-background: rgba(0, 0, 0, 0.9) !default; $--dk-titlebar-close-active-background: #fd0007 !default; $--dk-app-logo-color: #eee !default; $--dk-app-version-color: #a2a3a4 !default; $--dk-app-engine-title-color: #a2a3a4 !default; $--dk-app-engine-info-color: #777 !default; $--dk-app-copyright-color: #a2a3a4 !default; /* Aside -------------------------- */ $--dk-aside-background: rgba(0, 0, 0, 0.9) !default; $--dk-aside-text-color: #fff !default; /* SubNav -------------------------- */ $--dk-subnav-background: #2D2D2D !default; $--dk-subnav-title-color: #fff !default; $--dk-subnav-action-color: #fff !default; $--dk-subnav-text-color: #aaa !default; $--dk-subnav-active-text-color: #fff !default; $--dk-subnav-active-background: #444 !default; $--dk-subnav-border-color: #ccc !default; /* Main -------------------------- */ $--dk-main-background: #343434 !default; /* Panel -------------------------- */ $--dk-panel-background: #343434 !default; $--dk-panel-title-color: #fff !default; $--dk-panel-border-color: #EBECF0 !default; /* Form Actions -------------------------- */ $--dk-form-actions-background: #343434 !default; /* Task -------------------------- */ $--dk-task-action-color: $--dk-action-color-base !default; $--dk-task-action-hover-color: $--color-primary !default; $--dk-task-item-backgroud: #2d2d2d !default; $--dk-task-item-border-color: $--dk-border-color-base !default; $--dk-task-item-hover-border-color: $--color-primary !default; $--dk-task-item-hover-background: $--color-primary !default; $--dk-task-item-text-color: #eee !default; $--dk-task-item-action-background: #4a4a4a !default; $--dk-task-item-action-hover-background: $--color-primary !default; $--dk-task-item-action-border-color: #5f5f5f !default; $--dk-task-item-action-hover-border-color: $--color-primary !default; $--dk-task-item-action-color: $--dk-action-color-base !default; $--dk-task-item-action-hover-color: #fff !default; $--dk-no-task-color: #aaa !default; $--dk-add-task-dialog-footer-background: #4a4a4a !default; $--dk-task-detail-box-border: #565656 !default; /* Preference -------------------------- */ $--dk-preference-form-text-color: #dfdfdf !default; /* Speedometer -------------------------- */ $--dk-speedometer-background: #333 !default; $--dk-speedometer-border-color: #5f5f5f !default; $--dk-speedometer-hover-border-color: #9b9b9b !default; $--dk-speedometer-primary-color: $--color-primary !default; $--dk-speedometer-stopped-color: #9b9b9b !default; $--dk-speedometer-text-color: #9b9b9b !default; /* Task Graphic -------------------------- */ $--dk-graphic-box-background: #3f3f3f !default; $--dk-graphic-atom-color-0: #161b22 !default; $--dk-graphic-atom-color-1: #0e4429 !default; $--dk-graphic-atom-color-2: #006d32 !default; $--dk-graphic-atom-color-3: #26a641 !default; $--dk-graphic-atom-color-4: #39d353 !default; /* Element UI -------------------------- */ $--dk-dialog-background: #343434 !default; $--dk-dialog-text-color: #9b9b9b !default; $--dk-table-background: #2a2b2c !default; $--dk-table-striped-background: #1f2122 !default; $--dk-table-hover-background: #565656 !default; $--dk-table-th-background: #1f2122 !default; $--dk-table-text-color: #9b9b9b !default; $--dk-table-border-color: #565656 !default; $--dk-popover-background: #3d3d3d !default; $--dk-popover-border-color: $--dk-border-color-base !default; ================================================ FILE: src/renderer/components/Theme/Dark.scss ================================================ .theme-dark { .title-bar .window-actions > li { color: $--dk-titlebar-actions-color; &:hover { background-color: $--dk-titlebar-actions-active-background; } &.win-close-btn:hover { background-color: $--dk-titlebar-close-active-background; } } .logo > a { color: $--dk-app-logo-color; } .app-info .app-version span { color: $--dk-app-version-color; } .app-info .engine-info { h4 { color: $--dk-app-engine-title-color; } ul { color: $--dk-app-engine-info-color; } } .copyright a { color: $--dk-app-copyright-color; } .aside { background-color: $--dk-aside-background; } .subnav { background-color: $--dk-subnav-background; color: $--dk-subnav-text-color; } .subnav-inner { h3 { color: $--dk-subnav-title-color; } ul li { &:hover, &.active { background-color: $--dk-subnav-active-background; i, span, svg { color: $--dk-subnav-active-text-color; } } } } .form-actions { background: $--dk-form-actions-background; } .main { background-color: $--dk-main-background; } .panel { background-color: $--dk-panel-background; } .panel .panel-header { border-bottom-color: rgba(255, 255, 255, 0.1); h4 { color: $--dk-panel-title-color; } } .task-item { background-color: $--dk-task-item-backgroud; border-color: $--dk-task-item-border-color; &:hover { border-color: $--dk-task-item-hover-border-color; } } .selected .task-item { border-color: $--dk-task-item-hover-border-color; } .task-name { color: $--dk-task-item-text-color; } .task-actions { color: $--dk-task-action-color; } .task-item-actions { background-color: $--dk-task-item-action-background; border-color: $--dk-task-item-action-border-color; &:hover { border-color: $--dk-task-item-action-hover-border-color; color: $--dk-task-item-action-hover-color; background-color: $--dk-task-item-action-hover-background; } } .mo-speedometer { background-color: $--dk-speedometer-background; border-color: $--dk-speedometer-border-color; } .no-task { color: $--dk-no-task-color; } .mo-table-wrapper { border-color: $--dk-table-border-color; } .graphic-box { border-color: $--dk-task-detail-box-border; background-color: $--dk-graphic-box-background; } .graphic-atom-s0 { fill: $--dk-graphic-atom-color-0; } .graphic-atom-s1 { fill: $--dk-graphic-atom-color-1; } .graphic-atom-s2 { fill: $--dk-graphic-atom-color-2; } .graphic-atom-s3 { fill: $--dk-graphic-atom-color-3; } .graphic-atom-s4 { fill: $--dk-graphic-atom-color-4; } .form-static-value { color: #e7e7e7; } /* Element UI -------------------------- */ .el-progress-bar__outer { background-color: #4a4a4a; } .el-input__inner, .el-textarea__inner { background-color: #373737; border-color: #5f5f5f; color: #eee; &:focus { outline: none; border-color: $--color-primary; } &::placeholder { color: #777; } } .el-input.is-disabled .el-input__inner { background-color: #373737; border-color: #5f5f5f; color: #aaa; } .el-input-group__append, .el-input-group__prepend { background-color: #333; border-color: #5f5f5f; color: #e7e7e7; } .el-input-number__increase, .el-input-number__decrease { background-color: #333; color: #e7e7e7; } .el-input-number__decrease { border-right-color: #5f5f5f; } .el-input-number__increase { border-left-color: #5f5f5f; } .el-input-number.is-controls-right .el-input-number__increase { border-left-color: #5f5f5f; border-bottom-color: #5f5f5f; } .el-input-number.is-controls-right .el-input-number__decrease { border-left-color: #5f5f5f; } .el-form-item__label, .el-checkbox, .el-radio { color: $--dk-preference-form-text-color; } .el-switch__core, .el-checkbox__inner { border-color: #606060; background-color: #5c5d5f; } .el-select .el-input .el-select__caret { color: #e7e7e7; } .el-select-dropdown { background-color: #3d3d3d; border-color: #606060; } .el-select-dropdown__item { color: #eee; &.selected { color: $--color-primary; } &.hover, &:hover { background-color: $--color-primary; color: #fff; } } .el-select-dropdown.is-multiple .el-select-dropdown__item.selected { background-color: #3d3d3d; color: $--color-primary; } .el-upload-dragger { background-color: #2d2d2d; border-color: #606060; > i, .el-upload__text { color: #a2a3a4; } } .el-popper[x-placement^="top"] .popper__arrow { border-top-color: #606060; &:after { border-top-color: #3d3d3d; } } .el-popper[x-placement^="right"] .popper__arrow { border-right-color: #606060; &:after { border-right-color: #3d3d3d; } } .el-popper[x-placement^="bottom"] .popper__arrow { border-bottom-color: #606060; &:after { border-bottom-color: #3d3d3d; } } .el-popper[x-placement^="left"] .popper__arrow { border-left-color: #606060; &:after { border-left-color: #3d3d3d; } } .el-button { background-color: #5b5b5b; border-color: #606060; color: #e6e6e6; &:hover, &:focus { color: $--color-primary-light-4; border-color: #606060; background-color: #333; } } .el-button--primary { color: #eee; background-color: $--color-primary; border-color: $--color-primary; &:hover, &:focus { background: $--color-primary; border-color: $--color-primary; color: #fff; } } .el-button--danger.is-plain { color: #ff6157; background-color: #5b5b5b; border-color: #ffc0bc; } .el-button--danger.is-plain:hover, .el-button--danger.is-plain:focus { background-color: #ff6157; border-color: #ff6157; color: #fff; } /* Message */ .el-message { background-color: #2d2d2d; border-color: #606060; } .el-message__closeBtn { color: rgba(255, 255, 255, 0.3); } .el-message--info .el-message__content { color: #a2a3a4; } .el-message--success { background-color: #2d2d2d; border-color: #606060; .el-message__content { color: #67c23a; } } .el-message--warning { background-color: #2d2d2d; border-color: #606060; .el-message__content { color: #e6a23c; } } .el-message--error { background-color: #2d2d2d; border-color: #606060; .el-message__content { color: #f56c6c; } } /* Dialog */ .el-dialog { background-color: $--dk-dialog-background; .el-dialog__body { color: $--dk-dialog-text-color; } } .add-task-dialog .el-dialog__footer { background-color: $--dk-add-task-dialog-footer-background; } .torrent-file-list { border-color: $--dk-table-border-color; } .el-table { background-color: $--dk-table-background; color: $--dk-table-text-color; tr { background-color: $--dk-table-background; } th { background-color: $--dk-table-th-background; color: $--dk-table-text-color; } th.is-leaf, td { background-color: $--dk-table-background; color: $--dk-table-text-color; border-bottom-color: $--dk-table-border-color; } } .el-table thead th.is-leaf { background-color: $--dk-table-th-background; } .el-table--enable-row-hover .el-table__body tr:hover > td, .el-table--enable-row-hover .el-table__body tr.el-table__row--striped:hover > td { background-color: $--dk-table-hover-background; } .el-table--group::after, .el-table--border::after, .el-table::before { background-color: $--dk-table-border-color; } .el-table--striped .el-table__body tr.el-table__row--striped td { background-color: $--dk-table-striped-background; } /* Tabs */ .el-tabs__item { color: #a2a3a4; &.is-active { color: $--color-primary; } } .el-tabs__nav-wrap::after { background-color: #606060; } .form-preference .el-form-item__content { color: $--dk-preference-form-text-color; } .form-preference .el-switch__label { color: $--dk-preference-form-text-color; } .form-preference .el-checkbox__input.is-checked + .el-checkbox__label { color: $--dk-preference-form-text-color; } .form-preference .el-form-item { a { color: #dfdfdf; &:hover { color: #eee; } &:active { color: #eee; } } } /* Divider */ .el-divider { background-color: #666; } .el-divider__text { background-color: $--dk-panel-background; color: #a7a7a7; } /* Popover */ .el-popover { background-color: $--dk-popover-background; border-color: $--dk-popover-border-color; } .el-tag.el-tag--info.el-tag--light { background-color: #5b5b5b; border-color: #606060; color: #e6e6e6; } .el-tag__close.el-icon-close { color: #2d2d2d; } } ================================================ FILE: src/renderer/components/Theme/Default.scss ================================================ html, body { height: 100%; padding: 0; } body { font-family: "Monospaced Number", "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; font-variant: tabular-nums; font-size: $--font-size-medium; } ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-thumb { border-radius: 8px; background-color: rgba(0, 0, 0, 0.4); } ::-webkit-scrollbar-thumb:window-inactive { background-color: rgba(0, 0, 0, 0.25); } ::-webkit-scrollbar-corner { background: transparent; } ::-webkit-resizer { display: none; } img { width: auto; height: auto; max-width: 100%; max-height: 100%; } iframe { border: 0; } .clearfix { @include clearfix(); } /* Element UI -------------------------- */ .el-progress-bar__inner { transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s; } .el-progress--line.is-text { .el-progress-bar__inner::before { content: ""; opacity: 0; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #fff; border-radius: 10px; animation: mo-progress-active 2.4s cubic-bezier(0.23, 1, 0.32, 1) infinite; } } @keyframes mo-progress-active { 0% { opacity: 0.1; width: 0; } 20% { opacity: 0.5; width: 0; } 100% { opacity: 0; width: 100%; } } .el-message-box__wrapper { outline: none; } .el-message__content { line-height: 18px; word-break: break-all; a { color: $--link-color; } } .tab-title-dialog { .el-dialog__header { padding: 0 20px; position: relative; z-index: 10; } .el-dialog__body { padding-top: 10px; padding-bottom: 10px; } } .el-form-item--mini .el-form-item__info { padding-top: 1px; } .el-form-item__info { font-size: 12px; line-height: 1; padding-top: 4px; color: $--color-info; > a { margin-right: 10px; } } .el-input-number.el-input-number--mini { &.is-controls-left .el-input__inner { padding-left: 34px; padding-right: 5px; } &.is-controls-right .el-input__inner { padding-left: 5px; padding-right: 34px; } } .el-drawer__container { outline: none; } /* App Main -------------------------- */ #app, #container { height: 100%; background-color: $--app-background; } .draggable { -webkit-app-region: drag; user-select: none; } .non-draggable { -webkit-app-region: no-drag; } .aside { background-color: $--aside-background; color: $--aside-text-color; } .subnav { background-color: $--subnav-background; color: $--subnav-text-color; } .main { background-color: $--main-background; } .subnav-inner { margin-top: 44px; padding: 0 16px; user-select: none; h3 { font-size: 16px; color: $--subnav-title-color; font-weight: normal; line-height: 24px; margin: 0 0 28px; } ul { list-style: none; padding: 0; margin: 0; cursor: default; li { margin-bottom: 8px; padding: 8px 10px; font-size: 0; line-height: 20px; border-radius: 3px; cursor: pointer; i, span { display: inline-block; vertical-align: middle; font-size: 14px; } &:hover, &.active { background-color: $--subnav-active-background; i, span, svg { color: $--subnav-active-text-color; } } } } } .subnav-icon { padding: 2px; height: 16px; margin-right: 12px; svg { width: 16px; height: 16px; } } .content { padding: 0; } .panel { background: $--panel-background; .panel-header { position: relative; padding: 44px 0 12px; margin: 0 16px; border-bottom: 2px solid $--panel-border-color; user-select: none; h4 { margin: 0; color: $--panel-title-color; font-size: 16px; font-weight: normal; line-height: 24px; } } .panel-content { position: relative; padding: 0; height: 100%; } } .form-actions { background: $--form-actions-background; } .mo-table-wrapper { border: 1px solid $--table-border-color; border-bottom: none; overflow-x: hidden; overflow-y: auto; .el-table th { padding: 2px 0; } } .graphic-box { padding: 0.5rem 0.375rem; margin-bottom: 1.5rem; font-size: 0; line-height: 0; border: 1px solid $--task-detail-box-border; border-radius: $--border-radius-base; &> svg { display: block; margin: 0 auto; } } .form-static-value { word-break: break-all; color: $--input-font-color; } @media only screen and (max-width:567px) { .hidden-xs-only { display:none !important } } @media only screen and (min-width: 568px) { .panel { .panel-header { margin-left: 36px; margin-right: 36px; } } .task-list { padding-left: 36px; padding-right: 36px; } .form-preference { padding-left: 36px; } .form-actions { padding: 24px 36px; } .mo-speedometer { right: 36px; } .hidden-sm-and-up { display: none !important; } } @media only screen and (min-width: 568px) and (max-width: 991px) { .hidden-sm-only { display: none !important; } } @media only screen and (max-width: 791px) { .hidden-sm-and-down { display: none !important; } } @media only screen and (min-width: 792px) { .hidden-md-and-up { display: none !important; } } ================================================ FILE: src/renderer/components/Theme/Index.scss ================================================ /* Normalize.css -------------------------- */ @import '~normalize.css/normalize.css'; /* Element UI -------------------------- */ @import '~element-ui/packages/theme-chalk/src/index'; /* Theme Light (default) -------------------------- */ @import './Default.scss'; /* Theme Dark -------------------------- */ @import './Dark.scss'; ================================================ FILE: src/renderer/components/Theme/Light/Variables.scss ================================================ /* App -------------------------- */ $--app-background: transparent !default; $--titlebar-actions-color: #1f1f1f !default; $--titlebar-actions-active-background: #eee !default; $--titlebar-close-active-color: #fff !default; $--titlebar-close-active-background: #fd0007 !default; $--app-logo-color: #4D515A !default; $--app-version-color: $--color-text-regular !default; $--app-engine-title-color: $--color-text-regular !default; $--app-engine-info-color: $--color-text-secondary !default; $--app-copyright-color: $--color-text-regular !default; /* Aside -------------------------- */ $--aside-background: rgba(0, 0, 0, 0.8) !default; $--aside-text-color: #fff !default; /* SubNav -------------------------- */ $--subnav-background: #F4F5F7 !default; $--subnav-title-color: $--color-text-primary !default; $--subnav-action-color: #4D515A !default; $--subnav-text-color: #4D515A !default; $--subnav-active-text-color: $--color-primary !default; $--subnav-active-background: #EAECF0 !default; $--subnav-border-color: #ccc !default; /* Main -------------------------- */ $--main-background: $--color-white !default; /* Panel -------------------------- */ $--panel-background: $--color-white !default; $--panel-title-color: $--color-text-primary !default; $--panel-border-color: rgba(0, 0, 0, 0.1) !default; /* Form Actions -------------------------- */ $--form-actions-background: $--color-white !default; /* Task -------------------------- */ $--task-action-color: #4d515a !default; $--task-action-hover-color: $--color-primary !default; $--task-action-disabled-color: rgba(77, 81, 90, 0.5) !default; $--task-item-background: #fff !default; $--task-item-border-color: #ccc !default; $--task-item-hover-border-color: $--color-primary !default; $--task-item-hover-background: $--color-primary !default; $--task-item-text-color: #505753 !default; $--task-item-action-background: #fff !default; $--task-item-action-hover-background: $--color-primary !default; $--task-item-action-border-color: #F5F5F5 !default; $--task-item-action-hover-border-color: $--color-primary !default; $--task-item-action-color: #9B9B9B !default; $--task-item-action-hover-color: #fff !default; $--no-task-color: #eee !default; $--add-task-dialog-footer-background: #f5f5f5 !default; $--task-detail-box-border: #ebeef5 !default; /* Preference -------------------------- */ $--preference-form-text-color: #4c4c4c; /* Speedometer -------------------------- */ $--speedometer-background: $--color-white !default; $--speedometer-border-color: #ccc !default; $--speedometer-hover-border-color: #9b9b9b !default; $--speedometer-primary-color: $--color-primary !default; $--speedometer-stopped-color: #9b9b9b !default; $--speedometer-text-color: #9b9b9b !default; /* Task Graphic -------------------------- */ $--graphic-box-background: transparent !default; $--graphic-atom-outline-color: rgba(27, 31, 35, 0.06) !default; $--graphic-atom-color-0: #ebedf0 !default; $--graphic-atom-color-1: #9be9a8 !default; $--graphic-atom-color-2: #40c463 !default; $--graphic-atom-color-3: #30a14e !default; $--graphic-atom-color-4: #39d353 !default; /* Element UI -------------------------- */ $--dialog-background: #fff !default; $--table-border-color: #ebeef5 !default; ================================================ FILE: src/renderer/components/Theme/Variables.scss ================================================ /* Element Chalk Variables */ // Special comment for theme configurator // type|skipAutoTranslation|Category|Order // skipAutoTranslation 1 /* Transition -------------------------- */ $--all-transition: all .25s cubic-bezier(.645,.045,.355,1) !default; $--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default; $--fade-linear-transition: opacity 200ms linear !default; $--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default; $--border-transition-base: border-color .25s cubic-bezier(.645,.045,.355,1) !default; $--color-transition-base: color .25s cubic-bezier(.645,.045,.355,1) !default; /* Color -------------------------- */ /// color|1|Brand Color|0 $--color-primary: #5b5bfa !default; /// color|1|Background Color|4 $--color-white: #FFFFFF !default; /// color|1|Background Color|4 $--color-black: #000000 !default; $--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */ $--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */ $--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */ $--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */ $--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */ $--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */ $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */ $--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */ $--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */ /// color|1|Functional Color|1 $--color-success: #67C23A !default; /// color|1|Functional Color|1 $--color-warning: #E6A23C !default; /// color|1|Functional Color|1 $--color-danger: #F56C6C !default; /// color|1|Functional Color|1 $--color-info: #909399 !default; $--color-success-light: mix($--color-white, $--color-success, 80%) !default; $--color-warning-light: mix($--color-white, $--color-warning, 80%) !default; $--color-danger-light: mix($--color-white, $--color-danger, 80%) !default; $--color-info-light: mix($--color-white, $--color-info, 80%) !default; $--color-success-lighter: mix($--color-white, $--color-success, 90%) !default; $--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default; $--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default; $--color-info-lighter: mix($--color-white, $--color-info, 90%) !default; /// color|1|Font Color|2 $--color-text-primary: #303133 !default; /// color|1|Font Color|2 $--color-text-regular: #606266 !default; /// color|1|Font Color|2 $--color-text-secondary: #909399 !default; /// color|1|Font Color|2 $--color-text-placeholder: #C0C4CC !default; /// color|1|Border Color|3 $--border-color-base: #DCDFE6 !default; /// color|1|Border Color|3 $--border-color-light: #E4E7ED !default; /// color|1|Border Color|3 $--border-color-lighter: #EBEEF5 !default; /// color|1|Border Color|3 $--border-color-extra-light: #F2F6FC !default; // Background /// color|1|Background Color|4 $--background-color-base: #F5F7FA !default; /* Link -------------------------- */ $--link-color: $--color-primary-light-2 !default; $--link-hover-color: $--color-primary !default; /* Border -------------------------- */ $--border-width-base: 1px !default; $--border-style-base: solid !default; $--border-color-hover: $--color-text-placeholder !default; $--border-base: $--border-width-base $--border-style-base $--border-color-base !default; /// borderRadius|1|Radius|0 $--border-radius-base: 4px !default; /// borderRadius|1|Radius|0 $--border-radius-small: 2px !default; /// borderRadius|1|Radius|0 $--border-radius-circle: 100% !default; /// borderRadius|1|Radius|0 $--border-radius-zero: 0 !default; // Box-shadow /// boxShadow|1|Shadow|1 $--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default; // boxShadow|1|Shadow|1 $--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default; /// boxShadow|1|Shadow|1 $--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default; /* Fill -------------------------- */ $--fill-base: $--color-white !default; /* Typography -------------------------- */ $--font-path: 'fonts' !default; /// fontSize|1|Font Size|0 $--font-size-extra-large: 20px !default; /// fontSize|1|Font Size|0 $--font-size-large: 18px !default; /// fontSize|1|Font Size|0 $--font-size-medium: 16px !default; /// fontSize|1|Font Size|0 $--font-size-base: 14px !default; /// fontSize|1|Font Size|0 $--font-size-small: 13px !default; /// fontSize|1|Font Size|0 $--font-size-extra-small: 12px !default; /// fontWeight|1|Font Weight|1 $--font-weight-primary: 500 !default; /// fontWeight|1|Font Weight|1 $--font-weight-secondary: 100 !default; /// fontLineHeight|1|Line Height|2 $--font-line-height-primary: 24px !default; /// fontLineHeight|1|Line Height|2 $--font-line-height-secondary: 16px !default; $--font-color-disabled-base: #bbb !default; /* Size -------------------------- */ $--size-base: 14px !default; /* z-index -------------------------- */ $--index-normal: 1 !default; $--index-top: 1000 !default; $--index-popper: 2100 !default; /* Disable base -------------------------- */ $--disabled-fill-base: $--background-color-base !default; $--disabled-color-base: $--color-text-placeholder !default; $--disabled-border-base: $--border-color-light !default; /* Icon -------------------------- */ $--icon-color: #666 !default; $--icon-color-base: $--color-info !default; /* Checkbox -------------------------- */ /// fontSize||Font|1 $--checkbox-font-size: 14px !default; /// fontWeight||Font|1 $--checkbox-font-weight: normal !default; /// color||Color|0 $--checkbox-font-color: $--color-text-regular !default; $--checkbox-input-height: 14px !default; $--checkbox-input-width: 14px !default; /// borderRadius||Border|2 $--checkbox-border-radius: $--border-radius-small !default; /// color||Color|0 $--checkbox-background-color: $--color-white !default; $--checkbox-input-border: $--border-base !default; /// color||Color|0 $--checkbox-disabled-border-color: $--border-color-base !default; $--checkbox-disabled-input-fill: #edf2fc !default; $--checkbox-disabled-icon-color: $--color-text-placeholder !default; $--checkbox-disabled-checked-input-fill: $--border-color-extra-light !default; $--checkbox-disabled-checked-input-border-color: $--border-color-base !default; $--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default; /// color||Color|0 $--checkbox-checked-font-color: $--color-primary !default; $--checkbox-checked-input-border-color: $--color-primary !default; /// color||Color|0 $--checkbox-checked-background-color: $--color-primary !default; $--checkbox-checked-icon-color: $--fill-base !default; $--checkbox-input-border-color-hover: $--color-primary !default; /// height||Other|4 $--checkbox-bordered-height: 40px !default; /// padding||Spacing|3 $--checkbox-bordered-padding: 9px 20px 9px 10px !default; /// padding||Spacing|3 $--checkbox-bordered-medium-padding: 7px 20px 7px 10px !default; /// padding||Spacing|3 $--checkbox-bordered-small-padding: 5px 15px 5px 10px !default; /// padding||Spacing|3 $--checkbox-bordered-mini-padding: 3px 15px 3px 10px !default; $--checkbox-bordered-medium-input-height: 14px !default; $--checkbox-bordered-medium-input-width: 14px !default; /// height||Other|4 $--checkbox-bordered-medium-height: 36px !default; $--checkbox-bordered-small-input-height: 12px !default; $--checkbox-bordered-small-input-width: 12px !default; /// height||Other|4 $--checkbox-bordered-small-height: 32px !default; $--checkbox-bordered-mini-input-height: 12px !default; $--checkbox-bordered-mini-input-width: 12px !default; /// height||Other|4 $--checkbox-bordered-mini-height: 28px !default; /// color||Color|0 $--checkbox-button-checked-background-color: $--color-primary !default; /// color||Color|0 $--checkbox-button-checked-font-color: $--color-white !default; /// color||Color|0 $--checkbox-button-checked-border-color: $--color-primary !default; /* Radio -------------------------- */ /// fontSize||Font|1 $--radio-font-size: $--font-size-base !default; /// fontWeight||Font|1 $--radio-font-weight: $--font-weight-primary !default; /// color||Color|0 $--radio-font-color: $--color-text-regular !default; $--radio-input-height: 14px !default; $--radio-input-width: 14px !default; /// borderRadius||Border|2 $--radio-input-border-radius: $--border-radius-circle !default; /// color||Color|0 $--radio-input-background-color: $--color-white !default; $--radio-input-border: $--border-base !default; /// color||Color|0 $--radio-input-border-color: $--border-color-base !default; /// color||Color|0 $--radio-icon-color: $--color-white !default; $--radio-disabled-input-border-color: $--disabled-border-base !default; $--radio-disabled-input-fill: $--disabled-fill-base !default; $--radio-disabled-icon-color: $--disabled-fill-base !default; $--radio-disabled-checked-input-border-color: $--disabled-border-base !default; $--radio-disabled-checked-input-fill: $--disabled-fill-base !default; $--radio-disabled-checked-icon-color: $--color-text-placeholder !default; /// color||Color|0 $--radio-checked-font-color: $--color-primary !default; /// color||Color|0 $--radio-checked-input-border-color: $--color-primary !default; /// color||Color|0 $--radio-checked-input-background-color: $--color-white !default; /// color||Color|0 $--radio-checked-icon-color: $--color-primary !default; $--radio-input-border-color-hover: $--color-primary !default; $--radio-bordered-height: 40px !default; $--radio-bordered-padding: 12px 20px 0 10px !default; $--radio-bordered-medium-padding: 10px 20px 0 10px !default; $--radio-bordered-small-padding: 8px 15px 0 10px !default; $--radio-bordered-mini-padding: 6px 15px 0 10px !default; $--radio-bordered-medium-input-height: 14px !default; $--radio-bordered-medium-input-width: 14px !default; $--radio-bordered-medium-height: 36px !default; $--radio-bordered-small-input-height: 12px !default; $--radio-bordered-small-input-width: 12px !default; $--radio-bordered-small-height: 32px !default; $--radio-bordered-mini-input-height: 12px !default; $--radio-bordered-mini-input-width: 12px !default; $--radio-bordered-mini-height: 28px !default; /// fontSize||Font|1 $--radio-button-font-size: $--font-size-base !default; /// color||Color|0 $--radio-button-checked-background-color: $--color-primary !default; /// color||Color|0 $--radio-button-checked-font-color: $--color-white !default; /// color||Color|0 $--radio-button-checked-border-color: $--color-primary !default; $--radio-button-disabled-checked-fill: $--border-color-extra-light !default; /* Select -------------------------- */ $--select-border-color-hover: $--border-color-hover !default; $--select-disabled-border: $--disabled-border-base !default; /// fontSize||Font|1 $--select-font-size: $--font-size-base !default; $--select-close-hover-color: $--color-text-secondary !default; $--select-input-color: $--color-text-placeholder !default; $--select-multiple-input-color: #666 !default; /// color||Color|0 $--select-input-focus-border-color: $--color-primary !default; /// fontSize||Font|1 $--select-input-font-size: 14px !default; $--select-option-color: $--color-text-regular !default; $--select-option-disabled-color: $--color-text-placeholder !default; $--select-option-disabled-background: $--color-white !default; /// height||Other|4 $--select-option-height: 34px !default; $--select-option-hover-background: $--background-color-base !default; /// color||Color|0 $--select-option-selected-font-color: $--color-primary !default; $--select-option-selected-hover: $--background-color-base !default; $--select-group-color: $--color-info !default; $--select-group-height: 30px !default; $--select-group-font-size: 12px !default; $--select-dropdown-background: $--color-white !default; $--select-dropdown-shadow: $--box-shadow-light !default; $--select-dropdown-empty-color: #999 !default; /// height||Other|4 $--select-dropdown-max-height: 274px !default; $--select-dropdown-padding: 6px 0 !default; $--select-dropdown-empty-padding: 10px 0 !default; $--select-dropdown-border: solid 1px $--border-color-light !default; /* Alert -------------------------- */ $--alert-padding: 8px 16px !default; /// borderRadius||Border|2 $--alert-border-radius: $--border-radius-base !default; /// fontSize||Font|1 $--alert-title-font-size: 13px !default; /// fontSize||Font|1 $--alert-description-font-size: 12px !default; /// fontSize||Font|1 $--alert-close-font-size: 12px !default; /// fontSize||Font|1 $--alert-close-customed-font-size: 13px !default; $--alert-success-color: $--color-success-lighter !default; $--alert-info-color: $--color-info-lighter !default; $--alert-warning-color: $--color-warning-lighter !default; $--alert-danger-color: $--color-danger-lighter !default; /// height||Other|4 $--alert-icon-size: 16px !default; /// height||Other|4 $--alert-icon-large-size: 28px !default; /* MessageBox -------------------------- */ /// color||Color|0 $--messagebox-title-color: $--color-text-primary !default; $--msgbox-width: 420px !default; $--msgbox-border-radius: 4px !default; /// fontSize||Font|1 $--messagebox-font-size: $--font-size-large !default; /// fontSize||Font|1 $--messagebox-content-font-size: $--font-size-base !default; /// color||Color|0 $--messagebox-content-color: $--color-text-regular !default; /// fontSize||Font|1 $--messagebox-error-font-size: 12px !default; $--msgbox-padding-primary: 15px !default; /// color||Color|0 $--messagebox-success-color: $--color-success !default; /// color||Color|0 $--messagebox-info-color: $--color-info !default; /// color||Color|0 $--messagebox-warning-color: $--color-warning !default; /// color||Color|0 $--messagebox-danger-color: $--color-danger !default; /* Message -------------------------- */ $--message-shadow: $--box-shadow-base !default; $--message-min-width: 380px !default; $--message-background-color: #edf2fc !default; $--message-padding: 15px 15px 15px 20px !default; /// color||Color|0 $--message-close-icon-color: $--color-text-placeholder !default; /// height||Other|4 $--message-close-size: 16px !default; /// color||Color|0 $--message-close-hover-color: $--color-text-secondary !default; /// color||Color|0 $--message-success-font-color: $--color-success !default; /// color||Color|0 $--message-info-font-color: $--color-info !default; /// color||Color|0 $--message-warning-font-color: $--color-warning !default; /// color||Color|0 $--message-danger-font-color: $--color-danger !default; /* Notification -------------------------- */ $--notification-width: 330px !default; /// padding||Spacing|3 $--notification-padding: 14px 26px 14px 13px !default; $--notification-radius: 8px !default; $--notification-shadow: $--box-shadow-light !default; /// color||Color|0 $--notification-border-color: $--border-color-lighter !default; $--notification-icon-size: 24px !default; $--notification-close-font-size: $--message-close-size !default; $--notification-group-margin-left: 13px !default; $--notification-group-margin-right: 8px !default; /// fontSize||Font|1 $--notification-content-font-size: $--font-size-base !default; /// color||Color|0 $--notification-content-color: $--color-text-regular !default; /// fontSize||Font|1 $--notification-title-font-size: 16px !default; /// color||Color|0 $--notification-title-color: $--color-text-primary !default; /// color||Color|0 $--notification-close-color: $--color-text-secondary !default; /// color||Color|0 $--notification-close-hover-color: $--color-text-regular !default; /// color||Color|0 $--notification-success-icon-color: $--color-success !default; /// color||Color|0 $--notification-info-icon-color: $--color-info !default; /// color||Color|0 $--notification-warning-icon-color: $--color-warning !default; /// color||Color|0 $--notification-danger-icon-color: $--color-danger !default; /* Input -------------------------- */ $--input-font-size: $--font-size-base !default; /// color||Color|0 $--input-font-color: $--color-text-regular !default; /// height||Other|4 $--input-width: 140px !default; /// height||Other|4 $--input-height: 40px !default; $--input-border: $--border-base !default; $--input-border-color: $--border-color-base !default; /// borderRadius||Border|2 $--input-border-radius: $--border-radius-base !default; $--input-border-color-hover: $--border-color-hover !default; /// color||Color|0 $--input-background-color: $--color-white !default; $--input-fill-disabled: $--disabled-fill-base !default; $--input-color-disabled: $--font-color-disabled-base !default; /// color||Color|0 $--input-icon-color: $--color-text-placeholder !default; /// color||Color|0 $--input-placeholder-color: $--color-text-placeholder !default; $--input-max-width: 314px !default; $--input-hover-border: $--border-color-hover !default; $--input-clear-hover-color: $--color-text-secondary !default; $--input-focus-border: $--color-primary !default; $--input-focus-fill: $--color-white !default; $--input-disabled-fill: $--disabled-fill-base !default; $--input-disabled-border: $--disabled-border-base !default; $--input-disabled-color: $--disabled-color-base !default; $--input-disabled-placeholder-color: $--color-text-placeholder !default; /// fontSize||Font|1 $--input-medium-font-size: 14px !default; /// height||Other|4 $--input-medium-height: 36px !default; /// fontSize||Font|1 $--input-small-font-size: 13px !default; /// height||Other|4 $--input-small-height: 32px !default; /// fontSize||Font|1 $--input-mini-font-size: 12px !default; /// height||Other|4 $--input-mini-height: 28px !default; /* Cascader -------------------------- */ /// color||Color|0 $--cascader-menu-font-color: $--color-text-regular !default; /// color||Color|0 $--cascader-menu-selected-font-color: $--color-primary !default; $--cascader-menu-fill: $--fill-base !default; $--cascader-menu-font-size: $--font-size-base !default; $--cascader-menu-radius: $--border-radius-base !default; $--cascader-menu-border: solid 1px $--border-color-light !default; $--cascader-menu-shadow: $--box-shadow-light !default; $--cascader-node-background-hover: $--background-color-base !default; $--cascader-node-color-disabled:$--color-text-placeholder !default; $--cascader-color-empty:$--color-text-placeholder !default; $--cascader-tag-background: #f0f2f5; /* Group -------------------------- */ $--group-option-flex: 0 0 (0.2) * 100% !default; $--group-option-offset-bottom: 12px !default; $--group-option-fill-hover: rgba($--color-black, 0.06) !default; $--group-title-color: $--color-black !default; $--group-title-font-size: $--font-size-base !default; $--group-title-width: 66px !default; /* Tab -------------------------- */ $--tab-font-size: $--font-size-base !default; $--tab-border-line: 1px solid #e4e4e4 !default; $--tab-header-color-active: $--color-text-secondary !default; $--tab-header-color-hover: $--color-text-regular !default; $--tab-header-color: $--color-text-regular !default; $--tab-header-fill-active: rgba($--color-black, 0.06) !default; $--tab-header-fill-hover: rgba($--color-black, 0.06) !default; $--tab-vertical-header-width: 90px !default; $--tab-vertical-header-count-color: $--color-white !default; $--tab-vertical-header-count-fill: $--color-text-secondary !default; /* Button -------------------------- */ /// fontSize||Font|1 $--button-font-size: $--font-size-base !default; /// fontWeight||Font|1 $--button-font-weight: $--font-weight-primary !default; /// borderRadius||Border|2 $--button-border-radius: $--border-radius-base !default; /// padding||Spacing|3 $--button-padding-vertical: 12px !default; /// padding||Spacing|3 $--button-padding-horizontal: 20px !default; /// fontSize||Font|1 $--button-medium-font-size: $--font-size-base !default; /// borderRadius||Border|2 $--button-medium-border-radius: $--border-radius-base !default; /// padding||Spacing|3 $--button-medium-padding-vertical: 10px !default; /// padding||Spacing|3 $--button-medium-padding-horizontal: 20px !default; /// fontSize||Font|1 $--button-small-font-size: 12px !default; $--button-small-border-radius: #{$--border-radius-base - 1} !default; /// padding||Spacing|3 $--button-small-padding-vertical: 9px !default; /// padding||Spacing|3 $--button-small-padding-horizontal: 15px !default; /// fontSize||Font|1 $--button-mini-font-size: 12px !default; $--button-mini-border-radius: #{$--border-radius-base - 1} !default; /// padding||Spacing|3 $--button-mini-padding-vertical: 7px !default; /// padding||Spacing|3 $--button-mini-padding-horizontal: 15px !default; /// color||Color|0 $--button-default-font-color: $--color-text-regular !default; /// color||Color|0 $--button-default-background-color: $--color-white !default; /// color||Color|0 $--button-default-border-color: $--border-color-base !default; /// color||Color|0 $--button-disabled-font-color: $--color-text-placeholder !default; /// color||Color|0 $--button-disabled-background-color: $--color-white !default; /// color||Color|0 $--button-disabled-border-color: $--border-color-lighter !default; /// color||Color|0 $--button-primary-border-color: $--color-primary !default; /// color||Color|0 $--button-primary-font-color: $--color-white !default; /// color||Color|0 $--button-primary-background-color: $--color-primary !default; /// color||Color|0 $--button-success-border-color: $--color-success !default; /// color||Color|0 $--button-success-font-color: $--color-white !default; /// color||Color|0 $--button-success-background-color: $--color-success !default; /// color||Color|0 $--button-warning-border-color: $--color-warning !default; /// color||Color|0 $--button-warning-font-color: $--color-white !default; /// color||Color|0 $--button-warning-background-color: $--color-warning !default; /// color||Color|0 $--button-danger-border-color: $--color-danger !default; /// color||Color|0 $--button-danger-font-color: $--color-white !default; /// color||Color|0 $--button-danger-background-color: $--color-danger !default; /// color||Color|0 $--button-info-border-color: $--color-info !default; /// color||Color|0 $--button-info-font-color: $--color-white !default; /// color||Color|0 $--button-info-background-color: $--color-info !default; $--button-hover-tint-percent: 20% !default; $--button-active-shade-percent: 10% !default; /* cascader -------------------------- */ $--cascader-height: 200px !default; /* Switch -------------------------- */ /// color||Color|0 $--switch-on-color: $--color-primary !default; /// color||Color|0 $--switch-off-color: $--border-color-base !default; /// fontSize||Font|1 $--switch-font-size: $--font-size-base !default; $--switch-core-border-radius: 10px !default; // height||Other|4 TODO: width 代码写死的40px 所以下面这三个属性都没意义 $--switch-width: 40px !default; // height||Other|4 $--switch-height: 20px !default; // height||Other|4 $--switch-button-size: 16px !default; /* Dialog -------------------------- */ $--dialog-background-color: $--color-white !default; $--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default; /// fontSize||Font|1 $--dialog-title-font-size: $--font-size-large !default; /// fontSize||Font|1 $--dialog-content-font-size: 14px !default; /// fontLineHeight||LineHeight|2 $--dialog-font-line-height: $--font-line-height-primary !default; /// padding||Spacing|3 $--dialog-padding-primary: 20px !default; /* Table -------------------------- */ /// color||Color|0 $--table-border-color: $--border-color-lighter !default; $--table-border: 1px solid $--table-border-color !default; /// color||Color|0 $--table-font-color: $--color-text-regular !default; /// color||Color|0 $--table-header-font-color: $--color-text-secondary !default; /// color||Color|0 $--table-row-hover-background-color: $--background-color-base !default; $--table-current-row-background-color: $--color-primary-light-9 !default; /// color||Color|0 $--table-header-background-color: $--color-white !default; $--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default; /* Pagination -------------------------- */ /// fontSize||Font|1 $--pagination-font-size: 13px !default; /// color||Color|0 $--pagination-background-color: $--color-white !default; /// color||Color|0 $--pagination-font-color: $--color-text-primary !default; $--pagination-border-radius: 3px !default; /// color||Color|0 $--pagination-button-color: $--color-text-primary !default; /// height||Other|4 $--pagination-button-width: 35.5px !default; /// height||Other|4 $--pagination-button-height: 28px !default; /// color||Color|0 $--pagination-button-disabled-color: $--color-text-placeholder !default; /// color||Color|0 $--pagination-button-disabled-background-color: $--color-white !default; /// color||Color|0 $--pagination-hover-color: $--color-primary !default; /* Popup -------------------------- */ /// color||Color|0 $--popup-modal-background-color: $--color-black !default; /// opacity||Other|1 $--popup-modal-opacity: 0.5 !default; /* Popover -------------------------- */ /// color||Color|0 $--popover-background-color: $--color-white !default; /// fontSize||Font|1 $--popover-font-size: $--font-size-base !default; /// color||Color|0 $--popover-border-color: $--border-color-lighter !default; $--popover-arrow-size: 6px !default; /// padding||Spacing|3 $--popover-padding: 12px !default; $--popover-padding-large: 18px 20px !default; /// fontSize||Font|1 $--popover-title-font-size: 16px !default; /// color||Color|0 $--popover-title-font-color: $--color-text-primary !default; /* Tooltip -------------------------- */ /// color|1|Color|0 $--tooltip-fill: $--color-text-primary !default; /// color|1|Color|0 $--tooltip-color: $--color-white !default; /// fontSize||Font|1 $--tooltip-font-size: 12px !default; /// color||Color|0 $--tooltip-border-color: $--color-text-primary !default; $--tooltip-arrow-size: 6px !default; /// padding||Spacing|3 $--tooltip-padding: 10px !default; /* Tag -------------------------- */ /// color||Color|0 $--tag-info-color: $--color-info !default; /// color||Color|0 $--tag-primary-color: $--color-primary !default; /// color||Color|0 $--tag-success-color: $--color-success !default; /// color||Color|0 $--tag-warning-color: $--color-warning !default; /// color||Color|0 $--tag-danger-color: $--color-danger !default; /// fontSize||Font|1 $--tag-font-size: 12px !default; $--tag-border-radius: 4px !default; $--tag-padding: 0 10px !default; /* Tree -------------------------- */ /// color||Color|0 $--tree-node-hover-background-color: $--background-color-base !default; /// color||Color|0 $--tree-font-color: $--color-text-regular !default; /// color||Color|0 $--tree-expand-icon-color: $--color-text-placeholder !default; /* Dropdown -------------------------- */ $--dropdown-menu-box-shadow: $--box-shadow-light !default; $--dropdown-menuItem-hover-fill: $--color-primary-light-9 !default; $--dropdown-menuItem-hover-color: $--link-color !default; /* Badge -------------------------- */ /// color||Color|0 $--badge-background-color: $--color-danger !default; $--badge-radius: 10px !default; /// fontSize||Font|1 $--badge-font-size: 12px !default; /// padding||Spacing|3 $--badge-padding: 6px !default; /// height||Other|4 $--badge-size: 18px !default; /* Card --------------------------*/ /// color||Color|0 $--card-border-color: $--border-color-lighter !default; $--card-border-radius: 4px !default; /// padding||Spacing|3 $--card-padding: 20px !default; /* Slider --------------------------*/ /// color||Color|0 $--slider-main-background-color: $--color-primary !default; /// color||Color|0 $--slider-runway-background-color: $--border-color-light !default; $--slider-button-hover-color: mix($--color-primary, black, 97%) !default; $--slider-stop-background-color: $--color-white !default; $--slider-disable-color: $--color-text-placeholder !default; $--slider-margin: 16px 0 !default; $--slider-border-radius: 3px !default; /// height|1|Other|4 $--slider-height: 6px !default; /// height||Other|4 $--slider-button-size: 16px !default; $--slider-button-wrapper-size: 36px !default; $--slider-button-wrapper-offset: -15px !default; /* Steps --------------------------*/ $--steps-border-color: $--disabled-border-base !default; $--steps-border-radius: 4px !default; $--steps-padding: 20px !default; /* Menu --------------------------*/ /// fontSize||Font|1 $--menu-item-font-size: $--font-size-base !default; /// color||Color|0 $--menu-item-font-color: $--color-text-primary !default; /// color||Color|0 $--menu-background-color: $--color-white !default; $--menu-item-hover-fill: $--color-primary-light-9 !default; /* Rate --------------------------*/ $--rate-height: 20px !default; /// fontSize||Font|1 $--rate-font-size: $--font-size-base !default; /// height||Other|3 $--rate-icon-size: 18px !default; /// margin||Spacing|2 $--rate-icon-margin: 6px !default; $--rate-icon-color: $--color-text-placeholder !default; /* DatePicker --------------------------*/ $--datepicker-font-color: $--color-text-regular !default; /// color|1|Color|0 $--datepicker-off-font-color: $--color-text-placeholder !default; /// color||Color|0 $--datepicker-header-font-color: $--color-text-regular !default; $--datepicker-icon-color: $--color-text-primary !default; $--datepicker-border-color: $--disabled-border-base !default; $--datepicker-inner-border-color: #e4e4e4 !default; /// color||Color|0 $--datepicker-inrange-background-color: $--border-color-extra-light !default; /// color||Color|0 $--datepicker-inrange-hover-background-color: $--border-color-extra-light !default; /// color||Color|0 $--datepicker-active-color: $--color-primary !default; /// color||Color|0 $--datepicker-hover-font-color: $--color-primary !default; $--datepicker-cell-hover-color: #fff !default; /* Loading --------------------------*/ /// height||Other|4 $--loading-spinner-size: 42px !default; /// height||Other|4 $--loading-fullscreen-spinner-size: 50px !default; /* Scrollbar --------------------------*/ $--scrollbar-background-color: rgba($--color-text-secondary, .3) !default; $--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default; /* Carousel --------------------------*/ /// fontSize||Font|1 $--carousel-arrow-font-size: 12px !default; $--carousel-arrow-size: 36px !default; $--carousel-arrow-background: rgba(31, 45, 61, 0.11) !default; $--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23) !default; /// width||Other|4 $--carousel-indicator-width: 30px !default; /// height||Other|4 $--carousel-indicator-height: 2px !default; $--carousel-indicator-padding-horizontal: 4px !default; $--carousel-indicator-padding-vertical: 12px !default; $--carousel-indicator-out-color: $--border-color-hover !default; /* Collapse --------------------------*/ /// color||Color|0 $--collapse-border-color: $--border-color-lighter !default; /// height||Other|4 $--collapse-header-height: 48px !default; /// color||Color|0 $--collapse-header-background-color: $--color-white !default; /// color||Color|0 $--collapse-header-font-color: $--color-text-primary !default; /// fontSize||Font|1 $--collapse-header-font-size: 13px !default; /// color||Color|0 $--collapse-content-background-color: $--color-white !default; /// fontSize||Font|1 $--collapse-content-font-size: 13px !default; /// color||Color|0 $--collapse-content-font-color: $--color-text-primary !default; /* Transfer --------------------------*/ $--transfer-border-color: $--border-color-lighter !default; $--transfer-border-radius: $--border-radius-base !default; /// height||Other|4 $--transfer-panel-width: 200px !default; /// height||Other|4 $--transfer-panel-header-height: 40px !default; /// color||Color|0 $--transfer-panel-header-background-color: $--background-color-base !default; /// height||Other|4 $--transfer-panel-footer-height: 40px !default; /// height||Other|4 $--transfer-panel-body-height: 246px !default; /// height||Other|4 $--transfer-item-height: 30px !default; /// height||Other|4 $--transfer-filter-height: 32px !default; /* Header --------------------------*/ $--header-padding: 0 20px !default; /* Footer --------------------------*/ $--footer-padding: 0 20px !default; /* Main --------------------------*/ $--main-padding: 20px !default; /* Timeline --------------------------*/ $--timeline-node-size-normal: 12px !default; $--timeline-node-size-large: 14px !default; $--timeline-node-color: $--border-color-light !default; /* Backtop --------------------------*/ /// color||Color|0 $--backtop-background-color: $--color-white !default; /// color||Color|0 $--backtop-font-color: $--color-primary !default; /// color||Color|0 $--backtop-hover-background-color: $--border-color-extra-light !default; /* Link --------------------------*/ /// fontSize||Font|1 $--link-font-size: $--font-size-base !default; /// fontWeight||Font|1 $--link-font-weight: $--font-weight-primary !default; /// color||Color|0 $--link-default-font-color: $--color-text-regular !default; /// color||Color|0 $--link-default-active-color: $--color-primary !default; /// color||Color|0 $--link-disabled-font-color: $--color-text-placeholder !default; /// color||Color|0 $--link-primary-font-color: $--color-primary !default; /// color||Color|0 $--link-success-font-color: $--color-success !default; /// color||Color|0 $--link-warning-font-color: $--color-warning !default; /// color||Color|0 $--link-danger-font-color: $--color-danger !default; /// color||Color|0 $--link-info-font-color: $--color-info !default; /* Calendar --------------------------*/ /// border||Other|4 $--calendar-border: $--table-border !default; /// color||Other|4 $--calendar-selected-background-color: #F2F8FE !default; $--calendar-cell-width: 85px !default; /* Break-point --------------------------*/ $--sm: 768px !default; $--md: 992px !default; $--lg: 1200px !default; $--xl: 1920px !default; $--breakpoints: ( 'xs' : (max-width: $--sm - 1), 'sm' : (min-width: $--sm), 'md' : (min-width: $--md), 'lg' : (min-width: $--lg), 'xl' : (min-width: $--xl) ); $--breakpoints-spec: ( 'xs-only' : (max-width: $--sm - 1), 'sm-and-up' : (min-width: $--sm), 'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md - 1})", 'sm-and-down': (max-width: $--md - 1), 'md-and-up' : (min-width: $--md), 'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg - 1})", 'md-and-down': (max-width: $--lg - 1), 'lg-and-up' : (min-width: $--lg), 'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})", 'lg-and-down': (max-width: $--xl - 1), 'xl-only' : (min-width: $--xl), ); /* 改变 icon 字体路径变量,必需 */ $--font-path: '~element-ui/lib/theme-chalk/fonts'; /* Mixin -------------------------- */ @mixin clearfix { &:after { content: ""; display: table; clear: both; } } /* App Theme Variables -------------------------- */ @import './Light/Variables.scss'; @import './Dark/Variables.scss'; ================================================ FILE: src/renderer/pages/index/App.vue ================================================ ================================================ FILE: src/renderer/pages/index/commands.js ================================================ import { Message } from 'element-ui' import { base64StringToBlob } from 'blob-util' import router from '@/router' import store from '@/store' import { buildFileList } from '@shared/utils' import { ADD_TASK_TYPE } from '@shared/constants' import { getLocaleManager } from '@/components/Locale' import { commands } from '@/components/CommandManager/instance' import { initTaskForm, buildUriPayload, buildTorrentPayload } from '@/utils/task' const i18n = getLocaleManager().getI18n() const updateSystemTheme = (payload = {}) => { const { theme } = payload store.dispatch('app/updateSystemTheme', theme) } const updateTheme = (payload = {}) => { const { theme } = payload store.dispatch('preference/updateAppTheme', theme) } const updateLocale = (payload = {}) => { const { locale } = payload store.dispatch('preference/updateAppLocale', locale) } const updateTrayFocused = (payload = {}) => { const { focused } = payload store.dispatch('app/updateTrayFocused', focused) } const showAboutPanel = () => { store.dispatch('app/showAboutPanel') } const addTask = (payload = {}) => { const { type = ADD_TASK_TYPE.URI, uri, silent, ...rest } = payload const options = { ...rest } if (type === ADD_TASK_TYPE.URI && uri) { store.dispatch('app/updateAddTaskUrl', uri) } store.dispatch('app/updateAddTaskOptions', options) if (silent) { addTaskSilent(type) return } store.dispatch('app/showAddTaskDialog', type) } const addTaskSilent = (type) => { try { addTaskByType(type) } catch (err) { Message.error(i18n.t(err.message)) } } const addTaskByType = (type) => { const form = initTaskForm(store.state) let payload = null if (type === ADD_TASK_TYPE.URI) { payload = buildUriPayload(form) store.dispatch('task/addUri', payload).catch(err => { Message.error(err.message) }) } else if (type === ADD_TASK_TYPE.TORRENT) { payload = buildTorrentPayload(form) store.dispatch('task/addTorrent', payload).catch(err => { Message.error(err.message) }) } else if (type === 'metalink') { // @TODO addMetalink } else { console.error('addTask fail', form) } } const showAddBtTask = () => { store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.TORRENT) } const showAddBtTaskWithFile = (payload = {}) => { const { name, dataURL = '' } = payload if (!dataURL) { return } const blob = base64StringToBlob(dataURL, 'application/x-bittorrent') const file = new File([blob], name, { type: 'application/x-bittorrent' }) const fileList = buildFileList(file) store.dispatch('app/showAddTaskDialog', ADD_TASK_TYPE.TORRENT) setTimeout(() => { store.dispatch('app/addTaskAddTorrents', { fileList }) }, 200) } const navigateTaskList = (payload = {}) => { const { status = 'active' } = payload router.push({ path: `/task/${status}` }).catch(err => { console.log(err) }) } const navigatePreferences = () => { router.push({ path: '/preference' }).catch(err => { console.log(err) }) } const showUnderDevelopmentMessage = () => { Message.info(i18n.t('app.under-development-message')) } const pauseTask = () => { store.dispatch('task/batchPauseSelectedTasks') } const resumeTask = () => { store.dispatch('task/batchResumeSelectedTasks') } const deleteTask = () => { commands.emit('batch-delete-task', { deleteWithFiles: false }) } const moveTaskUp = () => { showUnderDevelopmentMessage() } const moveTaskDown = () => { showUnderDevelopmentMessage() } const pauseAllTask = () => { store.dispatch('task/pauseAllTask') } const resumeAllTask = () => { store.dispatch('task/resumeAllTask') } const selectAllTask = () => { store.dispatch('task/selectAllTask') } const showTaskDetail = (payload = {}) => { const { gid } = payload navigateTaskList() if (gid) { store.dispatch('task/showTaskDetailByGid', gid) } } const fetchPreference = () => { store.dispatch('preference/fetchPreference') } commands.register('application:task-list', navigateTaskList) commands.register('application:preferences', navigatePreferences) commands.register('application:about', showAboutPanel) commands.register('application:new-task', addTask) commands.register('application:new-bt-task', showAddBtTask) commands.register('application:new-bt-task-with-file', showAddBtTaskWithFile) commands.register('application:pause-task', pauseTask) commands.register('application:resume-task', resumeTask) commands.register('application:delete-task', deleteTask) commands.register('application:move-task-up', moveTaskUp) commands.register('application:move-task-down', moveTaskDown) commands.register('application:pause-all-task', pauseAllTask) commands.register('application:resume-all-task', resumeAllTask) commands.register('application:select-all-task', selectAllTask) commands.register('application:show-task-detail', showTaskDetail) commands.register('application:update-preference-config', fetchPreference) commands.register('application:update-system-theme', updateSystemTheme) commands.register('application:update-theme', updateTheme) commands.register('application:update-locale', updateLocale) commands.register('application:update-tray-focused', updateTrayFocused) ================================================ FILE: src/renderer/pages/index/main.js ================================================ import is from 'electron-is' import { ipcRenderer } from 'electron' import Vue from 'vue' import VueI18Next from '@panter/vue-i18next' import { sync } from 'vuex-router-sync' import Element, { Loading, Message } from 'element-ui' import axios from 'axios' import App from './App' import router from '@/router' import store from '@/store' import { getLocaleManager } from '@/components/Locale' import Icon from '@/components/Icons/Icon' import Msg from '@/components/Msg' import { commands } from '@/components/CommandManager/instance' import TrayWorker from '@/workers/tray.worker' import '@/components/Theme/Index.scss' const updateTray = is.renderer() ? async (payload) => { const { tray } = payload if (!tray) { return } const ab = await tray.arrayBuffer() ipcRenderer.send('command', 'application:update-tray', ab) } : () => {} function initTrayWorker () { const worker = new TrayWorker() worker.addEventListener('message', (event) => { const { type, payload } = event.data switch (type) { case 'initialized': case 'log': console.log('[Motrix] Log from Tray Worker: ', payload) break case 'tray:drawed': updateTray(payload) break default: console.warn('[Motrix] Tray Worker unhandled message type:', type, payload) } }) return worker } function init (config) { if (is.renderer()) { Vue.use(require('vue-electron')) } Vue.http = Vue.prototype.$http = axios Vue.config.productionTip = false const { locale } = config const localeManager = getLocaleManager() localeManager.changeLanguageByLocale(locale) Vue.use(VueI18Next) const i18n = new VueI18Next(localeManager.getI18n()) Vue.use(Element, { size: 'mini', i18n: (key, value) => i18n.t(key, value) }) Vue.use(Msg, Message, { showClose: true }) Vue.component('mo-icon', Icon) const loading = Loading.service({ fullscreen: true, background: 'rgba(0, 0, 0, 0.1)' }) sync(store, router) /* eslint-disable no-new */ global.app = new Vue({ components: { App }, router, store, i18n, template: '' }).$mount('#app') global.app.commands = commands require('./commands') global.app.trayWorker = initTrayWorker() setTimeout(() => { loading.close() }, 400) } store.dispatch('preference/fetchPreference') .then((config) => { console.info('[Motrix] load preference:', config) init(config) }) .catch((err) => { alert(err) }) ================================================ FILE: src/renderer/router/index.js ================================================ import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'main', component: require('@/components/Main').default, children: [ { path: '/task', alias: '/', component: require('@/components/Task/Index').default, props: { status: 'active' } }, { path: '/task/:status', name: 'task', component: require('@/components/Task/Index').default, props: true }, { path: '/preference', name: 'preference', component: require('@/components/Preference/Index').default, props: true, children: [ { path: 'basic', alias: '', components: { subnav: require('@/components/Subnav/PreferenceSubnav').default, form: require('@/components/Preference/Basic').default }, props: { subnav: { current: 'basic' } } }, { path: 'advanced', components: { subnav: require('@/components/Subnav/PreferenceSubnav').default, form: require('@/components/Preference/Advanced').default }, props: { subnav: { current: 'advanced' } } }, { path: 'lab', components: { subnav: require('@/components/Subnav/PreferenceSubnav').default, form: require('@/components/Preference/Lab').default }, props: { subnav: { current: 'lab' } } } ] } ] }, { path: '*', redirect: '/' } ] }) ================================================ FILE: src/renderer/store/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' import modules from './modules' Vue.use(Vuex) export default new Vuex.Store({ modules, strict: process.env.NODE_ENV !== 'production' }) ================================================ FILE: src/renderer/store/modules/app.js ================================================ import { ADD_TASK_TYPE } from '@shared/constants' import api from '@/api' import { getSystemTheme } from '@/utils/native' const BASE_INTERVAL = 1000 const PER_INTERVAL = 100 const MIN_INTERVAL = 500 const MAX_INTERVAL = 6000 const state = { systemTheme: getSystemTheme(), trayFocused: false, aboutPanelVisible: false, engineInfo: { version: '', enabledFeatures: [] }, engineOptions: {}, interval: BASE_INTERVAL, stat: { downloadSpeed: 0, uploadSpeed: 0, numActive: 0, numWaiting: 0, numStopped: 0 }, addTaskVisible: false, addTaskType: ADD_TASK_TYPE.URI, addTaskUrl: '', addTaskTorrents: [], addTaskOptions: {}, progress: 0 } const getters = { } const mutations = { UPDATE_SYSTEM_THEME (state, theme) { state.systemTheme = theme }, UPDATE_TRAY_FOCUSED (state, focused) { state.trayFocused = focused }, UPDATE_ABOUT_PANEL_VISIBLE (state, visible) { state.aboutPanelVisible = visible }, UPDATE_ENGINE_INFO (state, engineInfo) { state.engineInfo = { ...state.engineInfo, ...engineInfo } }, UPDATE_ENGINE_OPTIONS (state, engineOptions) { state.engineOptions = { ...state.engineOptions, ...engineOptions } }, UPDATE_GLOBAL_STAT (state, stat) { state.stat = stat }, UPDATE_ADD_TASK_VISIBLE (state, visible) { state.addTaskVisible = visible }, UPDATE_ADD_TASK_TYPE (state, taskType) { state.addTaskType = taskType }, UPDATE_ADD_TASK_URL (state, text) { state.addTaskUrl = text }, UPDATE_ADD_TASK_TORRENTS (state, fileList) { state.addTaskTorrents = [...fileList] }, UPDATE_ADD_TASK_OPTIONS (state, options) { state.addTaskOptions = { ...options } }, UPDATE_INTERVAL (state, millisecond) { let interval = millisecond if (millisecond > MAX_INTERVAL) { interval = MAX_INTERVAL } if (millisecond < MIN_INTERVAL) { interval = MIN_INTERVAL } if (state.interval === interval) { return } state.interval = interval }, INCREASE_INTERVAL (state, millisecond) { if (state.interval < MAX_INTERVAL) { state.interval += millisecond } }, DECREASE_INTERVAL (state, millisecond) { if (state.interval > MIN_INTERVAL) { state.interval -= millisecond } }, UPDATE_PROGRESS (state, progress) { state.progress = progress } } const actions = { updateSystemTheme ({ commit }, theme) { commit('UPDATE_SYSTEM_THEME', theme) }, updateTrayFocused ({ commit }, focused) { commit('UPDATE_TRAY_FOCUSED', focused) }, showAboutPanel ({ commit }) { commit('UPDATE_ABOUT_PANEL_VISIBLE', true) }, hideAboutPanel ({ commit }) { commit('UPDATE_ABOUT_PANEL_VISIBLE', false) }, fetchEngineInfo ({ commit }) { api.getVersion() .then((data) => { commit('UPDATE_ENGINE_INFO', data) }) }, fetchEngineOptions ({ commit }) { return new Promise((resolve) => { api.getGlobalOption() .then((data) => { commit('UPDATE_ENGINE_OPTIONS', data) resolve(data) }) }) }, fetchGlobalStat ({ commit, dispatch }) { api.getGlobalStat() .then((data) => { const stat = {} Object.keys(data).forEach((key) => { stat[key] = Number(data[key]) }) const { numActive } = stat if (numActive > 0) { const interval = BASE_INTERVAL - PER_INTERVAL * numActive dispatch('updateInterval', interval) } else { // fix downloadSpeed when numActive = 0 stat.downloadSpeed = 0 dispatch('increaseInterval') } commit('UPDATE_GLOBAL_STAT', stat) }) }, increaseInterval ({ commit }, millisecond = 100) { commit('INCREASE_INTERVAL', millisecond) }, showAddTaskDialog ({ commit }, taskType) { commit('UPDATE_ADD_TASK_TYPE', taskType) commit('UPDATE_ADD_TASK_VISIBLE', true) }, hideAddTaskDialog ({ commit }) { commit('UPDATE_ADD_TASK_VISIBLE', false) commit('UPDATE_ADD_TASK_URL', '') commit('UPDATE_ADD_TASK_TORRENTS', []) }, changeAddTaskType ({ commit }, taskType) { commit('UPDATE_ADD_TASK_TYPE', taskType) }, updateAddTaskUrl ({ commit }, uri = '') { commit('UPDATE_ADD_TASK_URL', uri) }, addTaskAddTorrents ({ commit }, { fileList }) { commit('UPDATE_ADD_TASK_TORRENTS', fileList) }, updateAddTaskOptions ({ commit }, options = {}) { commit('UPDATE_ADD_TASK_OPTIONS', options) }, updateInterval ({ commit }, millisecond) { commit('UPDATE_INTERVAL', millisecond) }, resetInterval ({ commit }) { commit('UPDATE_INTERVAL', BASE_INTERVAL) }, fetchProgress ({ commit }) { api.fetchActiveTaskList() .then((data) => { let progress = -1 if (data.length !== 0) { data.forEach((task) => { task.totalLength = Number(task.totalLength) task.completedLength = Number(task.completedLength) }) const realTotal = data.reduce((total, task) => total + task.totalLength, 0) if (realTotal === 0) { progress = 2 } else { const tasks = data.filter((task) => task.totalLength !== 0) const completed = tasks.reduce((total, task) => total + task.completedLength, 0) const total = tasks.reduce((total, task) => total + task.totalLength, 0) progress = completed / total } } commit('UPDATE_PROGRESS', progress) }) } } export default { namespaced: true, state, getters, mutations, actions } ================================================ FILE: src/renderer/store/modules/index.js ================================================ /** * The file enables `@/store/index.js` to import all vuex modules * in a one-shot manner. There should not be any reason to edit this file. */ const files = require.context('.', false, /\.js$/) const modules = {} files.keys().forEach(key => { if (key === './index.js') return modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default }) export default modules ================================================ FILE: src/renderer/store/modules/preference.js ================================================ import { isEmpty } from 'lodash' import api from '@/api' import { getLangDirection, pushItemToFixedLengthArray, removeArrayItem } from '@shared/utils' import { fetchBtTrackerFromSource } from '@shared/utils/tracker' import { MAX_NUM_OF_DIRECTORIES } from '@shared/constants' const state = { engineMode: 'MAX', config: {} } const getters = { theme: state => state.config.theme, locale: state => state.config.locale, direction: state => getLangDirection(state.config.locale) } const mutations = { UPDATE_PREFERENCE_DATA (state, config) { state.config = { ...state.config, ...config } } } const actions = { fetchPreference ({ dispatch }) { return new Promise((resolve) => { api.fetchPreference() .then((config) => { dispatch('updatePreference', config) resolve(config) }) }) }, save ({ dispatch }, config) { dispatch('task/saveSession', null, { root: true }) if (isEmpty(config)) { return } dispatch('updatePreference', config) return api.savePreference(config) }, recordHistoryDirectory ({ state, dispatch }, directory) { const { historyDirectories = [], favoriteDirectories = [] } = state.config const all = new Set([...historyDirectories, ...favoriteDirectories]) if (all.has(directory)) { return } dispatch('addHistoryDirectory', directory) }, addHistoryDirectory ({ state, dispatch }, directory) { const { historyDirectories = [] } = state.config const history = pushItemToFixedLengthArray( historyDirectories, MAX_NUM_OF_DIRECTORIES, directory ) dispatch('save', { historyDirectories: history }) }, favoriteDirectory ({ state, dispatch }, directory) { const { historyDirectories = [], favoriteDirectories = [] } = state.config if (favoriteDirectories.includes(directory) || favoriteDirectories.length >= MAX_NUM_OF_DIRECTORIES ) { return } const favorite = pushItemToFixedLengthArray( favoriteDirectories, MAX_NUM_OF_DIRECTORIES, directory ) const history = removeArrayItem(historyDirectories, directory) dispatch('save', { historyDirectories: history, favoriteDirectories: favorite }) }, cancelFavoriteDirectory ({ state, dispatch }, directory) { const { historyDirectories = [], favoriteDirectories = [] } = state.config if (historyDirectories.includes(directory)) { return } const favorite = removeArrayItem(favoriteDirectories, directory) const history = pushItemToFixedLengthArray( historyDirectories, MAX_NUM_OF_DIRECTORIES, directory ) dispatch('save', { historyDirectories: history, favoriteDirectories: favorite }) }, removeDirectory ({ state, dispatch }, directory) { const { historyDirectories = [], favoriteDirectories = [] } = state.config const favorite = removeArrayItem(favoriteDirectories, directory) const history = removeArrayItem(historyDirectories, directory) dispatch('save', { historyDirectories: history, favoriteDirectories: favorite }) }, updateAppTheme ({ dispatch }, theme) { dispatch('updatePreference', { theme }) }, updateAppLocale ({ dispatch }, locale) { dispatch('updatePreference', { locale }) }, updatePreference ({ commit }, config) { commit('UPDATE_PREFERENCE_DATA', config) }, fetchBtTracker (_, trackerSource = []) { const { proxy = { enable: false } } = state.config console.log('fetchBtTracker', trackerSource, proxy) return fetchBtTrackerFromSource(trackerSource, proxy) }, toggleEngineMode () { } } export default { namespaced: true, state, getters, mutations, actions } ================================================ FILE: src/renderer/store/modules/task.js ================================================ import api from '@/api' import { EMPTY_STRING, TASK_STATUS } from '@shared/constants' import { checkTaskIsBT, intersection } from '@shared/utils' const state = { currentList: 'active', taskDetailVisible: false, currentTaskGid: EMPTY_STRING, enabledFetchPeers: false, currentTaskItem: null, currentTaskFiles: [], currentTaskPeers: [], seedingList: [], taskList: [], selectedGidList: [] } const getters = { } const mutations = { UPDATE_SEEDING_LIST (state, seedingList) { state.seedingList = seedingList }, UPDATE_TASK_LIST (state, taskList) { state.taskList = taskList }, UPDATE_SELECTED_GID_LIST (state, gidList) { state.selectedGidList = gidList }, CHANGE_CURRENT_LIST (state, currentList) { state.currentList = currentList }, CHANGE_TASK_DETAIL_VISIBLE (state, visible) { state.taskDetailVisible = visible }, UPDATE_CURRENT_TASK_GID (state, gid) { state.currentTaskGid = gid }, UPDATE_ENABLED_FETCH_PEERS (state, enabled) { state.enabledFetchPeers = enabled }, UPDATE_CURRENT_TASK_ITEM (state, task) { state.currentTaskItem = task }, UPDATE_CURRENT_TASK_FILES (state, files) { state.currentTaskFiles = files }, UPDATE_CURRENT_TASK_PEERS (state, peers) { state.currentTaskPeers = peers } } const actions = { changeCurrentList ({ commit, dispatch }, currentList) { commit('CHANGE_CURRENT_LIST', currentList) commit('UPDATE_SELECTED_GID_LIST', []) dispatch('fetchList') }, fetchList ({ commit, state }) { return api.fetchTaskList({ type: state.currentList }) .then((data) => { commit('UPDATE_TASK_LIST', data) const { selectedGidList } = state const gids = data.map((task) => task.gid) const list = intersection(selectedGidList, gids) commit('UPDATE_SELECTED_GID_LIST', list) }) }, selectTasks ({ commit }, list) { commit('UPDATE_SELECTED_GID_LIST', list) }, selectAllTask ({ commit, state }) { const gids = state.taskList.map((task) => task.gid) commit('UPDATE_SELECTED_GID_LIST', gids) }, fetchItem ({ dispatch }, gid) { return api.fetchTaskItem({ gid }) .then((data) => { dispatch('updateCurrentTaskItem', data) }) }, fetchItemWithPeers ({ dispatch }, gid) { return api.fetchTaskItemWithPeers({ gid }) .then((data) => { console.log('fetchItemWithPeers===>', data) dispatch('updateCurrentTaskItem', data) }) }, showTaskDetailByGid ({ commit, dispatch }, gid) { api.fetchTaskItem({ gid }) .then((task) => { dispatch('updateCurrentTaskItem', task) commit('UPDATE_CURRENT_TASK_GID', task.gid) commit('CHANGE_TASK_DETAIL_VISIBLE', true) }) }, showTaskDetail ({ commit, dispatch }, task) { dispatch('updateCurrentTaskItem', task) commit('UPDATE_CURRENT_TASK_GID', task.gid) commit('CHANGE_TASK_DETAIL_VISIBLE', true) }, hideTaskDetail ({ commit }) { commit('CHANGE_TASK_DETAIL_VISIBLE', false) }, toggleEnabledFetchPeers ({ commit }, enabled) { commit('UPDATE_ENABLED_FETCH_PEERS', enabled) }, updateCurrentTaskItem ({ commit }, task) { commit('UPDATE_CURRENT_TASK_ITEM', task) if (task) { commit('UPDATE_CURRENT_TASK_FILES', task.files) commit('UPDATE_CURRENT_TASK_PEERS', task.peers) } else { commit('UPDATE_CURRENT_TASK_FILES', []) commit('UPDATE_CURRENT_TASK_PEERS', []) } }, updateCurrentTaskGid ({ commit }, gid) { commit('UPDATE_CURRENT_TASK_GID', gid) }, addUri ({ dispatch }, data) { const { uris, outs, options } = data return api.addUri({ uris, outs, options }) .then(() => { dispatch('fetchList') dispatch('app/updateAddTaskOptions', {}, { root: true }) }) }, addTorrent ({ dispatch }, data) { const { torrent, options } = data return api.addTorrent({ torrent, options }) .then(() => { dispatch('fetchList') dispatch('app/updateAddTaskOptions', {}, { root: true }) }) }, addMetalink ({ dispatch }, data) { const { metalink, options } = data return api.addMetalink({ metalink, options }) .then(() => { dispatch('fetchList') dispatch('app/updateAddTaskOptions', {}, { root: true }) }) }, getTaskOption (_, gid) { return new Promise((resolve) => { api.getOption({ gid }) .then((data) => { resolve(data) }) }) }, changeTaskOption (_, payload) { const { gid, options } = payload return api.changeOption({ gid, options }) }, removeTask ({ state, dispatch }, task) { const { gid } = task if (gid === state.currentTaskGid) { dispatch('hideTaskDetail') } return api.removeTask({ gid }) .finally(() => { dispatch('fetchList') dispatch('saveSession') }) }, forcePauseTask ({ dispatch }, task) { const { gid, status } = task if (status !== TASK_STATUS.ACTIVE) { return Promise.resolve(true) } return api.forcePauseTask({ gid }) .finally(() => { dispatch('fetchList') dispatch('saveSession') }) }, pauseTask ({ dispatch }, task) { const { gid } = task const isBT = checkTaskIsBT(task) const promise = isBT ? api.forcePauseTask({ gid }) : api.pauseTask({ gid }) promise.finally(() => { dispatch('fetchList') dispatch('saveSession') }) return promise }, resumeTask ({ dispatch }, task) { const { gid } = task return api.resumeTask({ gid }) .finally(() => { dispatch('fetchList') dispatch('saveSession') }) }, pauseAllTask ({ dispatch }) { return api.pauseAllTask() .catch(() => { return api.forcePauseAllTask() }) .finally(() => { dispatch('fetchList') dispatch('saveSession') }) }, resumeAllTask ({ dispatch }) { return api.resumeAllTask() .finally(() => { dispatch('fetchList') dispatch('saveSession') }) }, addToSeedingList ({ state, commit }, gid) { const { seedingList } = state if (seedingList.includes(gid)) { return } const list = [ ...seedingList, gid ] commit('UPDATE_SEEDING_LIST', list) }, removeFromSeedingList ({ state, commit }, gid) { const { seedingList } = state const idx = seedingList.indexOf(gid) if (idx === -1) { return } const list = [...seedingList.slice(0, idx), ...seedingList.slice(idx + 1)] commit('UPDATE_SEEDING_LIST', list) }, stopSeeding ({ dispatch }, { gid }) { const options = { seedTime: 0 } return dispatch('changeTaskOption', { gid, options }) }, removeTaskRecord ({ state, dispatch }, task) { const { gid, status } = task if (gid === state.currentTaskGid) { dispatch('hideTaskDetail') } const { ERROR, COMPLETE, REMOVED } = TASK_STATUS if ([ERROR, COMPLETE, REMOVED].indexOf(status) === -1) { return } return api.removeTaskRecord({ gid }) .finally(() => dispatch('fetchList')) }, saveSession () { api.saveSession() }, purgeTaskRecord ({ dispatch }) { return api.purgeTaskRecord() .finally(() => dispatch('fetchList')) }, toggleTask ({ dispatch }, task) { const { status } = task const { ACTIVE, WAITING, PAUSED } = TASK_STATUS if (status === ACTIVE) { return dispatch('pauseTask', task) } else if (status === WAITING || status === PAUSED) { return dispatch('resumeTask', task) } }, batchResumeSelectedTasks ({ state }) { const gids = state.selectedGidList if (gids.length === 0) { return } return api.batchResumeTask({ gids }) }, batchPauseSelectedTasks ({ state }) { const gids = state.selectedGidList if (gids.length === 0) { return } return api.batchPauseTask({ gids }) }, batchForcePauseTask (_, gids) { return api.batchForcePauseTask({ gids }) }, batchResumeTask (_, gids) { return api.batchResumeTask({ gids }) }, batchRemoveTask ({ dispatch }, gids) { return api.batchRemoveTask({ gids }) .finally(() => { dispatch('fetchList') dispatch('saveSession') }) } } export default { namespaced: true, state, getters, mutations, actions } ================================================ FILE: src/renderer/utils/native.js ================================================ import { access, constants } from 'node:fs' import { resolve } from 'node:path' import { shell, nativeTheme } from '@electron/remote' import { Message } from 'element-ui' import { getFileNameFromFile, isMagnetTask } from '@shared/utils' import { APP_THEME, TASK_STATUS } from '@shared/constants' export const showItemInFolder = (fullPath, { errorMsg }) => { if (!fullPath) { return } fullPath = resolve(fullPath) access(fullPath, constants.F_OK, (err) => { console.warn(`[Motrix] ${fullPath} ${err ? 'does not exist' : 'exists'}`) if (err && errorMsg) { Message.error(errorMsg) return } shell.showItemInFolder(fullPath) }) } export const openItem = async (fullPath) => { if (!fullPath) { return } const result = await shell.openPath(fullPath) return result } export const getTaskFullPath = (task) => { const { dir, files, bittorrent } = task let result = resolve(dir) // Magnet link task if (isMagnetTask(task)) { return result } if (bittorrent && bittorrent.info && bittorrent.info.name) { result = resolve(result, bittorrent.info.name) return result } const [file] = files const path = file.path ? resolve(file.path) : '' let fileName = '' if (path) { result = path } else { if (files && files.length === 1) { fileName = getFileNameFromFile(file) if (fileName) { result = resolve(result, fileName) } } } return result } export const moveTaskFilesToTrash = (task) => { /** * For magnet link tasks, there is bittorrent, but there is no bittorrent.info. * The path is not a complete path before it becomes a BT task. * In order to avoid accidentally deleting the directory * where the task is located, it directly returns true when deleting. */ if (isMagnetTask(task)) { return true } const { dir, status } = task const path = getTaskFullPath(task) if (!path || dir === path) { throw new Error('task.file-path-error') } let deleteResult1 = true access(path, constants.F_OK, async (err) => { console.log(`[Motrix] ${path} ${err ? 'does not exist' : 'exists'}`) if (!err) { deleteResult1 = await shell.trashItem(path) } }) // There is no configuration file for the completed task. if (status === TASK_STATUS.COMPLETE) { return deleteResult1 } let deleteResult2 = true const extraFilePath = `${path}.aria2` access(extraFilePath, constants.F_OK, async (err) => { console.log(`[Motrix] ${extraFilePath} ${err ? 'does not exist' : 'exists'}`) if (!err) { deleteResult2 = await shell.trashItem(extraFilePath) } }) return deleteResult1 && deleteResult2 } export const getSystemTheme = () => { return nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT } export const delayDeleteTaskFiles = (task, delay) => { return new Promise((resolve, reject) => { setTimeout(() => { try { const result = moveTaskFilesToTrash(task) resolve(result) } catch (err) { reject(err.message) } }, delay) }) } ================================================ FILE: src/renderer/utils/task.js ================================================ import { isEmpty } from 'lodash' import { ADD_TASK_TYPE, NONE_SELECTED_FILES, SELECTED_ALL_FILES } from '@shared/constants' import { splitTaskLinks } from '@shared/utils' import { buildOuts } from '@shared/utils/rename' import { buildUrisFromCurl, buildHeadersFromCurl, buildDefaultOptionsFromCurl } from '@shared/utils/curl' export const initTaskForm = state => { const { addTaskUrl, addTaskOptions } = state.app const { allProxy, dir, engineMaxConnectionPerServer, followMetalink, followTorrent, maxConnectionPerServer, newTaskShowDownloading, split } = state.preference.config const result = { allProxy, cookie: '', dir, engineMaxConnectionPerServer, followMetalink, followTorrent, maxConnectionPerServer, newTaskShowDownloading, out: '', referer: '', selectFile: NONE_SELECTED_FILES, split, torrent: '', uris: addTaskUrl, userAgent: '', authorization: '', ...addTaskOptions } return result } export const buildHeader = (form) => { const { userAgent, referer, cookie, authorization } = form const result = [] if (!isEmpty(userAgent)) { result.push(`User-Agent: ${userAgent}`) } if (!isEmpty(referer)) { result.push(`Referer: ${referer}`) } if (!isEmpty(cookie)) { result.push(`Cookie: ${cookie}`) } if (!isEmpty(authorization)) { result.push(`Authorization: ${authorization}`) } return result } export const buildOption = (type, form) => { const { allProxy, dir, out, selectFile, split } = form const result = {} if (!isEmpty(allProxy)) { result.allProxy = allProxy } if (!isEmpty(dir)) { result.dir = dir } if (!isEmpty(out)) { result.out = out } if (split > 0) { result.split = split } if (type === ADD_TASK_TYPE.TORRENT) { if ( selectFile !== SELECTED_ALL_FILES && selectFile !== NONE_SELECTED_FILES ) { result.selectFile = selectFile } } const header = buildHeader(form) if (!isEmpty(header)) { result.header = header } return result } export const buildUriPayload = (form) => { let { uris, out } = form if (isEmpty(uris)) { throw new Error('task.new-task-uris-required') } uris = splitTaskLinks(uris) const curlHeaders = buildHeadersFromCurl(uris) uris = buildUrisFromCurl(uris) const outs = buildOuts(uris, out) form = buildDefaultOptionsFromCurl(form, curlHeaders) const options = buildOption(ADD_TASK_TYPE.URI, form) const result = { uris, outs, options } return result } export const buildTorrentPayload = (form) => { const { torrent } = form if (isEmpty(torrent)) { throw new Error('task.new-task-torrent-required') } const options = buildOption(ADD_TASK_TYPE.TORRENT, form) const result = { torrent, options } return result } ================================================ FILE: src/renderer/workers/tray.worker.js ================================================ /* eslint no-unused-vars: 'off' */ import { TRAY_CANVAS_CONFIG } from '@shared/constants' import { draw } from '@shared/utils/tray' let idx = 0 let canvas const initCanvas = () => { if (canvas) { return canvas } const { WIDTH, HEIGHT } = TRAY_CANVAS_CONFIG return new OffscreenCanvas(WIDTH, HEIGHT) } const drawTray = async (payload) => { self.postMessage({ type: 'log', payload }) if (!canvas) { canvas = initCanvas() } try { const tray = await draw({ canvas, ...payload }) self.postMessage({ type: 'tray:drawed', payload: { idx, tray } }) idx += 1 } catch (error) { logger(error.message) } } const logger = (text) => { self.postMessage({ type: 'log', payload: text }) } self.postMessage({ type: 'initialized', payload: Date.now() }) self.addEventListener('message', (event) => { const { type, payload } = event.data switch (type) { case 'tray:draw': drawTray(payload) break default: logger(JSON.stringify(event.data)) } }) ================================================ FILE: src/shared/aria2/index.js ================================================ 'use strict' const Aria2 = require('./lib/Aria2') module.exports = Aria2 ================================================ FILE: src/shared/aria2/lib/Aria2.js ================================================ 'use strict' import { JSONRPCClient } from './JSONRPCClient' export class Aria2 extends JSONRPCClient { prefix (str) { if (!str.startsWith('system.') && !str.startsWith('aria2.')) { str = 'aria2.' + str } return str } unprefix (str) { const suffix = str.split('aria2.')[1] return suffix || str } addSecret (parameters) { let params = this.secret ? ['token:' + this.secret] : [] if (Array.isArray(parameters)) { params = params.concat(parameters) } return params } _onnotification (notification) { const { method, params } = notification const event = this.unprefix(method) if (event !== method) this.emit(event, params) return super._onnotification(notification) } async call (method, ...params) { return super.call(this.prefix(method), this.addSecret(params)) } async multicall (calls) { const multi = [ calls.map(([method, ...params]) => { return { methodName: this.prefix(method), params: this.addSecret(params) } }) ] return super.call('system.multicall', multi) } async batch (calls) { return super.batch( calls.map(([method, ...params]) => [ this.prefix(method), this.addSecret(params) ]) ) } async listNotifications () { const events = await this.call('system.listNotifications') return events.map((event) => this.unprefix(event)) } async listMethods () { const methods = await this.call('system.listMethods') return methods.map((method) => this.unprefix(method)) } defaultOptions = Object.assign({}, JSONRPCClient.defaultOptions, { secure: false, host: 'localhost', port: 16800, secret: '', path: '/jsonrpc' }) } ================================================ FILE: src/shared/aria2/lib/Deferred.js ================================================ 'use strict' module.exports = function Deferred () { this.promise = new Promise((resolve, reject) => { this.resolve = resolve this.reject = reject }) } ================================================ FILE: src/shared/aria2/lib/JSONRPCClient.js ================================================ 'use strict' import { EventEmitter } from 'node:events' import _fetch from 'node-fetch' import _WebSocket from 'ws' import { JSONRPCError } from './JSONRPCError' const Deferred = require('./Deferred') const promiseEvent = require('./promiseEvent') const WebSocket = global.WebSocket || _WebSocket const fetch = global.fetch ? global.fetch.bind(global) : _fetch export class JSONRPCClient extends EventEmitter { constructor (options) { super() this.deferreds = Object.create(null) this.lastId = 0 Object.assign(this, this.defaultOptions, options) } id () { return this.lastId++ } url (protocol) { return ( protocol + (this.secure ? 's' : '') + '://' + this.host + ':' + this.port + this.path ) } websocket (message) { return new Promise((resolve, reject) => { const cb = (err) => { if (err) reject(err) else resolve() } this.socket.send(JSON.stringify(message), cb) if (global.WebSocket && this.socket instanceof global.WebSocket) cb() }) } async http (message) { const response = await fetch(this.url('http'), { method: 'POST', body: JSON.stringify(message), headers: { Accept: 'application/json', 'Content-Type': 'application/json' } }) response .json() .then(this._onmessage) .catch((err) => { this.emit('error', err) }) return response } _buildMessage (method, params) { if (typeof method !== 'string') { throw new TypeError(method + ' is not a string') } const message = { method, 'json-rpc': '2.0', id: this.id() } if (params) Object.assign(message, { params }) return message } async batch (calls) { const message = calls.map(([method, params]) => { return this._buildMessage(method, params) }) await this._send(message) return message.map(({ id }) => { const { promise } = (this.deferreds[id] = new Deferred()) return promise }) } async call (method, parameters) { const message = this._buildMessage(method, parameters) await this._send(message) const { promise } = (this.deferreds[message.id] = new Deferred()) return promise } async _send (message) { this.emit('output', message) const { socket } = this return socket && socket.readyState === 1 ? this.websocket(message) : this.http(message) } _onresponse ({ id, error, result }) { const deferred = this.deferreds[id] if (!deferred) return if (error) deferred.reject(new JSONRPCError(error)) else deferred.resolve(result) delete this.deferreds[id] } _onrequest ({ method, params }) { return this.onrequest(method, params) } _onnotification ({ method, params }) { this.emit(method, params) } _onmessage = (message) => { this.emit('input', message) if (Array.isArray(message)) { for (const object of message) { this._onobject(object) } } else { this._onobject(message) } } _onobject (message) { if (message.method === undefined) this._onresponse(message) else if (message.id === undefined) this._onnotification(message) else this._onrequest(message) } async open () { const socket = (this.socket = new WebSocket(this.url('ws'))) socket.onclose = (...args) => { this.emit('close', ...args) } socket.onmessage = (event) => { let message try { message = JSON.parse(event.data) } catch (err) { this.emit('error', err) return } this._onmessage(message) } socket.onopen = (...args) => { this.emit('open', ...args) } socket.onerror = (...args) => { this.emit('error', ...args) } return promiseEvent(this, 'open') } async close () { const { socket } = this socket.close() return promiseEvent(this, 'close') } defaultOptions = { secure: false, host: 'localhost', port: 80, secret: '', path: '/jsonrpc', fetch, WebSocket } } ================================================ FILE: src/shared/aria2/lib/JSONRPCError.js ================================================ 'use strict' export class JSONRPCError extends Error { constructor ({ message, code, data }) { super(message) this.code = code if (data) this.data = data this.name = this.constructor.name } } ================================================ FILE: src/shared/aria2/lib/debug.js ================================================ 'use strict' import { inspect } from 'util' module.exports = (aria2) => { aria2.on('open', () => { console.log('aria2', 'OPEN') }) aria2.on('close', () => { console.log('aria2', 'CLOSE') }) aria2.on('input', (m) => { console.log('aria2', 'IN') console.log(inspect(m, { depth: null, colors: true })) }) aria2.on('output', (m) => { console.log('aria2', 'OUT') console.log(inspect(m, { depth: null, colors: true })) }) } ================================================ FILE: src/shared/aria2/lib/promiseEvent.js ================================================ 'use strict' module.exports = function promiseEvent (target, event) { return new Promise((resolve, reject) => { function cleanup () { target.removeListener(event, onEvent) target.removeListener('error', onError) } function onEvent (data) { resolve(data) cleanup() } function onError (err) { reject(err) cleanup() } target.addListener(event, onEvent) target.addListener('error', onError) }) } ================================================ FILE: src/shared/colors.json ================================================ { "active": "#5b5bea", "waiting": "#737373", "paused": "#737373", "error": "#FF6157", "complete": "#2ACB42", "removed": "#737373", "seeding": "#2ACB42" } ================================================ FILE: src/shared/configKeys.js ================================================ const userKeys = [ 'auto-check-update', 'auto-hide-window', 'auto-sync-tracker', 'cookie', 'enable-upnp', 'engine-bin-path', 'engine-max-connection-per-server', 'favorite-directories', 'hide-app-menu', 'history-directories', 'keep-seeding', 'keep-window-state', 'last-check-update-time', 'last-sync-tracker-time', 'locale', 'log-level', 'new-task-show-downloading', 'no-confirm-before-delete-task', 'open-at-login', 'protocols', 'proxy', 'resume-all-when-app-launched', 'run-mode', 'show-progress-bar', 'task-notification', 'theme', 'tracker-source', 'tray-speedometer' ] const systemKeys = [ 'all-proxy-passwd', 'all-proxy-user', 'all-proxy', 'allow-overwrite', 'allow-piece-length-change', 'always-resume', 'async-dns', 'auto-file-renaming', 'bt-enable-hook-after-hash-check', 'bt-enable-lpd', 'bt-exclude-tracker', 'bt-external-ip', 'bt-force-encryption', 'bt-hash-check-seed', 'bt-load-saved-metadata', 'bt-max-peers', 'bt-metadata-only', 'bt-min-crypto-level', 'bt-prioritize-piece', 'bt-remove-unselected-file', 'bt-request-peer-speed-limit', 'bt-require-crypto', 'bt-save-metadata', 'bt-seed-unverified', 'bt-stop-timeout', 'bt-tracker-connect-timeout', 'bt-tracker-interval', 'bt-tracker-timeout', 'bt-tracker', 'check-integrity', 'checksum', 'conditional-get', 'connect-timeout', 'content-disposition-default-utf8', 'continue', 'dht-file-path', 'dht-file-path6', 'dht-listen-port', 'dir', 'dry-run', 'enable-http-keep-alive', 'enable-http-pipelining', 'enable-mmap', 'enable-peer-exchange', 'file-allocation', 'follow-metalink', 'follow-torrent', 'force-save', 'force-sequential', 'ftp-passwd', 'ftp-pasv', 'ftp-proxy-passwd', 'ftp-proxy-user', 'ftp-proxy', 'ftp-reuse-connection', 'ftp-type', 'ftp-user', 'gid', 'hash-check-only', 'header', 'http-accept-gzip', 'http-auth-challenge', 'http-no-cache', 'http-passwd', 'http-proxy-passwd', 'http-proxy-user', 'http-proxy', 'http-user', 'https-proxy-passwd', 'https-proxy-user', 'https-proxy', 'index-out', 'listen-port', 'lowest-speed-limit', 'max-concurrent-downloads', 'max-connection-per-server', 'max-download-limit', 'max-file-not-found', 'max-mmap-limit', 'max-overall-download-limit', 'max-overall-upload-limit', 'max-resume-failure-tries', 'max-tries', 'max-upload-limit', 'metalink-base-uri', 'metalink-enable-unique-protocol', 'metalink-language', 'metalink-location', 'metalink-os', 'metalink-preferred-protocol', 'metalink-version', 'min-split-size', 'no-file-allocation-limit', 'no-netrc', 'no-proxy', 'no-want-digest-header', 'out', 'parameterized-uri', 'pause-metadata', 'pause', 'piece-length', 'proxy-method', 'realtime-chunk-checksum', 'referer', 'remote-time', 'remove-control-file', 'retry-wait', 'reuse-uri', 'rpc-listen-port', 'rpc-save-upload-metadata', 'rpc-secret', 'seed-ratio', 'seed-time', 'select-file', 'split', 'ssh-host-key-md', 'stream-piece-selector', 'timeout', 'uri-selector', 'use-head', 'user-agent' ] const needRestartKeys = [ 'dht-listen-port', 'hide-app-menu', 'listen-port', 'rpc-listen-port', 'rpc-secret' ] export { userKeys, systemKeys, needRestartKeys } ================================================ FILE: src/shared/constants.js ================================================ export const EMPTY_STRING = '' export const PORTABLE_EXECUTABLE_DIR = process.env.PORTABLE_EXECUTABLE_DIR export const IS_PORTABLE = PORTABLE_EXECUTABLE_DIR && PORTABLE_EXECUTABLE_DIR !== EMPTY_STRING export const APP_THEME = { AUTO: 'auto', LIGHT: 'light', DARK: 'dark' } export const APP_RUN_MODE = { STANDARD: 1, TRAY: 2, HIDE_TRAY: 3 } export const ADD_TASK_TYPE = { URI: 'uri', TORRENT: 'torrent' } export const TASK_STATUS = { ACTIVE: 'active', WAITING: 'waiting', PAUSED: 'paused', ERROR: 'error', COMPLETE: 'complete', REMOVED: 'removed', SEEDING: 'seeding' } export const LOG_LEVELS = [ 'error', 'warn', 'info', 'verbose', 'debug', 'silly' ] export const MAX_NUM_OF_DIRECTORIES = 5 export const ENGINE_RPC_HOST = '127.0.0.1' export const ENGINE_RPC_PORT = 16800 export const ENGINE_MAX_CONCURRENT_DOWNLOADS = 10 export const ENGINE_MAX_CONNECTION_PER_SERVER = 64 export const UNKNOWN_PEERID = '%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00' export const UNKNOWN_PEERID_NAME = 'unknown' export const GRAPHIC = '░▒▓█' export const ONE_SECOND = 1000 export const ONE_MINUTE = ONE_SECOND * 60 export const ONE_HOUR = ONE_MINUTE * 60 export const ONE_DAY = ONE_HOUR * 24 // 12 Hours export const AUTO_SYNC_TRACKER_INTERVAL = ONE_HOUR * 12 // One Week export const AUTO_CHECK_UPDATE_INTERVAL = ONE_DAY * 7 export const MAX_BT_TRACKER_LENGTH = 6144 /** * @see https://github.com/ngosang/trackerslist */ export const NGOSANG_TRACKERS_BEST_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt' export const NGOSANG_TRACKERS_BEST_IP_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best_ip.txt' export const NGOSANG_TRACKERS_ALL_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt' export const NGOSANG_TRACKERS_ALL_IP_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_ip.txt' export const NGOSANG_TRACKERS_BEST_URL_CDN = 'https://cdn.jsdelivr.net/gh/ngosang/trackerslist/trackers_best.txt' export const NGOSANG_TRACKERS_BEST_IP_URL_CDN = 'https://cdn.jsdelivr.net/gh/ngosang/trackerslist/trackers_best_ip.txt' export const NGOSANG_TRACKERS_ALL_URL_CDN = 'https://cdn.jsdelivr.net/gh/ngosang/trackerslist/trackers_all.txt' export const NGOSANG_TRACKERS_ALL_IP_URL_CDN = 'https://cdn.jsdelivr.net/gh/ngosang/trackerslist/trackers_all_ip.txt' /** * @see https://github.com/XIU2/TrackersListCollection */ export const XIU2_TRACKERS_BEST_URL = 'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/best.txt' export const XIU2_TRACKERS_ALL_URL = 'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/all.txt' export const XIU2_TRACKERS_HTTP_URL = 'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/http.txt' export const XIU2_TRACKERS_BEST_URL_CDN = 'https://cdn.jsdelivr.net/gh/XIU2/TrackersListCollection/best.txt' export const XIU2_TRACKERS_ALL_URL_CDN = 'https://cdn.jsdelivr.net/gh/XIU2/TrackersListCollection/all.txt' export const XIU2_TRACKERS_HTTP_URL_CDN = 'https://cdn.jsdelivr.net/gh/XIU2/TrackersListCollection/http.txt' // For bt-exclude-tracker export const XIU2_TRACKERS_BLACK_URL = 'https://cdn.jsdelivr.net/gh/XIU2/TrackersListCollection/blacklist.txt' export const TRACKER_SOURCE_OPTIONS = [ { label: 'ngosang/trackerslist', options: [ { value: NGOSANG_TRACKERS_BEST_URL, label: 'trackers_best.txt', cdn: false }, { value: NGOSANG_TRACKERS_BEST_IP_URL, label: 'trackers_best_ip.txt', cdn: false }, { value: NGOSANG_TRACKERS_ALL_URL, label: 'trackers_all.txt', cdn: false }, { value: NGOSANG_TRACKERS_ALL_IP_URL, label: 'trackers_all_ip.txt', cdn: false }, { value: NGOSANG_TRACKERS_BEST_URL_CDN, label: 'trackers_best.txt', cdn: true }, { value: NGOSANG_TRACKERS_BEST_IP_URL_CDN, label: 'trackers_best_ip.txt', cdn: true }, { value: NGOSANG_TRACKERS_ALL_URL_CDN, label: 'trackers_all.txt', cdn: true }, { value: NGOSANG_TRACKERS_ALL_IP_URL_CDN, label: 'trackers_all_ip.txt', cdn: true } ] }, { label: 'XIU2/TrackersListCollection', options: [ { value: XIU2_TRACKERS_BEST_URL, label: 'best.txt', cdn: false }, { value: XIU2_TRACKERS_ALL_URL, label: 'all.txt', cdn: false }, { value: XIU2_TRACKERS_HTTP_URL, label: 'http.txt', cdn: false }, { value: XIU2_TRACKERS_BEST_URL_CDN, label: 'best.txt', cdn: true }, { value: XIU2_TRACKERS_ALL_URL_CDN, label: 'all.txt', cdn: true }, { value: XIU2_TRACKERS_HTTP_URL_CDN, label: 'http.txt', cdn: true } ] } ] export const PROXY_SCOPES = { DOWNLOAD: 'download', UPDATE_APP: 'update-app', UPDATE_TRACKERS: 'update-trackers' } export const PROXY_SCOPE_OPTIONS = [ PROXY_SCOPES.DOWNLOAD, PROXY_SCOPES.UPDATE_APP, PROXY_SCOPES.UPDATE_TRACKERS ] export const NONE_SELECTED_FILES = 'none' export const SELECTED_ALL_FILES = 'all' export const IP_VERSION = { V4: 4, V6: 6 } export const LOGIN_SETTING_OPTIONS = { // For Windows args: [ '--opened-at-login=1' ] } export const TRAY_CANVAS_CONFIG = { WIDTH: 66, HEIGHT: 16, ICON_WIDTH: 16, ICON_HEIGHT: 16, TEXT_WIDTH: 46, TEXT_FONT_SIZE: 8 } export const COMMON_RESOURCE_TAGS = ['http://', 'https://', 'ftp://', 'magnet:'] export const THUNDER_RESOURCE_TAGS = ['thunder://'] export const RESOURCE_TAGS = [ ...COMMON_RESOURCE_TAGS, ...THUNDER_RESOURCE_TAGS ] export const SUPPORT_RTL_LOCALES = [ /* 'العربية', Arabic */ 'ar', /* 'فارسی', Persian */ 'fa', /* 'עברית', Hebrew */ 'he', /* 'Kurdî / كوردی', Kurdish */ 'ku', /* 'پنجابی', Western Punjabi */ 'pa', /* 'پښتو', Pashto, */ 'ps', /* 'سنڌي', Sindhi */ 'sd', /* 'اردو', Urdu */ 'ur', /* 'ייִדיש', Yiddish */ 'yi' ] export const IMAGE_SUFFIXES = [ '.ai', '.bmp', '.eps', '.fig', '.gif', '.heic', '.icn', '.ico', '.jpeg', '.jpg', '.png', '.psd', '.raw', '.sketch', '.svg', '.tif', '.webp', '.xd' ] export const AUDIO_SUFFIXES = [ '.aac', '.ape', '.flac', '.flav', '.m4a', '.mp3', '.ogg', '.wav', '.wma' ] export const VIDEO_SUFFIXES = [ '.avi', '.m4v', '.mkv', '.mov', '.mp4', '.mpg', '.rmvb', '.vob', '.wmv' ] export const SUB_SUFFIXES = [ '.ass', '.idx', '.smi', '.srt', '.ssa', '.sst', '.sub' ] export const DOCUMENT_SUFFIXES = [ '.azw3', '.csv', '.doc', '.docx', '.epub', '.key', '.mobi', '.numbers', '.pages', '.pdf', '.ppt', '.pptx', '.txt', '.xsl', '.xslx' ] ================================================ FILE: src/shared/keymap.json ================================================ { "cmdctrl-q": "application:quit", "cmdctrl-n": "application:new-task", "cmdctrl-shift-n": "application:new-bt-task", "cmdctrl-o": "application:open-file", "cmdctrl-l": "application:task-list", "cmdctrl-,": "application:preferences", "cmdctrl-shift-p": "application:pause-all-task", "cmdctrl-shift-r": "application:resume-all-task", "ctrl-shift-a": "application:select-all-task" } ================================================ FILE: src/shared/locales/LocaleManager.js ================================================ import i18next from 'i18next' import { getLanguage } from '@shared/locales' export default class LocaleManager { constructor (options = {}) { this.options = options i18next.init({ fallbackLng: 'en-US', resources: options.resources }) } changeLanguage (lng) { return i18next.changeLanguage(lng) } changeLanguageByLocale (locale) { const lng = getLanguage(locale) return this.changeLanguage(lng) } getI18n () { return i18next } } ================================================ FILE: src/shared/locales/all.js ================================================ import eleLocaleAr from 'element-ui/lib/locale/lang/ar' import eleLocaleBg from 'element-ui/lib/locale/lang/bg' import eleLocaleCa from 'element-ui/lib/locale/lang/ca' import eleLocaleDe from 'element-ui/lib/locale/lang/de' import eleLocaleEl from 'element-ui/lib/locale/lang/el' import eleLocaleEn from 'element-ui/lib/locale/lang/en' import eleLocaleEs from 'element-ui/lib/locale/lang/es' import eleLocaleFa from 'element-ui/lib/locale/lang/fa' import eleLocaleFr from 'element-ui/lib/locale/lang/fr' import eleLocaleHu from 'element-ui/lib/locale/lang/hu' import eleLocaleId from 'element-ui/lib/locale/lang/id' import elelocaleIt from 'element-ui/lib/locale/lang/it' import eleLocaleJa from 'element-ui/lib/locale/lang/ja' import eleLocaleKo from 'element-ui/lib/locale/lang/ko' import eleLocaleNb from 'element-ui/lib/locale/lang/nb-NO' import eleLocaleNl from 'element-ui/lib/locale/lang/nl' import eleLocalePl from 'element-ui/lib/locale/lang/pl' import eleLocalePtBR from 'element-ui/lib/locale/lang/pt-br' import eleLocaleRo from 'element-ui/lib/locale/lang/ro' import eleLocaleRu from 'element-ui/lib/locale/lang/ru-RU' import eleLocaleTh from 'element-ui/lib/locale/lang/th' import eleLocaleTr from 'element-ui/lib/locale/lang/tr-TR' import eleLocaleUk from 'element-ui/lib/locale/lang/ua' import eleLocaleVi from 'element-ui/lib/locale/lang/vi' import eleLocaleZhCN from 'element-ui/lib/locale/lang/zh-CN' import eleLocaleZhTW from 'element-ui/lib/locale/lang/zh-TW' import appLocaleAr from '@shared/locales/ar' import appLocaleBg from '@shared/locales/bg' import appLocaleCa from '@shared/locales/ca' import appLocaleDe from '@shared/locales/de' import appLocaleEl from '@shared/locales/el' import appLocaleEnUS from '@shared/locales/en-US' import appLocaleEs from '@shared/locales/es' import appLocaleFa from '@shared/locales/fa' import appLocaleFr from '@shared/locales/fr' import appLocaleHu from '@shared/locales/hu' import appLocaleId from '@shared/locales/id' import applocaleIt from '@shared/locales/it' import appLocaleJa from '@shared/locales/ja' import appLocaleKo from '@shared/locales/ko' import appLocaleNb from '@shared/locales/nb' import appLocaleNl from '@shared/locales/nl' import appLocalePl from '@shared/locales/pl' import appLocalePtBR from '@shared/locales/pt-BR' import appLocaleRo from '@shared/locales/ro' import appLocaleRu from '@shared/locales/ru' import appLocaleTh from '@shared/locales/th' import appLocaleTr from '@shared/locales/tr' import appLocaleUk from '@shared/locales/uk' import appLocaleVi from '@shared/locales/vi' import appLocaleZhCN from '@shared/locales/zh-CN' import appLocaleZhTW from '@shared/locales/zh-TW' // Please keep the locale key in alphabetical order. /* eslint-disable quote-props */ const resources = { 'ar': { translation: { ...eleLocaleAr, ...appLocaleAr } }, 'bg': { translation: { ...eleLocaleBg, ...appLocaleBg } }, 'ca': { translation: { ...eleLocaleCa, ...appLocaleCa } }, 'de': { translation: { ...eleLocaleDe, ...appLocaleDe } }, 'el': { translation: { ...eleLocaleEl, ...appLocaleEl } }, 'en-US': { translation: { ...eleLocaleEn, ...appLocaleEnUS } }, 'es': { translation: { ...eleLocaleEs, ...appLocaleEs } }, 'fa': { translation: { ...eleLocaleFa, ...appLocaleFa } }, 'fr': { translation: { ...eleLocaleFr, ...appLocaleFr } }, 'hu': { translation: { ...eleLocaleHu, ...appLocaleHu } }, 'id': { translation: { ...eleLocaleId, ...appLocaleId } }, 'it': { translation: { ...elelocaleIt, ...applocaleIt } }, 'ja': { translation: { ...eleLocaleJa, ...appLocaleJa } }, 'ko': { translation: { ...eleLocaleKo, ...appLocaleKo } }, 'nb': { translation: { ...eleLocaleNb, ...appLocaleNb } }, 'nl': { translation: { ...eleLocaleNl, ...appLocaleNl } }, 'pl': { translation: { ...eleLocalePl, ...appLocalePl } }, 'pt-BR': { translation: { ...eleLocalePtBR, ...appLocalePtBR } }, 'ro': { translation: { ...eleLocaleRo, ...appLocaleRo } }, 'ru': { translation: { ...eleLocaleRu, ...appLocaleRu } }, 'th': { translation: { ...eleLocaleTh, ...appLocaleTh } }, 'tr': { translation: { ...eleLocaleTr, ...appLocaleTr } }, 'uk': { translation: { ...eleLocaleUk, ...appLocaleUk } }, 'vi': { translation: { ...eleLocaleVi, ...appLocaleVi } }, 'zh-CN': { translation: { ...eleLocaleZhCN, ...appLocaleZhCN } }, 'zh-TW': { translation: { ...eleLocaleZhTW, ...appLocaleZhTW } } } /* eslint-enable quote-props */ export default resources ================================================ FILE: src/shared/locales/app.js ================================================ import appLocaleAr from '@shared/locales/ar' import appLocaleBg from '@shared/locales/bg' import appLocaleCa from '@shared/locales/ca' import appLocaleDe from '@shared/locales/de' import appLocaleEl from '@shared/locales/el' import appLocaleEnUS from '@shared/locales/en-US' import appLocaleFa from '@shared/locales/fa' import appLocaleFr from '@shared/locales/fr' import appLocaleHu from '@shared/locales/hu' import appLocaleId from '@shared/locales/id' import appLocaleIt from '@shared/locales/it' import appLocaleJa from '@shared/locales/ja' import appLocaleNl from '@shared/locales/nl' import appLocaleKo from '@shared/locales/ko' import appLocalePl from '@shared/locales/pl' import appLocalePtBR from '@shared/locales/pt-BR' import appLocaleRo from '@shared/locales/ro' import appLocaleRu from '@shared/locales/ru' import appLocaleTh from '@shared/locales/th' import appLocaleTr from '@shared/locales/tr' import appLocaleUk from '@shared/locales/uk' import appLocaleVi from '@shared/locales/vi' import appLocaleZhCN from '@shared/locales/zh-CN' import appLocaleZhTW from '@shared/locales/zh-TW' // Please keep the locale key in alphabetical order. /* eslint-disable quote-props */ const resources = { 'ar': { translation: { ...appLocaleAr } }, 'bg': { translation: { ...appLocaleBg } }, 'ca': { translation: { ...appLocaleCa } }, 'de': { translation: { ...appLocaleDe } }, 'el': { translation: { ...appLocaleEl } }, 'en-US': { translation: { ...appLocaleEnUS } }, 'fa': { translation: { ...appLocaleFa } }, 'fr': { translation: { ...appLocaleFr } }, 'hu': { translation: { ...appLocaleHu } }, 'id': { translation: { ...appLocaleId } }, 'it': { translation: { ...appLocaleIt } }, 'ja': { translation: { ...appLocaleJa } }, 'nl': { translation: { ...appLocaleNl } }, 'ko': { translation: { ...appLocaleKo } }, 'pl': { translation: { ...appLocalePl } }, 'pt-BR': { translation: { ...appLocalePtBR } }, 'ro': { translation: { ...appLocaleRo } }, 'ru': { translation: { ...appLocaleRu } }, 'th': { translation: { ...appLocaleTh } }, 'tr': { translation: { ...appLocaleTr } }, 'uk': { translation: { ...appLocaleUk } }, 'vi': { translation: { ...appLocaleVi } }, 'zh-CN': { translation: { ...appLocaleZhCN } }, 'zh-TW': { translation: { ...appLocaleZhTW } } } /* eslint-enable quote-props */ export default resources ================================================ FILE: src/shared/locales/ar/about.js ================================================ export default { 'engine-version': 'إصدار المحرك', 'license': 'الرخصة', 'about': 'حول', 'release': 'الإصدار', 'support': 'الدعم' } ================================================ FILE: src/shared/locales/ar/app.js ================================================ export default { 'task-list': 'قائمة التحميلات', 'add-task': 'إضافة تحميل', 'about': 'حول موتركس', 'preferences': 'التفضيلات...', 'check-for-updates': 'التحقق من وجود تحديثات ...', 'check-updates-now': 'تحقق الآن', 'checking-for-updates': 'جاري التحقق من وجود تحديثات...', 'check-for-updates-title': 'التحقق من وجود تحديثات', 'update-available-message': 'يتوفر إصدار أحدث من موتركس، تحديث الآن؟', 'update-not-available-message': 'لديك أحدث إصدار!', 'update-downloaded-message': 'جاهز للتثبيت...', 'update-error-message': 'حدث خطأ أثناء التحديث', 'engine-damaged-message': 'المحرك متضرر، الرجاء إعادة التثبيت : (', 'engine-missing-message': 'المحرك مفقود، الرجاء إعادة التثبيت : (', 'system-error-title': 'خطأ في النظام', 'system-error-message': 'فشل بدء تشغيل التطبيق: {{message}}', 'hide': 'إخفاء موتركس', 'hide-others': 'إخفاء الآخرين', 'unhide': 'إظهار الكل', 'show': 'إظهار موتركس', 'quit': 'الخروج من موتركس', 'under-development-message': 'عذراً، هذه الميزة قيد التطوير...', 'yes': 'نعم', 'no': 'لا', 'save': 'يحفظ', 'reset': 'ينبذ', 'cancel': 'إلغاء', 'submit': 'إرسال', 'gt1d': 'أكثر من يوم', 'hour': 'س', 'minute': 'د', 'second': 'ث' } ================================================ FILE: src/shared/locales/ar/edit.js ================================================ export default { 'undo': 'تراجع', 'redo': 'إعادة', 'cut': 'قص', 'copy': 'نسخ', 'paste': 'لصق', 'delete': 'حذف', 'select-all': 'تحديد الكل' } ================================================ FILE: src/shared/locales/ar/help.js ================================================ export default { 'official-website': 'موقع موتركس', 'manual': 'دليل الاستخدام', 'release-notes': 'ملاحظات الإصدار...', 'report-problem': 'الإبلاغ عن مشكلة', 'toggle-dev-tools': 'تفعيل أدوات المطور' } ================================================ FILE: src/shared/locales/ar/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ar/menu.js ================================================ export default { 'app': 'موتركس', 'file': 'الملف', 'task': 'التحميل', 'edit': 'تعديل', 'window': 'النافذة', 'help': 'المساعدة' } ================================================ FILE: src/shared/locales/ar/preferences.js ================================================ export default { 'basic': 'أساسية', 'advanced': 'متقدمة', 'lab': 'المختبر', 'save': 'حفظ وتطبيق', 'save-success-message': 'تم حفظ التفضيلات بنجاح', 'save-fail-message': 'فشل في حفظ التفضيلات', 'discard': 'تجاهل', 'startup': 'التشغيل', 'open-at-login': 'الفتح عند تسجيل الدخول', 'keep-window-state': 'المحافظة على حجم وموضع النافذة عند الخروج', 'auto-resume-all': 'استئناف جميع التحميلات غير المكتملة تلقائيًا', 'default-dir': 'المسار الافتراضي', 'mas-default-dir-tips': '~/Downloads نظراً لقيود آلية تحديد الصلاحيات في متجر أبل، يستحسن ضبط مجلد التحميل الافتراضي إلى', 'transfer-settings': 'النقل', 'transfer-speed-upload': 'حد سرعة الرفع', 'transfer-speed-download': 'حد سرعة التحميل', 'transfer-speed-unlimited': 'غير محدود', 'bt-settings': 'بت تورنت', 'bt-save-metadata': 'احفظ البيانات الوصفية للرابط المغناطيسي كملف تورنت', 'bt-auto-download-content': 'قم تلقائيا بتنزيل محتوى المغناطيس والسيل', 'bt-force-encryption': 'تشفير BT الإجباري', 'keep-seeding': 'الحفاظ على البزرة حتى يتم ايقافها يدويًا', 'seed-ratio': 'نسبة البذرة', 'seed-time': 'وقت البذرة', 'seed-time-unit': 'دقائق', 'task-manage': 'إدارة التحميلات', 'max-concurrent-downloads': 'الحد الأقصى من التحميلات النشطة', 'max-connection-per-server': 'الحد الأقصى من الاتصالات لكل خادم', 'new-task-show-downloading': 'إظهار التحميل تلقائيًا بعد إضافة التحميل', 'no-confirm-before-delete-task': 'لاتطلب التأكيد قبل حذف التحميل', 'continue': 'الإستمرارية', 'task-completed-notify': 'إشعار بعد اكتمال التحميل', 'auto-purge-record': 'مسح سجلات التحميل تلقائيًا عند الخروج من التطبيق', 'ui': 'الواجهة', 'appearance': 'المظهر', 'theme-auto': 'تلقائي', 'theme-light': 'فاتح', 'theme-dark': 'داكن', 'auto-hide-window': 'إخفاء النافذة تلقائيًا', 'run-mode': 'تشغيل كـ', 'run-mode-standard': 'التطبيق القياسي', 'run-mode-tray': 'تطبيق العلبة', 'run-mode-hide-tray': 'إخفاء تطبيق العلبة', 'tray-speedometer': 'يعرض درج شريط القوائم السرعة في الوقت الفعلي', 'show-progress-bar': 'عرض شريط تقدم التنزيل', 'language': 'اللغة', 'change-language': 'تغيير اللغة', 'hide-app-menu': 'إخفاء قائمة التطبيقات (Windows و Linux فقط)', 'proxy': 'الخادم الوسيط', 'enable-proxy': 'تفعيل الخادم الوسيط', 'proxy-bypass-input-tips': 'تخطي إعدادات الخادم الوسيط لهذه المضيفات والمجالات، واحدة لكل سطر', 'proxy-scope-download': 'تنزيل', 'proxy-scope-update-app': 'تحديث التطبيق', 'proxy-scope-update-trackers': 'تحديث المتتبعات', 'proxy-tips': 'عرض دليل الخادم الوسيط', 'bt-tracker': 'خوادم التعقب', 'bt-tracker-input-tips': 'خوادم التعقب، واحدة لكل سطر', 'bt-tracker-tips': 'مستحسن: ', 'sync-tracker-tips': 'مزامنة', 'auto-sync-tracker': 'تحديث قائمة التعقب تلقائيًا كل يوم', 'port': 'منافذ الاستماع', 'bt-port': 'منفذ BT للاستماع', 'dht-port': 'منفذ DHT للاستماع', 'security': 'الحماية', 'rpc': 'RPC', 'rpc-listen-port': 'منفذ استماع RPC', 'rpc-secret': 'رمز RPC السري', 'rpc-secret-tips': 'عرض دليل رمز RPC السري', 'developer': 'المطور', 'user-agent': 'User-Agent', 'mock-user-agent': 'وكيل مستخدم وهمي', 'aria2-conf-path': 'مسار aria2.conf المدمج', 'app-log-path': 'مسار سجلات التطبيق', 'download-session-path': 'مسار التحميلات', 'session-reset': 'إعادة التحميل', 'session-reset-confirm': 'هل أنت متأكد أنك تريد إعادة تحميله؟', 'factory-reset': 'العودة إلى الإعدادات الافتراضية', 'factory-reset-confirm': 'هل أنت متأكد من العودة إلى الإعدادات الافتراضية؟', 'lab-warning': '⚠️ قد يؤدي تمكين إضافات المختبر إلى تعطل التطبيق أو فقدان البيانات، لذا قرر على مسؤوليتك الخاصة!', 'download-protocol': 'البروتوكولات', 'protocols-default-client': 'تعيين كعميل افتراضي للبروتوكولات التالية', 'protocols-magnet': 'المغناطيس [ magnet:// ]', 'protocols-thunder': 'الرعد [ thunder:// ]', 'browser-extensions': 'الإضافات', 'baidu-exporter': 'مُصدر بايدو (Baidu)', 'browser-extensions-tips': 'مقدمة من المجتمع، ', 'baidu-exporter-help': 'اضغط هنا لبدء الاستخدام', 'auto-update': 'التحديث التلقائي', 'auto-check-update': 'تحقق تلقائيًا من التحديث', 'last-check-update-time': 'آخر مرة تم التحقق من وجود تحديثات', 'not-saved': 'التفضيلات غير محفوظة', 'not-saved-confirm': 'ستفقد التفضيلات التي تم تغييرها ، هل أنت متأكد من المغادرة؟' } ================================================ FILE: src/shared/locales/ar/subnav.js ================================================ export default { 'task-list': 'قائمة التحميلات', 'preferences': 'التفضيلات' } ================================================ FILE: src/shared/locales/ar/task.js ================================================ export default { 'active': 'جاري التحميل', 'waiting': 'جاري الانتظار', 'stopped': 'المتوقفة', 'new-task': 'اضافة تحميل جديد', 'new-bt-task': 'اضافة تحميل تورنت جديد', 'open-file': 'فتح ملف تورنت...', 'uri-task': 'رابط التحميل', 'torrent-task': 'تورنت', 'uri-task-tips': 'عنوان تحميل واحد لكل سطر، (يدعم الروابط المغناطيسية)', 'thunder-link-tips': 'نصيحة: قد لا تكون روابط الرعد قابلة للتحميل بعد فك التشفير', 'new-task-uris-required': 'الرجاء إدخال رابط تحميل صالح واحد على الأقل', 'new-task-torrent-required': 'الرجاء اختيار ملف تورنت', 'file-name': 'اسم الملف', 'file-extension': 'نوع الملف', 'file-size': 'حجم الملف', 'file-completed-size': 'تم التنزيل', 'selected-files-sum': 'الملف المختار: {{selectedFilesCount}} ملف, الحجم الكلي {{selectedFilesTotalSize}}', 'select-at-least-one': 'الرجاء تحديد ملف واحد على الأقل', 'task-gid': 'GID', 'task-name': 'اسم التحميل', 'task-out': 'إعادة تسمية', 'task-out-tips': 'اختياري', 'task-split': 'تقسيم', 'task-dir': 'حفظ إلى', 'pause-task': 'إيقاف مؤقت', 'task-ua': 'UA', 'task-user-agent': 'وكيل المستخدم', 'task-authorization': 'تفويض', 'task-referer': 'المرجع', 'task-cookie': 'الكوكيز', 'task-proxy': 'الخادم الوسيط', 'task-error-info': 'خطأ', 'task-piece': 'قطعة', 'task-piece-length': 'حجم القطعة', 'task-num-pieces': 'قطع', 'task-bittorrent-info': 'معلومات التورنت', 'task-info-hash': 'تجزئة', 'task-bittorrent-creation-date': 'تاريخ الإنشاء', 'task-bittorrent-comment': 'تعليق', 'task-progress-info': 'تقدم', 'task-status': 'حالة', 'task-num-seeders': 'بزار', 'task-connections': 'روابط', 'task-file-size': 'بحجم', 'task-download-speed': 'سرعة التنزيل', 'task-upload-speed': 'سرعة التحميل', 'task-download-length': 'تم التنزيل', 'task-upload-length': 'تم الرفع', 'task-ratio': 'نسبة', 'task-peer-host': 'مضيف', 'task-peer-ip': 'IP', 'task-peer-client': 'عميل', 'navigate-to-downloading': 'الانتقال إلى التحميل', 'show-advanced-options': 'الخيارات المتقدمة', 'copyright-warning': 'تحذير حقوق الطبع والنشر', 'copyright-warning-message': 'قد يكون الملف الذي تريد تحميله محميًا بحقوق الطبع والنشر بالصوت أو بالفيديو، يرجى التأكد من حصولك على إذن للوصول إليه.', 'copyright-yes': 'نعم، أملك الإذن', 'copyright-no': 'لا، لا أملك الإذن', 'copyright-error-message': 'فشل في إضافة التحميل بسبب حقوق الطبع والنشر', 'pause-task-success': 'تم بنجاح ايقاف التحميل "{{taskName}}"', 'pause-task-fail': ' فشل في ايقاف التحميل "{{taskName}}"', 'resume-task': 'إستئناف التحميل', 'resume-task-success': 'تم بنجاح إستئناف التحميل "{{taskName}}"', 'resume-task-fail': 'فشل في إستئناف التحميل "{{taskName}}"', 'delete-task': 'حذف التحميل', 'delete-selected-tasks': 'حذف التحميلات المحددة', 'delete-task-confirm': 'هل أنت متأكد أنك تريد حذف تحميل "{{taskName}}" ؟', 'batch-delete-task-confirm': 'هل انت متأكد أنك تريد حذف {{count}} التحميلات دفعة واحدة', 'delete-task-label': 'حذف مع الملفات', 'delete-task-success': 'تم بنجاح حذف التحميل "{{taskName}}"', 'delete-task-fail': 'فشل في حذف التحميل "{{taskName}}"', 'remove-task-file-fail': 'فشل في حذف ملف(ات) التحميل، الرجاء حذفها يدويًا', 'remove-task-config-file-fail': 'فشل في حذف ملف تهيئة التحميل (config file)، يرجى حذفه يدويًا', 'move-task-up': 'تحريك التحميل لأعلى', 'move-task-down': 'تحريك التحميل لأسغل', 'pause-all-task': 'إيقاف جميع التحميلات', 'pause-all-task-success': 'تم إيقاف جميع التحميلات بنجاح', 'pause-all-task-fail': 'فشل في إيقاف جميع التحميلات', 'resume-all-task': 'إستئناف جميع التحميلات', 'resume-all-task-success': 'تم إستئناف جميع التحميلات بنجاح', 'resume-all-task-fail': 'فشل في إستئناف جميع التحميلات', 'select-all-task': 'تحديد جميع التحميلات', 'clear-recent-tasks': 'حذف التحميلات الحديثة', 'purge-record': 'تطهير سجل التحميل', 'purge-record-success': 'تم تطهير سجلات المهة بنجاح', 'purge-record-fail': 'فشل في تطهير سجلات التحميل', 'batch-delete-task-success': 'تم بنجاح حذف التحميلات دفعة واحدة', 'batch-delete-task-fail': 'فشل في حذف التحميلات دفعة واحدة', 'refresh-list': 'تحديث قائمة التحميلات', 'no-task': 'لا توجد تحميلات حالية', 'copy-link': 'نسخ الرابط', 'copy-link-success': 'تم نسخ الرابط بنجاح', 'remove-record': 'حذف سجل التحميل', 'remove-record-confirm': 'هل أنت متأكد انك تريد حذف سجل التحميل الخاص بـ "{{taskName}}" ؟', 'remove-record-label': 'حذف مع الملفات', 'remove-record-success': 'تم بنجاح حذف سجل التحميل الخاص بـ "{{taskName}}"', 'remove-record-fail': 'فشل في حذف سجل التحميل الخاص بـ "{{taskName}}"', 'show-in-folder': 'عرض التحميل في المجلد', 'file-not-exist': 'الملف المُستَهدَف غير موجود أو تم حذفه', 'file-path-error': 'خطأ في مسار الملف', 'opening-task-message': 'جاري فتح "{{taskName}}" ...', 'get-task-name': 'جاري جلب إسم الملف...', 'remaining-prefix': 'متبقى', 'select-torrent': 'أفلت ملف التورنت هنا، أو اضغط تحديد', 'task-info-dialog-title': '{{title}} تفاصيل', 'download-start-message': 'بدأ تحميل {{taskName}}', 'download-pause-message': 'وقف تحميل {{taskName}} مؤقتاً', 'download-stop-message': 'وقف تحميل {{taskName}}', 'download-error-message': 'حدث خطأ أثناء تحميل {{taskName}}', 'download-complete-message': 'اكتمل تحميل {{taskName}}', 'download-complete-notify': 'اكتمل التحميل', 'bt-download-complete-message': 'اكتمل تحميل {{taskName}، وعملية البَذر', 'bt-download-complete-notify': 'اكتمل تحميل التورنت، وعملية البَذر...', 'bt-download-complete-tips': 'نصيحة: يمكنك إيقاف التحميل لإنهاء عملية البَذر', 'bt-stopping-seeding-tip': 'جاري إيقاف عملية البَذر، سيستغرق قطع الاتصال بعض الوقت، الرجاء الانتظار...', 'download-fail-message': 'فشل تحميل {{taskName}}', 'download-fail-notify': 'فشل التحميل' } ================================================ FILE: src/shared/locales/ar/window.js ================================================ export default { 'reload': 'إعادة تحميل', 'close': 'إغلاق', 'minimize': 'تصغير', 'zoom': 'تكبير', 'toggle-fullscreen': 'تفعيل وضع ملء الشاشة', 'front': 'إحضار الكل إلى المقدمة' } ================================================ FILE: src/shared/locales/bg/about.js ================================================ export default { 'engine-version': 'Версия', 'license': 'Лиценз', 'about': 'Информация', 'release': 'Съобщение', 'support': 'Подкрепа' } ================================================ FILE: src/shared/locales/bg/app.js ================================================ export default { 'task-list': 'Задачи', 'add-task': 'Добавяне на задача', 'about': 'О Motrix', 'preferences': 'Меню...', 'check-for-updates': 'Проверка на актуализацията...', 'check-updates-now': 'Проверете сега', 'checking-for-updates': 'Проверка за актуализации...', 'check-for-updates-title': 'Проверка за актуализации', 'update-available-message': 'Новата версия на Motrix е достъпна за изтегляне, Изтеглете сега?', 'update-not-available-message': 'Вече използвате най-новата версия!', 'update-downloaded-message': 'Готово за инсталиране...', 'update-error-message': 'Грешка при обновяване', 'engine-damaged-message': 'Програмата е повредена, моля преинсталирайте :(', 'engine-missing-message': 'Програмата е загубена, моля преинсталирайте :(', 'system-error-title': 'Грешка', 'system-error-message': 'Грешка при стартиране на приложението: {{message}}', 'hide': 'Скрия Motrix', 'hide-others': 'Скрийте всичко останало', 'unhide': 'Показване на всички', 'show': 'Показване Motrix', 'quit': 'Затваряне Motrix', 'under-development-message': 'За съжаление тази функция все още е в процес на разработка...', 'yes': 'Да', 'no': 'Не', 'save': 'Запазете', 'reset': 'Изхвърлете', 'cancel': 'Отказ', 'submit': 'Потвърдя', 'gt1d': '> 1 ден', 'hour': 'ч', 'minute': 'м', 'second': 'с' } ================================================ FILE: src/shared/locales/bg/edit.js ================================================ export default { 'undo': 'Отказ', 'redo': 'Повторя', 'cut': 'Нарежа', 'copy': 'Копирам', 'paste': 'Поставя', 'delete': 'Премахна', 'select-all': 'Изберете всички' } ================================================ FILE: src/shared/locales/bg/help.js ================================================ export default { 'official-website': 'Сайт Motrix', 'manual': 'Инструкция', 'release-notes': 'Маркировки...', 'report-problem': 'Докладвайте за проблем', 'toggle-dev-tools': 'Превключване на инструменти за разработчици' } ================================================ FILE: src/shared/locales/bg/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/bg/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Файл', 'task': 'Задача', 'edit': 'Редктиране', 'window': 'Прозорец', 'help': 'Грижа' } ================================================ FILE: src/shared/locales/bg/preferences.js ================================================ export default { 'basic':'Основа', 'advanced':'Разширени', 'lab':'Лаборатория', 'save': 'Съхронване и прилагане', 'save-success-message':'настройките са запазени успешно', 'save-fail-message': 'грешка при запазване на настройките', 'discard': 'Отказ', 'startup': 'стартиране', 'open-at-login': 'стартиране на програмата заедно със стартирането на операционната система', 'keep-window-state':'по време на затваряне на приложението, Запишете розмера и позицията на прозореца', 'auto-resume-all':'автоматично възобновяване на всички недовършени задачи', 'default-dir':'пея по подразбиране', 'mas-default-dir-tips': 'поради ограничения в App Store, препоръчително е да зададете пътя по подразбиране като ~/Downloads', 'transfer-settings':'скоростна кутия', 'transfer-speed-upload': 'лимит на откат', 'transfer-speed-download': 'лимит за изтегляне', 'transfer-speed-unlimited':'Unlimited', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Запазете магнитната връзка като торент файл', 'bt-auto-download-content': 'Автоматично изтегляне на магнит и торент съдържание', 'bt-force-encryption': 'BT задължително криптиране', 'keep-seeding': 'Продължавайте да засявате, докато не го спрете ръчно', 'seed-ratio': 'Съотношение на семената', 'seed-time': 'Време на семената', 'seed-time-unit': 'минути', 'task-manage':'мениджър на задачи', 'max-concurrent-downloads': 'Максимум активни задачи', 'max-connection-per-server': 'максимални връзки към сървъра', 'new-task-show-downloading': 'автоматично показване на задача след добавяне', 'no-confirm-before-delete-task': 'Не се изисква потвърждение преди изтриване на задача', 'continue':'Продължи', 'task-completed-notify': 'съобщение след изтеглянето', 'auto-purge-record': 'автоматично почистване на записите за изтегляне след затваряне на приложението', 'ui': 'UI', 'appearance': 'външен вид', 'theme-auto':'автоматично', 'theme-light':'Светло', 'theme-dark':'dark', 'auto-hide-window': 'Автоматично отваряне на прозорци', 'run-mode': 'Бягай като', 'run-mode-standard': 'стандартно приложение', 'run-mode-tray': 'Приложение в таванчето', 'run-mode-hide-tray': 'Скриване на приложение в таванчето', 'tray-speedometer': 'Таблата на лентата с менюта показва скорост в реално време', 'language':'Език', 'change-language': 'промяна на езика', 'hide-app-menu': 'Скриване на менюто на приложението (само за Windows и Linux)', 'proxy': 'Proxy', 'enable-proxy': 'използване на Proxy', 'proxy-bypass-input-tips': 'заобикаляне на настройките на прокси за тези хостове и домейни, един по един на ред', 'proxy-scope-download': 'Изтегляне', 'proxy-scope-update-app': 'Актуализиране на приложението', 'proxy-scope-update-trackers': 'Актуализиране на трекери', 'proxy-tips': 'преглед на ръководството за прокси', 'bt-tracker':'Tracker сървър', 'bt-tracker-input-tips': 'Tracker сървър, един на ред', 'bt-tracker-tips': 'препоръчително:', 'sync-tracker-tips':'Синхронизация', 'auto-sync-tracker':'актуализиране на списъка с тракери всеки ден автоматично', 'port':'портове за слушане', 'bt-port':'пристанище на слушане BT', 'dht-port':'DHT слушане Порт', 'security':'сигурност', 'rpc': 'RPC', 'rpc-listen-port': 'RPC слушащ порт', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Гледайте инструкцията RPC Secret', 'developer':'developer', 'Mock-user-agent':'оформление User-Agent', 'aria2-conf-path': 'Вграден път за aria2.conf', 'app-log-path': 'път към дневника на приложението', 'download-session-path': 'качване на пътя на сесията', 'factory-reset': 'Настройки по подразбиране', 'factory-reset-confirm': 'Сигурни ли сте, че искате да се върнете към настройките по подразбиране?', 'lab-warning': '️ ️ включването на функциите на лабораторията може да доведе до срив на приложението и загуба на данни, решете на свой риск!', 'download-protocol':'протоколи', 'protocols-default-client':'Задаване като клиент по подразбиране за следните протоколи', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Розширения', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'предоставени от общността,', 'baidu-exporter-help': 'Кликнете тук, за да използвате', 'auto-update':'автоматично обновяване', 'auto-check-update':'автоматична проверка на актуализациите', 'last-check-update-time': 'последната актуализация е проверена', 'not-saved': 'Предпочитанията не са запазени', 'not-saved-confirm': 'Променените предпочитания ще бъдат загубени, сигурни ли сте, че ще напуснете?' } ================================================ FILE: src/shared/locales/bg/subnav.js ================================================ export default { 'task-list':'Задачи', 'preferences':'Настройки' } ================================================ FILE: src/shared/locales/bg/task.js ================================================ export default { 'active':'Активен', 'waiting':'чакане', 'stopped':'спряно', 'new-task': 'нова задача', 'new-bt-task': 'Нова BT задача', 'open-file': 'отваряне на торент файл...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'една URL задача в низ (поддръжка на magnet)', 'thunder-link-tips': 'съвет: връзките от типа Thunder може да не се зареждат след декодиране', 'new-task-uris-required': 'въведете валиден URL адрес на ресурс', 'new-task-torrent-required': 'моля, изберете торент файл', 'file-name': 'Име на файл', 'file-extension':'тип файл', 'file-size': 'Размер', 'file-completed-size': 'Изтеглено', 'selected-files-sum': 'избрано: {{selectedFilesCount}} файлове, общ размер {{selectedFilesTotalSize}}', 'select-at-least-one': 'Моля, изберете поне един файл', 'task-gid': 'GID', 'task-name':'Име на изтегляне', 'task-out': 'Преименуване', 'task-out-tips':'незадължителен', 'task-split': 'разделяне', 'task-dir': 'Запазване в', 'pause-task': 'пауза на задачата', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Упълномощаване', 'task-referer': 'препращане', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Грешка', 'task-piece': 'Парче', 'task-piece-length': 'Размер на парче', 'task-num-pieces': 'Парчета', 'task-bittorrent-info': 'Информация за торента', 'task-info-hash': 'Хеш', 'task-bittorrent-creation-date': 'Дата на създаване', 'task-bittorrent-comment': 'Коментирайте', 'task-progress-info': 'Напредък', 'task-status': 'Състояние', 'task-num-seeders': 'Сеялки', 'task-connections': 'Връзки', 'task-file-size': 'Размер', 'task-download-speed': 'Скорост на сваляне', 'task-upload-speed': 'Скорост на качване', 'task-download-length': 'Изтеглено', 'task-upload-length': 'Качено', 'task-ratio': 'Съотношение', 'task-peer-host': 'Водещ', 'task-peer-ip': 'IP', 'task-peer-client': 'Клиент', 'navigate-to-downloading': 'напред към изтегляне', 'show-advanced-options': 'Разширени опции', 'copyright-warning':'предупреждение за авторски права', 'copyright-warning-message': 'файлът, който се опитвате да изтеглите, има авторски права върху видео или аудио съдържание, моля, проверете дали имате права да изтеглите този файл.', 'copyright-yes': 'Да, имам права', 'copyright-no': 'Не, нямам права', 'copyright-error-message': 'грешка при добавяне на задача поради проблеми с авторските права', 'pause-task-success': 'успешно спряна задача" {{TaskName}}"', 'pause-task-fail': 'грешка при спиране на задачата" {{taskName}}"', 'resume-task': 'възобновяване на задачата', 'resume-task-success': 'успешно възобновена задача" {{TaskName}}"', 'resume-task-fail': 'грешка при възобновяване на задачата" {{taskName}}"', 'delete-task': 'изтриване на задача', 'delete-selected-tasks': 'изтриване на избраните задачи', 'delete-task-confirm': 'сигурни ли Сте, че искате да изтриете задачата "{{taskName}}"?', 'batch-delete-task-confirm': 'Сигурни ли сте, че искате да изтриете {{count}} задачи за зареждане в партиден режим?', 'delete-task-label': 'изтриване заедно с файловете', 'delete-task-success': 'успешно изтрита задача" {{TaskName}}"', 'delete-task-fail': 'грешка при изтриване на задача" {{taskName}}"', 'remove-task-file-fail': 'грешка при изтриване на файл (файлове) на задача, моля, изтрийте го (тях) сами', 'remove-task-config-file-fail': 'грешка при изтриване на конфигурационния файл на заданието, моля, изтрийте го сами', 'move-task-up': 'Преместване на задача нагоре', 'move-task-down': 'Преместване на задача надолу', 'pause-all-task': 'пауза на всички задачи', 'pause-all-task-success': 'всички задачи са успешно прекратени', 'pause-all-task-fail': 'грешка при спиране на всички задачи', 'resume-all-task': 'възобновяване на всички задачи', 'resume-all-task-success': 'успешно възобновени всички задачи', 'resume-all-task-fail': 'грешка при възобновяване на всички задачи', 'select-all-task': 'Изберете всички задачи', 'clear-recent-tasks': 'Изчистване на последните задачи', 'purge-record': 'Изчистване на записките за задачи', 'purge-record-success': 'успешно изчистени записи на задачи', 'purge-record-fail': 'грешка при изчистване на записите за задачи', 'batch-delete-task-success': 'успешно изтриване на задачи в партиден режим', 'batch-delete-task-fail': 'Неуспешно изтриване на задачи в партиден режим', 'refresh-list': 'обновяване на списъка със задачи', 'no-task': 'няма текущи задачи', 'copy-link': 'копиране на връзка', 'copy-link-success': 'успешно копирана връзка', 'remove-record': 'изтриване на запис за задача', 'remove-record-confirm': 'Сигурни ли сте, че искате да изтриете записа за задачата "{{taskName}}"?', 'remove-record-label':'изтриване с файлове', 'remove-record-success': 'успешно изтрит запис за задача" {{taskName}}"', 'remove-record-fail': 'грешка при изтриване на запис за задача "{{taskName}}"', 'show-in-folder': 'показване на файловете със задачи в папка', 'file-not-exist': 'търсеният файл не съществува или е изтрит', 'file-path-error': 'Грешка в пътя към файла', 'opening-task-message': 'отваряне "{{TaskName}}" ...', 'get-task-name': 'получаване на име на задача...', 'remaining-prefix':'ляво', 'select-torrent':'плъзнете торент файла тук, или натиснете Избери', 'task-info-dialog-title':'{{Title}} детайли', 'download-start-message': 'изтеглянето започна {{taskName}}', 'download-pause-message': 'спиране на изтеглянето {{taskName}}', 'download-stop-message': 'спиране на изтеглянето {{taskName}}', 'download-error-message': 'грешка при изтегляне {{taskName}}', 'download-complete-message': 'Завършено изтегляне {{taskName}}', 'download-complete-notify':'изтеглянето Завършено', 'BT-download-complete-message': 'завършено изтегляне {{TaskName}}, раздаване', 'BT-download-complete-notify': 'BT изтеглянето приключи, раздаване...', 'BT-download-complete-tips': 'съвет: можете да спрете задачата, за да спрете раздаването', 'bt-stopping-seeding-tip': 'Спирането на засяването ще отнеме известно време, за да прекъснете връзката, моля изчакайте...', 'download-fail-message': 'не може да бъде изтеглено {{taskName}}', 'download-fail-notify': 'грешка при зареждане' } ================================================ FILE: src/shared/locales/bg/window.js ================================================ export default { 'reload': 'Перезагрузить', 'close': 'Закрыть', 'minimize': 'Свернуть', 'zoom': 'Увеличение', 'toggle-fullscreen': 'Перейти в полноэкранный режим', 'front': 'Поверх всех окон' } ================================================ FILE: src/shared/locales/ca/about.js ================================================ export default { 'engine-version': 'Versió del motor', 'license': 'Llicència', 'about': 'Sobre ', 'release': 'Llençaments', 'support': 'Suport' } ================================================ FILE: src/shared/locales/ca/app.js ================================================ export default { 'task-list': 'Tasques', 'add-task': 'Afegir tasca', 'about': 'Sobre Motrix', 'preferences': 'Preferències...', 'check-for-updates': 'Comprovar actualitzacions...', 'check-updates-now': 'Comprovar ara', 'checking-for-updates': 'Comprovant actualitzacions...', 'check-for-updates-title': 'Comprovar actualitzacions', 'update-available-message': 'Hi ha una nova versió de Motrix. Actualitzar ara?', 'update-not-available-message': 'Estàs en l\'última versió!', 'update-downloaded-message': 'Llest per instal·lar...', 'update-error-message': 'Error mentre s\'actualitzava', 'engine-damaged-message': 'El motor està danyat, per favor reinstal·la :(', 'engine-missing-message': 'No es troba el motor, por favor reinstal·la :(', 'system-error-title': 'Error del sistema', 'system-error-message': 'L\'aplicació va fallar en iniciar: {{message}}', 'hide': 'Ocultar Motrix', 'hide-others': 'Ocultar altres', 'unhide': 'Mostrar tot', 'show': 'Mostrar Motrix', 'quit': 'Sortir de Motrix', 'under-development-message': 'Ho sentim, aquesta característica està en desenvolupament...', 'yes': 'Sí', 'no': 'No', 'save': 'Desa', 'reset': 'Descarta', 'cancel': 'Cancel·lar', 'submit': 'Enviar', 'gt1d': '> 1 dia', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/ca/edit.js ================================================ export default { 'undo': 'Desfer', 'redo': 'Refer', 'cut': 'Retallar', 'copy': 'Copiar', 'paste': 'Enganxar', 'delete': 'Eliminar', 'select-all': 'Seleccionar tot' } ================================================ FILE: src/shared/locales/ca/help.js ================================================ export default { 'official-website': 'Lloc web de Motrix', 'manual': 'Manual', 'release-notes': 'Notes de la versió...', 'report-problem': 'Informar d\'un problema', 'toggle-dev-tools': 'Alternar les eines de desenvolupament' } ================================================ FILE: src/shared/locales/ca/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ca/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Arxiu', 'task': 'Tasca', 'edit': 'Editar', 'window': 'Finestra', 'help': 'Ajuda' } ================================================ FILE: src/shared/locales/ca/preferences.js ================================================ export default { 'basic': 'Bàsic', 'advanced': 'Avançat', 'lab': 'Lab', 'save': 'Guardar i aplicar', 'save-success-message': 'Preferències guardades amb èxit', 'save-fail-message': 'Hi va haver un error al guardar les teves preferències', 'discard': 'Descartar', 'startup': 'Inici', 'open-at-login': 'Obrir en iniciar sessió', 'keep-window-state': 'Mantenir la mida i la posició de la finestra al sortir', 'auto-resume-all': 'Resumir automàticament totes les tasques sense finalitzar', 'default-dir': 'Ruta per defecte', 'mas-default-dir-tips': 'Degut a les restriccions de la botiga d\'aplicacions, la ruta per defecte es recomana que sigui ~/Downloads', 'transfer-settings': 'Transmission', 'transfer-speed-upload': 'Límit de pujada', 'transfer-speed-download': 'Límit de baixada', 'transfer-speed-unlimited': 'Il·limitat', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Deseu l\'enllaç magnet com a fitxer torrent', 'bt-auto-download-content': 'Descarregueu automàticament el contingut de Magnet i Torrent', 'bt-force-encryption': 'Forçar xifratge de BT', 'keep-seeding': 'Seguiu sembrant fins aturar-lo manualment', 'seed-ratio': 'Relació de llavors', 'seed-time': 'Temps de llavors', 'seed-time-unit': 'minuts', 'task-manage': 'Gestió de tasques', 'max-concurrent-downloads': 'Tasques màximes actives', 'max-connection-per-server': 'Connexions màximes per servidor', 'new-task-show-downloading': 'Mostrar automàticament la descàrrega després d\'afegir una tasca', 'no-confirm-before-delete-task': 'No cal confirmar abans de suprimir la tasca', 'continue': 'Continuar', 'task-completed-notify': 'Notificar després que la descàrrega finalitzi', 'auto-purge-record': 'Purgar automàticament el registre de descàrregues en sortir', 'ui': 'UI', 'appearance': 'Aparença', 'theme-auto': 'Auto', 'theme-light': 'Clar', 'theme-dark': 'Fosc', 'auto-hide-window': 'Amaga automàticament les finestres', 'run-mode': 'Executa com', 'run-mode-standard': 'Aplicació estàndard', 'run-mode-tray': 'Aplicació de la safata', 'run-mode-hide-tray': 'Amagar l\'aplicació de la safata', 'tray-speedometer': 'La safata de barres de menús mostra la velocitat en temps real', 'show-progress-bar': 'Mostra la barra de progrés de la descàrrega', 'language': 'Idioma', 'change-language': 'Cambiar idioma', 'hide-app-menu': 'Ocultar el menú (només Windows i Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Activar proxy', 'proxy-bypass-input-tips': 'Eviteu la configuració del servidor intermediari per a aquests amfitrions i dominis, un per línia', 'proxy-scope-download': 'Descàrrega', 'proxy-scope-update-app': 'Actualització de l\'aplicació', 'proxy-scope-update-trackers': 'Actualitza els rastrejadors', 'proxy-tips': 'Consulteu el manual del servidor intermediari', 'bt-tracker': 'Seguir servidors', 'bt-tracker-input-tips': 'Seguir servidors, un per línia', 'bt-tracker-tips': 'Recomenat: ', 'sync-tracker-tips': 'Sincronitzar', 'auto-sync-tracker': 'Actualitza la llista de seguidors automàticament cada dia', 'port': 'Escolta Ports', 'bt-port': 'Port d\'escolta BT', 'dht-port': 'Port d\'escolta DHT', 'security': 'Seguretat', 'rpc': 'RPC', 'rpc-listen-port': 'Port d\'Escolta RPC', 'rpc-secret': 'Clau RPC', 'rpc-secret-tips': 'Mirar manual de la clau RPC', 'developer': 'Desenvolupador', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Ruta incorporada per al fitxer aria2.conf', 'app-log-path': 'Ruta del log', 'download-session-path': 'Ruta de descàrrega de la sessió', 'factory-reset': 'Reseteig de fàbrica', 'factory-reset-confirm': 'Estàs segur que vols resetejar de fàbrica?', 'lab-warning': '⚠️ Activar les característiques "Lab" pot resultar en errors i pèrdua de dades!', 'download-protocol': 'Protocols', 'protocols-default-client': 'Establir com client per defecte dels següents protocols', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Extensions', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Proporcionats per la comunitat, ', 'baidu-exporter-help': 'Fes click aquí per veure l\'ús', 'auto-update': 'Actualitzar automàticament', 'auto-check-update': 'Revisar actualitzacions automàticament', 'last-check-update-time': 'Última revisió d\'actualitzacions', 'not-saved': 'Preferències no desades', 'not-saved-confirm': 'Les preferències modificades es perdran, esteu segur que marxareu?' } ================================================ FILE: src/shared/locales/ca/subnav.js ================================================ export default { 'task-list': 'Tasques', 'preferences': 'Preferències' } ================================================ FILE: src/shared/locales/ca/task.js ================================================ export default { 'active': 'Descarregant', 'waiting': 'Esperant', 'stopped': 'Detinguda', 'new-task': 'Nova Tasca', 'new-bt-task': 'Nova Tasca BT', 'open-file': 'Obrir arxiu Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Una URL de tasca per línia (suporta magnet)', 'thunder-link-tips': 'Tip: És possible que els enllaços Thunder no es puguin descarregar després de la descodificació.', 'new-task-uris-required': 'Per favor, introdueix al menys una URL de recurs vàlida', 'new-task-torrent-required': 'Seleccioni un arxiu torrent', 'file-name': 'Nom', 'file-extension': 'Extensió', 'file-size': 'Mida', 'file-completed-size': 'Descarregat', 'selected-files-sum': 'Seleccionat: {{selectedFilesCount}} arxius, mida total: {{selectedFilesTotalSize}}', 'select-at-least-one': 'Seleccioneu com a mínim un fitxer', 'task-gid': 'GID', 'task-name': 'Nom de la tasca', 'task-out': 'Canviar nom', 'task-out-tips': 'Opcional', 'task-split': 'Dividir', 'task-dir': 'Guardar a', 'pause-task': 'Pausar tasca', 'task-ua': 'UA', 'task-user-agent': 'Usuari-Agent', 'task-authorization': 'Autorització', 'task-referer': 'Referent', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Error', 'task-piece': 'Peça', 'task-piece-length': 'Mida de la peça', 'task-num-pieces': 'Peces', 'task-bittorrent-info': 'Informació del torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Data de creació', 'task-bittorrent-comment': 'Comentari', 'task-progress-info': 'Progrés, progressar', 'task-status': 'Estat', 'task-num-seeders': 'Sembradores', 'task-connections': 'Connexions', 'task-file-size': 'Mida', 'task-download-speed': 'Velocitat de descàrrega', 'task-upload-speed': 'Velocitat de pujada', 'task-download-length': 'Descarregat', 'task-upload-length': 'Carregat', 'task-ratio': 'Relació', 'task-peer-host': 'Amfitrió', 'task-peer-ip': 'IP', 'task-peer-client': 'Client', 'navigate-to-downloading': 'Anar a Descàrregues', 'show-advanced-options': 'Opcions avançades', 'copyright-warning': 'Advertència sobre drets d\'autor', 'copyright-warning-message': 'L\'arxiu que vols descarregar pot tenir drets d\'autor d\'audio o vídeo, per favor asegura\'t que tens permís per accedir a ell.', 'copyright-yes': 'Sí, tinc permís', 'copyright-no': 'No, no tinc permís.', 'copyright-error-message': 'No s\'ha pogut afegir una tasca degut a un problema de drets d\'autor', 'pause-task-success': 'S\'ha pausat la tasca "{{taskName}}"', 'pause-task-fail': 'Hi ha hagut un problema al pausar la tasca "{{taskName}}"', 'resume-task': 'Resumir tasca', 'resume-task-success': 'S\'ha resumido la tasca "{{taskName}}"', 'resume-task-fail': 'Hi ha hagut un error en resumir la tasca "{{taskName}}"', 'delete-task': 'Eliminar tasca', 'delete-selected-tasks': 'Eliminar tasques sel·leccionades', 'delete-task-confirm': 'Estàs segur que vols eliminar la tasca "{{taskName}}"?', 'batch-delete-task-confirm': 'Esteu segur que voleu suprimir {{count}} tasques de descàrrega en un lot?', 'delete-task-label': 'Eliminar amb arxius', 'delete-task-success': 'Tasca eliminada amb èxit "{{taskName}}"', 'delete-task-fail': 'Hi ha hagut un error en eliminar la tasca "{{taskName}}"', 'remove-task-file-fail': 'No s\'han eliminat els arxius de tasques, per favor, elimina\'ls manualment.', 'remove-task-config-file-fail': 'No s\'ha pogut eliminar l\'arxiu de configuració de la tasca, per favor, elimina\' manualment.', 'move-task-up': 'Desplaçar tasca cap a dalt', 'move-task-down': 'Desplaçar tasca cap a baix', 'pause-all-task': 'Pausar totes les tasques', 'pause-all-task-success': 'S\'han pausat totes les tasques amb èxit', 'pause-all-task-fail': 'Hi ha hagut un error en pausar totes les tasques', 'resume-all-task': 'Resumir totes les tasques', 'resume-all-task-success': 'S\'han resumit amb èxit totes les tasques', 'resume-all-task-fail': 'Hi ha hagut un error en resumir totes les tasques', 'select-all-task': 'Seleccioneu tota la tasca', 'clear-recent-tasks': 'Limpiar les darreres tasques', 'purge-record': 'Purgar registre de tasques', 'purge-record-success': 'Registres de tasques purgats amb èxit', 'purge-record-fail': 'No s\'ha pogut purgar els registres de tasques', 'batch-delete-task-success': 'Suprimiu les tasques correctament al lot', 'batch-delete-task-fail': 'No s\'ha pogut suprimir les tasques del lot', 'refresh-list': 'Refrescar llista de tasques', 'no-task': 'No hi ha tasques actuals', 'copy-link': 'Copiar enllaç', 'copy-link-success': 'Enllaç copiat amb èxit', 'remove-record': 'Eliminar tasca', 'remove-record-confirm': 'Estàs segur que vols eliminar la tasca "{{taskName}}"?', 'remove-record-label': 'Eliminar amb arxius', 'remove-record-success': 'S\'ha eliminat amb èxit la tasca "{{taskName}}"', 'remove-record-fail': 'Hi ha hagut un error en eliminar la tasca "{{taskName}}"', 'show-in-folder': 'Mostrar la carpeta de la tasca', 'file-not-exist': 'L\'archivo objetiu no existeix o s\'ha eliminat', 'file-path-error': 'Error en la ruta de l\'arxiu', 'opening-task-message': 'Obrint "{{taskName}}"...', 'get-task-name': 'Obtenint el nom de la tasca...', 'remaining-prefix': 'restant', 'select-torrent': 'Arrosega un arxiu Torrent aquí o fes click en seleccionar', 'task-info-dialog-title': 'Detalls de {{title}}', 'download-start-message': 'S\'ha iniciat la descàrrega de {{taskName}}', 'download-pause-message': 'S\'ha pausat la descàrrega de {{taskName}}', 'download-stop-message': 'S\'ha detingut la descàrrega de {{taskName}}', 'download-error-message': 'Ha ocurrit un error en descarregar {{taskName}}', 'download-complete-message': 'S\'ha terminat de descarregar {{taskName}}', 'download-complete-notify': 'Descàrrega completada', 'bt-download-complete-message': 'Descàrrega completada {{taskName}}. Compartint...', 'bt-download-complete-notify': 'Descàrrega BT completa. Compartint...', 'bt-download-complete-tips': 'Tips: Pot detenir una tasca per deixar de compartir', 'bt-stopping-seeding-tip': 'Aturar la sembra, es necessitarà un temps per desconnectar-se, espereu...', 'download-fail-message': 'No s\'ha pogut descarregar {{taskName}}', 'download-fail-notify': 'Descàrrega fallida' } ================================================ FILE: src/shared/locales/ca/window.js ================================================ export default { 'reload': 'Recarregar', 'close': 'Tancar', 'minimize': 'Minimitzar', 'zoom': 'Zoom', 'toggle-fullscreen': 'Posar a Pantalla Completa', 'front': 'Posar Tot Al Front' } ================================================ FILE: src/shared/locales/de/about.js ================================================ export default { 'engine-version': 'Engine Version', 'license': 'Lizenz', 'about': 'Über', 'release': 'Versionen', 'support': 'Unterstützung anfordern' } ================================================ FILE: src/shared/locales/de/app.js ================================================ export default { 'task-list': 'Aufgaben', 'add-task': 'Aufgabe hinzufügen', 'about': 'Über Motrix', 'preferences': 'Einstellungen...', 'check-for-updates': 'Nach Updates suchen...', 'check-updates-now': 'Jetzt prüfen', 'checking-for-updates': 'Nach Updates suchen ...', 'check-for-updates-title': 'Nach Updates suchen', 'update-available-message': 'Eine neue Version von Motrix ist verfügbar, jetzt aktualisieren?', 'update-not-available-message': 'Sie sind auf dem neuesten Stand!', 'update-downloaded-message': 'Bereit zur Installation...', 'update-error-message': 'Aktualisierungsfehler', 'engine-damaged-message': 'Der Motor ist beschädigt, bitte neu installieren : (', 'engine-missing-message': 'Der Motor fehlt, bitte neu installieren : (', 'system-error-title': 'Systemfehler', 'system-error-message': 'Die Anwendung konnte nicht gestartet werden: {{message}}', 'hide': 'Motrix verbergen', 'hide-others': 'Andere verbergen', 'unhide': 'Alles anzeigen', 'show': 'Motrix anzeigen', 'quit': 'Motrix beenden', 'under-development-message': 'Entschuldigung, diese Funktion befindet sich in der Entwicklung...', 'yes': 'Ja', 'no': 'Nein', 'save': 'Speichern', 'reset': 'Verwerfen', 'cancel': 'Abbrechen', 'submit': 'Übernehmen', 'gt1d': '> 1 Tag', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/de/edit.js ================================================ export default { 'undo': 'Rückgängig', 'redo': 'Wiederholen', 'cut': 'Ausschneiden', 'copy': 'Kopieren', 'paste': 'Einfügen', 'delete': 'Löschen', 'select-all': 'Alles auswählen' } ================================================ FILE: src/shared/locales/de/help.js ================================================ export default { 'official-website': 'Motrix Website', 'manual': 'Handbuch', 'release-notes': 'Versionshinweise...', 'report-problem': 'Problem melden', 'toggle-dev-tools': 'Entwicklerwerkzeuge umschalten' } ================================================ FILE: src/shared/locales/de/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/de/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Datei', 'task': 'Aufgabe', 'edit': 'Bearbeiten', 'window': 'Fenster', 'help': 'Hilfe' } ================================================ FILE: src/shared/locales/de/preferences.js ================================================ export default { 'basic': 'Standard', 'advanced': 'Erweitert', 'lab': 'Experimentell', 'save': 'Speichern & übernehmen', 'save-success-message': 'Einstellungen erfolgreich speichern', 'save-fail-message': 'Speichern der Einstellungen fehlgeschlagen', 'discard': 'Verwerfen', 'startup': 'Startup', 'open-at-login': 'Beim Login öffnen', 'keep-window-state': 'Stellen Sie die Größe und Position des Fensters wieder her', 'auto-resume-all': 'Alle nicht abgeschlossenen Aufgaben automatisch fortsetzen', 'default-dir': 'Standardpfad', 'mas-default-dir-tips': 'Aufgrund der Einschränkungen durch Sandbox-Berechtigungen im App Store wird der Download Ordner als Standard empfohlen', 'transfer-settings': 'Übertragung', 'transfer-speed-upload': 'Upload-Limit', 'transfer-speed-download': 'Download-Limit', 'transfer-speed-unlimited': 'Unbegrenzt', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Magnetlink als Torrent-Datei speichern', 'bt-auto-download-content': 'Laden Sie den Magneten- und Torrent-Inhalt automatisch herunter', 'bt-force-encryption': 'BT Zwangskodierung', 'keep-seeding': 'Setzen Sie die Aussaat fort, bis Sie sie manuell stoppen', 'seed-ratio': 'Samenverhältnis', 'seed-time': 'Startzeit', 'seed-time-unit': 'Protokoll', 'task-manage': 'Aufgaben verwalten', 'max-concurrent-downloads': 'Maximal aktive Aufgaben', 'max-connection-per-server': 'Maximale Verbindungen pro Server', 'new-task-show-downloading': 'Nach hinzufügen einer Aufgabe zu aktiven Downloads wechseln', 'no-confirm-before-delete-task': 'Vor dem Löschen der Aufgabe ist keine Bestätigung erforderlich', 'continue': 'HTTPS/FTP Downloads fortsetzen wenn bereits angefangen', 'task-completed-notify': 'Benachrichtigung nach abgeschlossenen Download anzeigen', 'auto-purge-record': 'Download Protokoll beim Schließen der App löschen', 'ui': 'UI', 'appearance': 'Erscheinungsbild', 'theme-auto': 'Automatisch', 'theme-light': 'Hell', 'theme-dark': 'Dunkel', 'auto-hide-window': 'Fenster automatisch ausblenden', 'run-mode': 'Rennen wie', 'run-mode-standard': 'Standardanwendung', 'run-mode-tray': 'Infobereichsanwendung', 'run-mode-hide-tray': 'Infobereichsanwendung ausblenden', 'tray-speedometer': 'Das Menüleistenfach zeigt die Echtzeitgeschwindigkeit an', 'show-progress-bar': 'Fortschrittsbalken anzeigen', 'language': 'Sprache', 'change-language': 'Sprache ändern', 'hide-app-menu': 'App Menü ausblenden (nur auf Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Proxy aktivieren', 'proxy-bypass-input-tips': 'Proxy-Einstellungen für diese Hosts und Domänen umgehen, eine pro Zeile', 'proxy-scope-download': 'Herunterladen', 'proxy-scope-update-app': 'Anwendung aktualisieren', 'proxy-scope-update-trackers': 'Tracker aktualisieren', 'proxy-tips': 'Proxy-Handbuch anzeigen', 'bt-tracker': 'Tracker-Server', 'bt-tracker-input-tips': 'Tracker-Server, einer pro Zeile', 'bt-tracker-tips': 'Empfehlen:', 'sync-tracker-tips': 'Synchronisieren', 'auto-sync-tracker': 'Aktualisieren Sie die Trackerliste jeden Tag automatisch', 'port': 'Listen Ports', 'bt-port': 'BT Listen Port', 'dht-port': 'DHT Listen Port', 'security': 'Sicherheit', 'rpc': 'RPC', 'rpc-listen-port': 'RPC-Hörport', 'rpc-secret': 'RPC-Geheimnis', 'rpc-secret-tips': 'Geheime RPC-Anleitung anzeigen', 'developer': 'Entwickler', 'user-agent': 'User-Agent', 'mock-user-agent': 'User-Agent simulieren', 'aria2-conf-path': 'Integrierter aria2.conf-Pfad', 'app-log-path': 'Appprotokollpfad', 'download-session-path': 'Downloadsitzungspfad', 'session-reset': 'Download-Session zurücksetzen', 'session-reset-confirm': 'Sind Sie sicher, dass Sie die Download-Session zurücksetzen wollen?', 'factory-reset': 'Werkseinstellungen', 'factory-reset-confirm': 'Sollen die Einstellungen auf die Werkseinstellungen unwiderruflich zurückgesetzt werden?', 'lab-warning': '⚠️ Die Aktivierung von experimentellen Funktionen kann zu App-Abstürzen oder Datenverlust führen!', 'download-protocol': 'Protokoll', 'protocols-default-client': 'Als Standardclient für die folgenden Protokolle festlegen', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Erweiterungen', 'baidu-exporter': 'Baidu Exporter', 'browser-extensions-tips': 'Von der Community bereitgestellt, ', 'baidu-exporter-help': 'mehr über die Verwendung zu erfahren', 'auto-update': 'Auto-Update', 'auto-check-update': 'Automatisch auf Updates prüfen', 'last-check-update-time': 'letzte kontrolle update - zeit', 'not-saved': 'Einstellungen nicht gespeichert', 'not-saved-confirm': 'Die geänderten Einstellungen gehen verloren. Möchten Sie wirklich gehen?' } ================================================ FILE: src/shared/locales/de/subnav.js ================================================ export default { 'task-list': 'Aufgaben', 'preferences': 'Einstellungen' } ================================================ FILE: src/shared/locales/de/task.js ================================================ export default { 'active': 'Aktiv', 'waiting': 'Warteschlange', 'stopped': 'Gestoppt', 'new-task': 'Neue Aufgabe', 'new-bt-task': 'Neue BT Aufgabe', 'open-file': 'Torrent-Datei öffnen...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Eine Download URL pro Zeile (magnet wird unterstützt)', 'thunder-link-tips': 'Tipp: Thunder Links werden möglicherweise nach dem dekodieren nicht heruntergeladen', 'new-task-uris-required': 'Bitte geben Sie mindestens eine gültige URL ein', 'new-task-torrent-required': 'Bitte wählen Sie eine Torrent-Datei', 'file-name': 'Dateiname', 'file-extension': 'Dateityp', 'file-size': 'Dateigröße', 'file-completed-size': 'Heruntergeladen', 'selected-files-sum': 'Ausgewählt: {{selectedFilesCount}} Dateien, insgesamt {{selectedFilesTotalSize}}', 'select-at-least-one': 'Bitte wählen Sie mindestens eine Datei aus', 'task-gid': 'GID', 'task-name': 'Aufgaben Name', 'task-out': 'Dateiname', 'task-out-tips': 'Optional', 'task-split': 'Splits', 'task-dir': 'Ordner', 'pause-task': 'Aufgabe pausieren', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorisierung', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Error', 'task-piece': 'Stück', 'task-piece-length': 'Stückgröße', 'task-num-pieces': 'Stücke', 'task-bittorrent-info': 'Torrent Info', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Erstellungsdatum', 'task-bittorrent-comment': 'Kommentar', 'task-progress-info': 'Fortschritt', 'task-status': 'Status', 'task-num-seeders': 'Sämaschinen', 'task-connections': 'Verbindungen', 'task-file-size': 'Größe', 'task-download-speed': 'Download-Geschwindigkeit', 'task-upload-speed': 'Upload-Geschwindigkeit', 'task-download-length': 'Heruntergeladen', 'task-upload-length': 'Hochgeladen', 'task-ratio': 'Verhältnis', 'task-peer-host': 'Gastgeber', 'task-peer-ip': 'IP', 'task-peer-client': 'Klient', 'navigate-to-downloading': 'Navigiere zu aktive Downloads', 'show-advanced-options': 'Erweiterte Optionen', 'copyright-warning': 'Copyright Warnung', 'copyright-warning-message': 'Die Datei, die Sie herunterladen möchten, könnte unter Copyright stehen. Bitte überprüfen Sie ob Sie in Besitz der notwendigen Lizenz sind.', 'copyright-yes': 'Ja, Ich habe', 'copyright-no': 'Nein', 'copyright-error-message': 'Aufgabe konnte Aufgrund von Copyright Problemen nicht hinzugefügt werden', 'pause-task-success': 'Aufgabe "{{taskName}}" erfolgreich pausiert', 'pause-task-fail': 'Aufgabe "{{taskName}}" pausieren fehlgeschlagen', 'resume-task': 'Aufgabe fortsetzen', 'resume-task-success': 'Aufgabe "{{taskName}}" erfolgreich fortgesetzt', 'resume-task-fail': 'Aufgabe "{{taskName}}" fortsetzen ist fehlgschlagen', 'delete-task': 'Aufgabe löschen', 'delete-selected-tasks': 'Ausgewählte Aufgaben löschen', 'delete-task-confirm': 'Den Download von "{{taskName}}" unwiederruflich löschen?', 'batch-delete-task-confirm': 'Möchten Sie wirklich {{count}} Download-Aufgaben im Batch entfernen?', 'delete-task-label': 'Datei auch löschen', 'delete-task-success': 'Aufgabe "{{taskName}}" erfolgreich gelöscht', 'delete-task-fail': 'Aufgabe "{{taskName}}" löschen fehlgeschlagen', 'remove-task-file-fail': 'Aufgaben Datei löschen fehlgeschlagen, bitte manuell löschen', 'remove-task-config-file-fail': 'Aufgaben Konfigurationsdatei löschen fehlgeschlagen, bitte manuell löschen', 'move-task-up': 'Aufgabe nach oben verschieben', 'move-task-down': 'Aufgabe nach unten verschieben', 'pause-all-task': 'Alle Aufgaben pausieren', 'pause-all-task-success': 'Alle Aufgaben erfolgreich pausiert', 'pause-all-task-fail': 'Die Aufgaben konnten nicht pausiert werden', 'resume-all-task': 'Alle Aufgaben fortsetzen', 'resume-all-task-success': 'Alle Aufgaben erfolgreich fortgesetzt', 'resume-all-task-fail': 'Fortsetzen aller Aufgaben fehlgeschlagen', 'select-all-task': 'Wählen Sie alle Aufgaben aus', 'clear-recent-tasks': 'Letzte Aufgaben entfernen', 'purge-record': 'Aufgaben Protokoll löschen', 'purge-record-success': 'Aufgabenprotokoll erfolgreich gelöscht', 'purge-record-fail': 'Aufgabenprotokoll konnte nicht gelöscht werden', 'batch-delete-task-success': 'Aufgaben im Batch erfolgreich löschen', 'batch-delete-task-fail': 'Fehler beim Löschen von Aufgaben im Stapel', 'refresh-list': 'Aufgabenliste aktualisieren', 'no-task': 'Keine Downloads aktiv', 'copy-link': 'Link kopieren', 'copy-link-success': 'Link erfolgreich kopiert', 'remove-record': 'Aufgaben Aufzeichnung löschen', 'remove-record-confirm': 'Soll die Aufzeichnung von "{{taskName}}" gelöscht werden?', 'remove-record-label': 'Datei auch löschen', 'remove-record-success': 'Aufzeichnung von "{{taskName}}" erfolgreich gelöscht', 'remove-record-fail': 'Aufzeichnung von "{{taskName}}" konnte nicht gelöscht werden', 'show-in-folder': 'Aufgabe in Ordner anzeigen', 'file-not-exist': 'Datei existiert nicht oder wurde gelöscht', 'file-path-error': 'Dateipfadfehler', 'opening-task-message': 'Öffne "{{taskName}}" ...', 'get-task-name': 'Aufgabenbezeichnung bekommen...', 'remaining-prefix': 'Verbleibend', 'select-torrent': 'Torrent mit Drag & Drop hinzufügen, oder klicken um auszuwählen', 'task-info-dialog-title': '{{title}} Details', 'download-start-message': 'Starte Download von {{taskName}}', 'download-pause-message': 'Pausiere Download von {{taskName}}', 'download-stop-message': 'Download von {{taskName}} gestoppt', 'download-error-message': 'Fehler beim Download von {{taskName}} aufgetreten', 'download-complete-message': 'Download von {{taskName}} abgeschlossen', 'download-complete-notify': 'Download abgeschlossen', 'bt-download-complete-message': 'Download von {{taskName}} abgeschlossen, aussaat...', 'bt-download-complete-notify': 'BT Download abgeschlossen, Aussaat...', 'bt-download-complete-tips': 'Tipps: Sie können die Aufgabe stoppen, die aussaat zu beenden', 'bt-stopping-seeding-tip': 'Wenn Sie die Aussaat beenden, dauert es einige Zeit, bis die Verbindung getrennt ist. Bitte warten Sie ...', 'download-fail-message': 'Download von {{taskName}} fehlgeschlagen', 'download-fail-notify': 'Download fehlgeschlagen' } ================================================ FILE: src/shared/locales/de/window.js ================================================ export default { 'reload': 'Neu laden', 'close': 'Schließen', 'minimize': 'Minimieren', 'zoom': 'Zoomen', 'toggle-fullscreen': 'Vollbildmodus aktivieren', 'front': 'Alles in den Vordergrund bringen' } ================================================ FILE: src/shared/locales/el/about.js ================================================ export default { 'engine-version': 'Έκδοση Μηχανής', 'license': 'Άδεια', 'about': 'Σχετικά', 'release': 'Έκδοση', 'support': 'Υποστήριξη' } ================================================ FILE: src/shared/locales/el/app.js ================================================ export default { 'task-list': 'Καθήκοντα', 'add-task': 'Προσθήκη Εργασίας', 'about': 'Σχετικά με το Motrix', 'preferences': 'Προτιμήσεις...', 'check-for-updates': 'Έλεγχος για Ενημερώσεις...', 'check-updates-now': 'Έλεγχος τώρα', 'checking-for-updates': 'Γίνεται Έλεγχος για Ενημερώσεις ...', 'check-for-updates-title': 'Έλεγχος για ενημερώσεις', 'update-available-message': 'Μία νέα έκδοση του Motrix είναι διαθέσιμη, ενημέρωση τώρα;', 'update-not-available-message': 'Έχετε τη τελευταία έκδοση!', 'update-downloaded-message': 'Έτοιμο για εγκατάσταση...', 'update-error-message': 'Σφάλμα κατά την Ενημέρωση', 'engine-damaged-message': 'Σφάλμα στη μηχανή, παρακαλώ κάντε επανεγκατάσταση : (', 'engine-missing-message': 'Η μηχανή λείπει, παρακαλώ κάντε επανεγκατάσταση : (', 'system-error-title': 'Σφάλμα Συστήματος', 'system-error-message': 'Η εκκίνηση της εφαρμογής απέτυχε: {{message}}', 'hide': 'Κρύψε το Motrix', 'hide-others': 'Κρύψε τα υπόλοιπα', 'unhide': 'Εμφάνιση όλων', 'show': 'Εμφάνιση του Motrix', 'quit': 'Κλείσιμο του Motrix', 'under-development-message': 'Συγγνώμη, αυτή η λειτουργία είναι υπό ανάπτυξη...', 'yes': 'Ναι', 'no': 'Όχι', 'save': 'Σώσει', 'reset': 'Απορρίπτω', 'cancel': 'Ακύρωση', 'submit': 'Υποβολή', 'gt1d': '> 1 μέρα', 'hour': 'ωρ', 'minute': 'λ', 'second': 'δ' } ================================================ FILE: src/shared/locales/el/edit.js ================================================ export default { 'undo': 'Αναίρεση', 'redo': 'Ακύρωση Αναίρεσης', 'cut': 'Αποκοπή', 'copy': 'Αντιγραφή', 'paste': 'Επικόλληση', 'delete': 'Διαγραφή', 'select-all': 'Επιλογή Όλων' } ================================================ FILE: src/shared/locales/el/help.js ================================================ export default { 'official-website': 'Ιστοσελίδα του Motrix', 'manual': 'Εγχειρίδιο Οδηγιών', 'release-notes': 'Σημειώσεις έκδοσης...', 'report-problem': 'Αναφέρετε ένα πρόβλημα', 'toggle-dev-tools': 'Εμφάνιση εργαλείων για προγραμματιστές' } ================================================ FILE: src/shared/locales/el/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/el/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Αρχείο', 'task': 'Εργασία', 'edit': 'Επεξεργασία', 'window': 'Παράθυρο', 'help': 'Βοήθεια' } ================================================ FILE: src/shared/locales/el/preferences.js ================================================ export default { 'basic': 'Βασικές', 'advanced': 'Προχωρημένες', 'lab': 'Εργαστήριο', 'save': 'Αποθήκευση & Εφαρμογή', 'save-success-message': 'Οι προτιμήσεις αποθηκεύτηκαν επιτυχώς', 'save-fail-message': 'Η αποθήκευση των προτιμήσεων απέτυχε', 'discard': 'Απόρριψη', 'startup': 'Εκκίνηση', 'open-at-login': 'Εκκίνηση κατά τη σύνδεση', 'keep-window-state': 'Αποθήκευσε το μέγεθος και τη θέση του παραθύρου κατά την έξοδο', 'auto-resume-all': 'Συνέχισε αυτόματα όλες τις μη τελειωμένες λήψεις', 'default-dir': 'Προεπιλεγμένος φάκελος λήψεων', 'mas-default-dir-tips': 'Λόγω των περιορισμών δικαιωμάτων του App Store,ο προεπιλεγμένος φάκελος λήψεων συστήνεται να είναι το ~/Downloads', 'transfer-settings': 'Μετάδοση', 'transfer-speed-upload': 'Όριο αποστολής', 'transfer-speed-download': 'Όριο λήψης', 'transfer-speed-unlimited': 'Χωρίς όριο', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Αποθηκεύστε το σύνδεσμο μαγνήτη ως αρχείο torrent', 'bt-auto-download-content': 'Αυτόματη λήψη περιεχομένου μαγνήτη και χείμαρξης', 'bt-force-encryption': 'Εξαναγκασμός κρυπτογράφησης BT', 'keep-seeding': 'Συνεχίστε να σπέρνετε μέχρι να το σταματήσετε χειροκίνητα', 'seed-ratio': 'Αναλογία σπόρου', 'seed-time': 'Χρόνος σπόρου', 'seed-time-unit': 'λεπτά', 'task-manage': 'Διαχείριση εργασιών', 'max-concurrent-downloads': 'Μέγιστες ενεργές Εργασίες', 'max-connection-per-server': 'Μέγιστες συνδέσεις ανά Σέρβερ', 'new-task-show-downloading': 'Δείξε αυτόματα τη λήψη μετά τη πρόσθεση της εργασίας', 'no-confirm-before-delete-task': 'Δεν χρειάζεται επιβεβαίωση πριν τη διαγραφή της εργασίας', 'continue': 'Συνέχεια', 'task-completed-notify': 'Ειδοποίηση μόλις τελειώσει η λήψη', 'auto-purge-record': 'Καθάρισε αυτόματα τη λίστα λήψεων κατά το κλείσιμο της εφαρμογής', 'ui': 'UI', 'appearance': 'Θέμα', 'theme-auto': 'Αυτόματο', 'theme-light': 'Ανοιχτό', 'theme-dark': 'Σκούρο', 'auto-hide-window': 'Αυτόματη απόκρυψη του παραθύρου', 'run-mode': 'Εκτέλεση ως', 'run-mode-standard': 'Κανονική εφαρμογή', 'run-mode-tray': 'Εφαρμογή περιοχής ειδοποιήσεων', 'run-mode-hide-tray': 'Απόκρυψη εφαρμογής της περιοχής ειδοποιήσεων', 'tray-speedometer': 'Δείξε τη ταχύτητα στο μενου', 'show-progress-bar': 'Εμφάνιση μπάρας προόδου λήψης', 'language': 'Γλώσσα', 'change-language': 'Αλλαγή Γλώσσας', 'hide-app-menu': 'Κρύψε το μενού της εφαρμογής (μόνο για Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Χρησιμοίησε Proxy', 'proxy-bypass-input-tips': 'Μη χρησιμοποιήσεις ρυθμίσεις proxy για αυτούς τους Hosts και τα Domains, ένα ανά γραμμή', 'proxy-scope-download': 'Λήψη', 'proxy-scope-update-app': 'Ενημέρωση εφαρμογής', 'proxy-scope-update-trackers': 'Ενημέρωση Trackers', 'proxy-tips': 'Εμφάνιση εγχειριδίου Proxy', 'bt-tracker': 'Tracker Servers', 'bt-tracker-input-tips': 'Tracker servers, ένα ανά γραμμή', 'bt-tracker-tips': 'Συνιστόνται: ', 'sync-tracker-tips': 'Συγχρονισμός', 'auto-sync-tracker': 'Ανανέωση της λίστας των tracker κάθε μέρα αυτόματα', 'port': 'Ενεργές θύρες', 'bt-port': 'Ενεργή θύρα BT', 'dht-port': 'Ενεργή θύρα DHT', 'security': 'Ασφάλεια', 'rpc': 'RPC', 'rpc-listen-port': 'Θύρα Ακρόασης RPC', 'rpc-secret': 'Μυστικό για το RPC', 'rpc-secret-tips': 'Εμφάνιση εγχειριδίου για το Μυστικό RPC', 'developer': 'Προγραμματιστής', 'user-agent': 'User-Agent', 'mock-user-agent': 'Πλαστό User-Agent', 'aria2-conf-path': 'Ενσωματωμένη διαδρομή aria2.conf', 'app-log-path': 'Διαδρομή για το αρχείο log της εφαρμογής', 'download-session-path': 'Διαδρομή λήψεων για αυτή τη συνεδρία', 'factory-reset': 'Επαναφορά στις εργοστασιακές ρυθμίσεις', 'factory-reset-confirm': 'Είστε σίγουροι ότι θέλετε να επαναφέρετε τις εργοστασιακές ρυθμίσεις;', 'lab-warning': '⚠️ Η ενεργοποίηση ρυθμίσεων Εργαστηρίου μπορεί να οδηγήσει σε κολλήματα στην εφαρμογή ή απώλεια δεδομένων, αποφασίστε με δικό σας ρίσκο', 'download-protocol': 'Πρωτόκολλα', 'protocols-default-client': 'Ρύθμιση ως προεπιλεγμένο πρόγραμμα για τα παρακάτω πρωτόκολλα', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Επεκτάσεις', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Παρέχεται από την κοινότητα, ', 'baidu-exporter-help': 'Κάντε κλικ εδώ για χρήση', 'auto-update': 'Αυτόματη ενημέρωση', 'auto-check-update': 'Αυτόματος έλεγχος για ενημερώσεις', 'last-check-update-time': 'Τελευταία φορά που έγινε έλεγχος για Ενημερώσεις', 'not-saved': 'Οι προτιμήσεις δεν αποθηκεύτηκαν', 'not-saved-confirm': 'Οι αλλαγμένες προτιμήσεις θα χαθούν, είστε σίγουροι ότι θα φύγετε;' } ================================================ FILE: src/shared/locales/el/subnav.js ================================================ export default { 'task-list': 'Εργασίες', 'preferences': 'Προτιμήσεις' } ================================================ FILE: src/shared/locales/el/task.js ================================================ export default { 'active': 'Γίνεται λήψη', 'waiting': 'Σε αναμονή', 'stopped': 'Σταματημένα', 'new-task': 'Νέα εργασία', 'new-bt-task': 'Νέα εργασία BT', 'open-file': 'Άνοιγμα αρχείου Torrent ...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Ένα url εργασίας ανά γραμμή (υποστηρίζονται magnet)', 'thunder-link-tips': 'Συμβουλή: Οι σύνδεσμοι Thunder μπορεί να μην είναι δυνατόν να κατέβουν μετά την αποκωδικοποίηση', 'new-task-uris-required': 'Παρακαλώ εισάγετε τουλάχιστον ένα έγκυρο url', 'new-task-torrent-required': 'Παρακαλώ επιλέξτε ένα αρχεί torrent', 'file-name': 'Όνομα αρχείου', 'file-extension': 'Τύπος αρχείου', 'file-size': 'Μέγεθος αρχείου', 'file-completed-size': 'Έγινε λήψη', 'selected-files-sum': 'Επιλεγμένα: {{selectedFilesCount}} αρχεία, συνολικό μέγεθος {{selectedFilesTotalSize}}', 'select-at-least-one': 'Επιλέξτε τουλάχιστον ένα αρχείο', 'task-gid': 'GID', 'task-name': 'Όνομα εργασίας', 'task-out': 'Άλλο Όνομα', 'task-out-tips': 'Προαιρετικό', 'task-split': 'Κομμάτια', 'task-dir': 'Αποθήκευση σε', 'pause-task': 'Παύση εργασίας', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Εξουσιοδότηση', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Λάθος', 'task-piece': 'Κομμάτι', 'task-piece-length': 'Μέγεθος κομματιού', 'task-num-pieces': 'Κομμάτια', 'task-bittorrent-info': 'Πληροφορίες Torrent', 'task-info-hash': 'Χασίσι', 'task-bittorrent-creation-date': 'Ημερομηνία δημιουργίας', 'task-bittorrent-comment': 'Σχόλιο', 'task-progress-info': 'Πρόοδος', 'task-status': 'Κατάσταση', 'task-num-seeders': 'Σπόροι', 'task-connections': 'Συνδέσεις', 'task-file-size': 'Μέγεθος', 'task-download-speed': 'Ταχύτητα μεταφόρτωσης', 'task-upload-speed': 'Ταχύτητα μεταφόρτωσης', 'task-download-length': 'Έγινε λήψη', 'task-upload-length': 'Μεταφορτώθηκε', 'task-ratio': 'Αναλογία', 'task-peer-host': 'Πλήθος', 'task-peer-ip': 'IP', 'task-peer-client': 'Πελάτης', 'navigate-to-downloading': 'Πήγαινε στις λήψεις', 'show-advanced-options': 'Ρυθμίσεις για προχωρημένους', 'copyright-warning': 'Προειδοποίηση για Πνευματικά Δικαιώματα', 'copyright-warning-message': 'Το αρχείο που θέλετε να κατεβάσετε μπορεί να περιέχει οπτικό ή ακουστικό υλικό που προστατεύεται από πνευματικά δικαιώματα, παρακαλώ σιγουρευτείτε ότι έχετε άδεια πρόσβασης σε αυτό.', 'copyright-yes': 'Ναι, έχω άδεια', 'copyright-no': 'Όχι, δεν έχω άδεια', 'copyright-error-message': 'Η πρόσθεση του αρχείου απέτυχε λόγω προστασίας πνευματικών δικαιωμάτων', 'pause-task-success': 'Η παύση της εργασίας "{{taskName}}" ολοκληρώθηκε επιτυχώς', 'pause-task-fail': 'Η παύση της εργασίας "{{taskName}}" απέτυχε', 'resume-task': 'Συνέχεια Εργασίας', 'resume-task-success': 'Η εργασία "{{taskName}}" συνέχισε επιτυχώς', 'resume-task-fail': 'Η εργασία "{{taskName}}" απέτυχε να συνεχίσει', 'delete-task': 'Διαγραφή εργασίας', 'delete-selected-tasks': 'Διαγραφή επιλεγμένων εργασιών', 'delete-task-confirm': 'Είστε σίγουροι ότι θέλετε να διαγράψετε την εργασία λήψης "{{taskName}}";', 'batch-delete-task-confirm': 'Είστε σίγουροι ότι θέλετε να διαγράψετε {{count}} εργασίες λήψης μαζί;', 'delete-task-label': 'Διαγραφή μαζί με το αρχείο', 'delete-task-success': 'Επιτυχής διαγραφή της εργασίας "{{taskName}}"', 'delete-task-fail': 'Η διαγραφή της εργασίας "{{taskName}}" απέτυχε', 'remove-task-file-fail': 'Η διαγραφή του αρχείου/των αρχείων της εργασίας απέτυχε, παρακαλώ διαγράψτε τα χειροκίνητα', 'remove-task-config-file-fail': 'Η διαγραφή του αρχείου ρυθμίσεων της εργασίας απέτυχε, παρακαλώ διαγράψτε το χειροκίνητα', 'move-task-up': 'Μεταφορά της Εργασίας Πάνω', 'move-task-down': 'Μεταφορά της Εργασίας Κάτω', 'pause-all-task': 'Παύση όλων τον εργασιών', 'pause-all-task-success': 'Η παύση όλων των εργασιών ήταν επιτυχής', 'pause-all-task-fail': 'Αποτυχία στη παύση όλων των εργασιών', 'resume-all-task': 'Συνέχεια όλων των εργασιών', 'resume-all-task-success': 'Όλες οι εργασίες συνέχισαν επιτυχώς', 'resume-all-task-fail': 'Αποτυχία στη συνέχεια όλων τον εργασιών', 'select-all-task': 'Επιλογή όλων των εργασιών', 'clear-recent-tasks': 'Καθαρισμός πρόσφατων λήψεων', 'purge-record': 'Καθαρισμός αρχείου λήψεων', 'purge-record-success': 'Επιτυχής καθαρισμός αρχείου λήψεων', 'purge-record-fail': 'Αποτυχία στον καθαρισμό αρχείου λήψεων', 'batch-delete-task-success': 'Η μαζική διαγραφή εργασιών ήταν επιτυχής', 'batch-delete-task-fail': 'Απέτυχε η μαζική διαγραφή εργασιών', 'refresh-list': 'Επαναφόρτωση λίστας εργασιών', 'no-task': 'Δεν υπάρχουν εργασίες αυτή τη στιγμή', 'copy-link': 'Αντιγραφή συνδέσμου', 'copy-link-success': 'Επιτυχής αντιγραφή συνδέσμου', 'remove-record': 'Αφαίρεση αρχείου εργασίας', 'remove-record-confirm': 'Είστε σίγουροι ότι θέλετε να αφαιρέσετε το αρχείο εργασίας για το "{{taskName}}"?', 'remove-record-label': 'Διαγραφή μαζί με τα αρχεία', 'remove-record-success': 'Επιτυχής διαγραφή της εγγραφής της εργασίας "{{taskName}}"', 'remove-record-fail': 'Η διαγραφή της εγγραφής της εργασίας"{{taskName}}" απέτυχε', 'show-in-folder': 'Εμφάνιση της εργασίας στον φάκελο', 'file-not-exist': 'TΤο αρχείο δεν υπάρχει ή έχει διαγραφεί', 'file-path-error': 'Σφάλμα στο μονοπάτι για το αρχείο', 'opening-task-message': 'Άνοιγμα του "{{taskName}}" ...', 'get-task-name': 'Λήψη ονόματος εργασίας...', 'remaining-prefix': 'Απομένει', 'select-torrent': 'Σύρτε το αρχείο torrent εδώ, ή κάντε κλικ για επιλογή', 'task-info-dialog-title': 'Λεπτομέρειες για το {{title}}', 'download-start-message': 'Η λήψη του {{taskName}} ξεκίνησε', 'download-pause-message': 'Η λήψη του {{taskName}} σταμάτησε', 'download-stop-message': 'Η λήψη του {{taskName}} διακόπηκε', 'download-error-message': 'Σφάλμα κατά τη λήψη του {{taskName}}', 'download-complete-message': 'Η λήψη του {{taskName}} ολοκληρώθηκε', 'download-complete-notify': 'Η λήψη ολοκληρώθηκε', 'bt-download-complete-message': 'Η λήψη του {{taskName}} ολοκληρώθηκε, γίνεται διαμοιρασμός', 'bt-download-complete-notify': 'Μία λήψη BT ολοκληρώθηκε, γίνεται διαμοιρασμός...', 'bt-download-complete-tips': 'Συμβουλή: Μπορείτε να διακόψετε μία διεργασία για να σταματήσει ο διαμοιρασμός', 'bt-stopping-seeding-tip': 'Γίνεται διακοπή του διαμοιρασμού, μπορεί να πάρει λίγη ώρα για την αποσύνδεση, παρακαλώ περιμένετε', 'download-fail-message': 'Η λήψη του {{taskName}} απέτυχε', 'download-fail-notify': 'Η λήψη απέτυχε' } ================================================ FILE: src/shared/locales/el/window.js ================================================ export default { 'reload': 'Επαναφόρτωση', 'close': 'Κλείσιμο', 'minimize': 'Ελαχιστοποίηση', 'zoom': 'Ζουμ', 'toggle-fullscreen': 'Είσοδος σε πλήρη οθόνη', 'front': 'Εμφάνιση όλων μπροστά' } ================================================ FILE: src/shared/locales/en-US/about.js ================================================ export default { 'engine-version': 'Engine Version', 'license': 'License', 'about': 'About', 'release': 'Releases', 'support': 'Support' } ================================================ FILE: src/shared/locales/en-US/app.js ================================================ export default { 'task-list': 'Tasks', 'add-task': 'Add Task', 'about': 'About Motrix', 'preferences': 'Preferences...', 'check-for-updates': 'Check for Updates...', 'check-updates-now': 'Check now', 'checking-for-updates': 'Checking for updates ...', 'check-for-updates-title': 'Check for Updates', 'update-available-message': 'A newer version of Motrix is available, update now?', 'update-not-available-message': 'You are up-to-date!', 'update-downloaded-message': 'Ready to install...', 'update-error-message': 'Update Error', 'engine-damaged-message': 'The engine is damaged, please reinstall : (', 'engine-missing-message': 'The engine is missing, please reinstall : (', 'system-error-title': 'System Error', 'system-error-message': 'Application startup failed: {{message}}', 'hide': 'Hide Motrix', 'hide-others': 'Hide Others', 'unhide': 'Show All', 'show': 'Show Motrix', 'quit': 'Quit Motrix', 'under-development-message': 'Sorry, this feature is under development...', 'yes': 'Yes', 'no': 'No', 'save': 'Save', 'reset': 'Discard', 'cancel': 'Cancel', 'submit': 'Submit', 'gt1d': '> 1 day', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/en-US/edit.js ================================================ export default { 'undo': 'Undo', 'redo': 'Redo', 'cut': 'Cut', 'copy': 'Copy', 'paste': 'Paste', 'delete': 'Delete', 'select-all': 'Select All' } ================================================ FILE: src/shared/locales/en-US/help.js ================================================ export default { 'official-website': 'Motrix Website', 'manual': 'Manual', 'release-notes': 'Release Notes...', 'report-problem': 'Report Problem', 'toggle-dev-tools': 'Toggle Developer Tools' } ================================================ FILE: src/shared/locales/en-US/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/en-US/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'File', 'task': 'Task', 'edit': 'Edit', 'window': 'Window', 'help': 'Help' } ================================================ FILE: src/shared/locales/en-US/preferences.js ================================================ export default { 'basic': 'Basic', 'advanced': 'Advanced', 'lab': 'Lab', 'save': 'Save & Apply', 'save-success-message': 'Preferences saved successfully', 'save-fail-message': 'Preferences failed to save', 'discard': 'Discard', 'startup': 'Startup', 'open-at-login': 'Open at login', 'keep-window-state': 'Keep size and position of the window when exited', 'auto-resume-all': 'Automatically resume all unfinished tasks', 'default-dir': 'Default Path', 'mas-default-dir-tips': 'Due to sandbox permission restrictions of the App Store, the default download directory is recommended to be set to ~/Downloads', 'transfer-settings': 'Transmission', 'transfer-speed-upload': 'Upload limit', 'transfer-speed-download': 'Download limit', 'transfer-speed-unlimited': 'Unlimited', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Save magnet link as torrent file', 'bt-auto-download-content': 'Automatically download magnet and torrent content', 'bt-force-encryption': 'BT force encryption', 'keep-seeding': 'Keep seeding until manually stopped', 'seed-ratio': 'Seed Ratio', 'seed-time': 'Seed Time', 'seed-time-unit': 'minutes', 'task-manage': 'Task Management', 'max-concurrent-downloads': 'Maximum active tasks', 'max-connection-per-server': 'Maximum connections per server', 'new-task-show-downloading': 'Automatically show downloading after adding task', 'no-confirm-before-delete-task': 'No confirmation is required before deleting task', 'continue': 'Continue', 'task-completed-notify': 'Notify after download is complete', 'auto-purge-record': 'Automatically purge download records when exiting app', 'ui': 'UI', 'appearance': 'Appearance', 'theme-auto': 'Auto', 'theme-light': 'Light', 'theme-dark': 'Dark', 'auto-hide-window': 'Auto Hide Window', 'run-mode': 'Run As', 'run-mode-standard': 'Standard Application', 'run-mode-tray': 'Tray Application', 'run-mode-hide-tray': 'Hide Tray Application', 'tray-speedometer': 'Show the real-time speed in the menu bar tray', 'show-progress-bar': 'Show download progress bar', 'language': 'Language', 'change-language': 'Change language', 'hide-app-menu': 'Hide App Menu (Windows & Linux Only)', 'proxy': 'Proxy', 'enable-proxy': 'Enable Proxy', 'proxy-bypass-input-tips': 'Bypass proxy settings for these Hosts and Domains, one per line', 'proxy-scope-download': 'Download', 'proxy-scope-update-app': 'Update Application', 'proxy-scope-update-trackers': 'Update Trackers', 'proxy-tips': 'View Proxy Manual', 'bt-tracker': 'Tracker Servers', 'bt-tracker-input-tips': 'Tracker servers, one per line', 'bt-tracker-tips': 'Recommended: ', 'sync-tracker-tips': 'Sync', 'auto-sync-tracker': 'Update tracker list every day automatically', 'port': 'Listen Ports', 'bt-port': 'BT Listen Port', 'dht-port': 'DHT Listen Port', 'security': 'Security', 'rpc': 'RPC', 'rpc-listen-port': 'RPC Listen Port', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'View RPC Secret Manual', 'developer': 'Developer', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Built-in aria2.conf path', 'app-log-path': 'App log path', 'download-session-path': 'Download session path', 'session-reset': 'Reset download session', 'session-reset-confirm': 'Are you sure you want to reset download session?', 'factory-reset': 'Factory Reset', 'factory-reset-confirm': 'Are you sure you want to revert to factory settings?', 'lab-warning': '⚠️ Enabling lab features may result in app crash or data loss, decide at you own risk!', 'download-protocol': 'Protocols', 'protocols-default-client': 'Set as the default client for the following protocols', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Extensions', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Provided by the community, ', 'baidu-exporter-help': 'Click here for usage', 'auto-update': 'Auto Update', 'auto-check-update': 'Automatically check for updates', 'last-check-update-time': 'Last checked for an update', 'not-saved': 'Preferences not saved', 'not-saved-confirm': 'The modified preferences will be lost, are you sure you want to leave?' } ================================================ FILE: src/shared/locales/en-US/subnav.js ================================================ export default { 'task-list': 'Tasks', 'preferences': 'Preferences' } ================================================ FILE: src/shared/locales/en-US/task.js ================================================ export default { 'active': 'Downloading', 'waiting': 'Waiting', 'stopped': 'Stopped', 'new-task': 'New Task', 'new-bt-task': 'New BT Task', 'open-file': 'Open Torrent File...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'One task url per line (supports magnet)', 'thunder-link-tips': 'Tip: Thunder links may not be downloadable after decoding', 'new-task-uris-required': 'Please enter at least one valid resource url', 'new-task-torrent-required': 'Please select a torrent file', 'file-name': 'Name', 'file-extension': 'Extension', 'file-size': 'Size', 'file-completed-size': 'Completed', 'selected-files-sum': 'Selected: {{selectedFilesCount}} files, total size {{selectedFilesTotalSize}}', 'select-at-least-one': 'Please select at least one file', 'task-gid': 'GID', 'task-name': 'Task Name', 'task-out': 'Rename', 'task-out-tips': 'Optional', 'task-split': 'Splits', 'task-dir': 'Save to', 'pause-task': 'Pause Task', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Authorization', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Error', 'task-piece': 'Piece', 'task-piece-length': 'Piece Size', 'task-num-pieces': 'Pieces', 'task-bittorrent-info': 'Torrent Info', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Creation Date', 'task-bittorrent-comment': 'Comment', 'task-progress-info': 'Progress', 'task-status': 'Status', 'task-num-seeders': 'Seeders', 'task-connections': 'Connections', 'task-file-size': 'Size', 'task-download-speed': 'Download Speed', 'task-upload-speed': 'Upload Speed', 'task-download-length': 'Downloaded', 'task-upload-length': 'Uploaded', 'task-ratio': 'Ratio', 'task-peer-host': 'Host', 'task-peer-ip': 'IP', 'task-peer-client': 'Client', 'navigate-to-downloading': 'Navigate to Downloading', 'show-advanced-options': 'Advanced Options', 'copyright-warning': 'Copyright Warning', 'copyright-warning-message': 'The file you want to download may be copyrighted audio or video, please ensure that you have permission to access to it.', 'copyright-yes': 'Yes, I have permission', 'copyright-no': 'No, I don\'t have permission', 'copyright-error-message': 'Failed to add task due to copyright issue', 'pause-task-success': 'Successfully paused task "{{taskName}}"', 'pause-task-fail': 'Failed to pause task "{{taskName}}"', 'resume-task': 'Resume Task', 'resume-task-success': 'Successfully resumed task "{{taskName}}"', 'resume-task-fail': 'Failed to resume task "{{taskName}}"', 'delete-task': 'Delete Task', 'delete-selected-tasks': 'Delete Selected Tasks', 'delete-task-confirm': 'Are you sure you want to remove download task "{{taskName}}"?', 'batch-delete-task-confirm': 'Are you sure you want to remove {{count}} download tasks in batch?', 'delete-task-label': 'Delete with Files', 'delete-task-success': 'Successfully deleted task "{{taskName}}"', 'delete-task-fail': 'Failed to delete task "{{taskName}}"', 'remove-task-file-fail': 'Failed to delete task file(s), please delete them manually', 'remove-task-config-file-fail': 'Failed to delete task config file, please delete it manually', 'move-task-up': 'Move Task Up', 'move-task-down': 'Move Task Down', 'pause-all-task': 'Pause All Tasks', 'pause-all-task-success': 'Successfully paused all tasks', 'pause-all-task-fail': 'Failed to pause all tasks', 'resume-all-task': 'Resume All Tasks', 'resume-all-task-success': 'Successfully resumed all tasks', 'resume-all-task-fail': 'Failed to resume all tasks', 'select-all-task': 'Select All Tasks', 'clear-recent-tasks': 'Clear Recent Tasks', 'purge-record': 'Purge Task Record', 'purge-record-success': 'Successfully purged task records', 'purge-record-fail': 'Failed to purge task records', 'batch-delete-task-success': 'Successfully delete tasks in batch', 'batch-delete-task-fail': 'Failed to delete tasks in batch', 'refresh-list': 'Refresh Task List', 'no-task': 'There are no current tasks', 'copy-link': 'Copy Link', 'copy-link-success': 'Successfully copied link', 'remove-record': 'Remove Task Record', 'remove-record-confirm': 'Are you sure you want to remove download record for "{{taskName}}"?', 'remove-record-label': 'Delete with Files', 'remove-record-success': 'Successfully removed task record for "{{taskName}}"', 'remove-record-fail': 'Failed to remove task record for "{{taskName}}"', 'show-in-folder': 'Show Task In Folder', 'file-not-exist': 'Target file does not exist or has been deleted', 'file-path-error': 'File path error', 'opening-task-message': 'Opening "{{taskName}}" ...', 'get-task-name': 'Getting task name...', 'remaining-prefix': 'Remaining', 'select-torrent': 'Drag torrent file here, or click to select', 'task-detail-title': 'Task Details', 'task-info-dialog-title': '{{title}} Details', 'download-start-message': 'Started downloading {{taskName}}', 'download-pause-message': 'Paused downloading {{taskName}}', 'download-stop-message': 'Stopped downloading {{taskName}}', 'download-error-message': 'Error occurred when downloading {{taskName}}', 'download-complete-message': 'Completed downloading {{taskName}}', 'download-complete-notify': 'Download Completed', 'bt-download-complete-message': 'Completed downloading {{taskName}}, seeding', 'bt-download-complete-notify': 'BT Download Completed, seeding...', 'bt-download-complete-tips': 'Tips: You can stop a task to end its seeding', 'bt-stopping-seeding-tip': 'Stopping seeding, it will take some time to disconnect, please wait patiently', 'download-fail-message': 'Failed to download {{taskName}}', 'download-fail-notify': 'Download Failed' } ================================================ FILE: src/shared/locales/en-US/window.js ================================================ export default { 'reload': 'Reload', 'close': 'Close', 'minimize': 'Minimize', 'zoom': 'Zoom', 'toggle-fullscreen': 'Enter Full Screen', 'front': 'Bring All to Front' } ================================================ FILE: src/shared/locales/es/about.js ================================================ export default { 'engine-version': 'Versión del motor', 'license': 'Licencia', 'about': 'Acerca de', 'release': 'Versión', 'support': 'Soporte' } ================================================ FILE: src/shared/locales/es/app.js ================================================ export default { 'task-list': 'Tareas', 'add-task': 'Añadir tareas', 'about': 'Acerca de Motrix', 'preferences': 'Preferencias...', 'check-for-updates': 'Comprobar actualizaciones...', 'check-updates-now': 'Comprobar ahora', 'checking-for-updates': 'Comprobando actualizaciones...', 'check-for-updates-title': 'Comprobar actualizaciones', 'update-available-message': 'Hay una nueva versión de Motrix, ¿Actualizar ahora?', 'update-not-available-message': '¡Esta es la última versión!', 'update-downloaded-message': 'Listo para instalar...', 'update-error-message': 'Error al actualizar', 'engine-damaged-message': 'El motor está dañado, por favor reinstalar :(', 'engine-missing-message': 'No se encuentra el motor, por favor reinstalar :(', 'system-error-title': 'Error del sistema', 'system-error-message': 'La aplicación falló al iniciar: {{message}}', 'hide': 'Ocultar Motrix', 'hide-others': 'Ocultar otros', 'unhide': 'Mostrar Todo', 'show': 'Mostrar Motrix', 'quit': 'Salir de Motrix', 'under-development-message': 'Disculpa, esta función está en desarrollo...', 'yes': 'Sí', 'no': 'No', 'save': 'Guardar', 'reset': 'Reiniciar', 'cancel': 'Cancelar', 'submit': 'Enviar', 'gt1d': '> 1 día', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/es/edit.js ================================================ export default { 'undo': 'Deshacer', 'redo': 'Rehacer', 'cut': 'Cortar', 'copy': 'Copiar', 'paste': 'Pegar', 'delete': 'Eliminar', 'select-all': 'Seleccionar Todo' } ================================================ FILE: src/shared/locales/es/help.js ================================================ export default { 'official-website': 'Sitio web de Motrix', 'manual': 'Manual', 'release-notes': 'Notas de la versión...', 'report-problem': 'Reportar un problema', 'toggle-dev-tools': 'Alternar las herramientas de desarrollo' } ================================================ FILE: src/shared/locales/es/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/es/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Archivo', 'task': 'Tarea', 'edit': 'Editar', 'window': 'Ventana', 'help': 'Ayuda' } ================================================ FILE: src/shared/locales/es/preferences.js ================================================ export default { 'basic': 'Básico', 'advanced': 'Avanzado', 'lab': 'Lab', 'save': 'Guardar y Aplicar', 'save-success-message': 'Preferencias guardadas exitosamente', 'save-fail-message': 'Hubo un error al guardar tus preferencias', 'discard': 'Descartar', 'startup': 'Inicio', 'open-at-login': 'Abrir al iniciar sesión', 'keep-window-state': 'Mantener el tamaño y la posición de la ventana al salir', 'auto-resume-all': 'Reanudar automáticamente todas las tareas sin finalizar', 'default-dir': 'Ruta por defecto', 'mas-default-dir-tips': 'Debido a las restricciones de la tienda de aplicaciones, la ruta por defecto se recomienda que sea ~/Downloads', 'transfer-settings': 'Transferencia', 'transfer-speed-upload': 'Límite de subida', 'transfer-speed-download': 'Límite de bajada', 'transfer-speed-unlimited': 'Ilimitado', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Guardar enlace magnet como archivo torrent', 'bt-auto-download-content': 'Descargar automáticamente el contenido de magnet y torrent', 'bt-force-encryption': 'Forzar encriptación BT', 'keep-seeding': 'Siga sembrando hasta detenerlo manualmente', 'seed-ratio': 'Proporción de semillas', 'seed-time': 'Tiempo de semilla', 'seed-time-unit': 'minutos', 'task-manage': 'Gestión de tareas', 'max-concurrent-downloads': 'Tareas máximas activas', 'max-connection-per-server': 'Conexiones máximas por servidor', 'new-task-show-downloading': 'Mostrar automáticamente la descarga después de añadir una tarea', 'no-confirm-before-delete-task': 'No se requiere confirmación antes de eliminar la tarea', 'continue': 'Continuar', 'task-completed-notify': 'Notificar después de que la descarga se complete', 'auto-purge-record': 'Eliminar automáticamente el registro de descargas al salir', 'ui': 'UI', 'appearance': 'Apariencia', 'theme-auto': 'Auto', 'theme-light': 'Claro', 'theme-dark': 'Oscuro', 'auto-hide-window': 'Ocultar automáticamente ventanas', 'run-mode': 'Correr como', 'run-mode-standard': 'Aplicación estándar', 'run-mode-tray': 'Aplicación de bandeja', 'run-mode-hide-tray': 'Ocultar aplicación de bandeja', 'tray-speedometer': 'La bandeja de la barra de menú muestra la velocidad en tiempo real', 'show-progress-bar': 'Mostrar la barra de progreso de descarga', 'language': 'Idioma', 'change-language': 'Cambiar Idioma', 'hide-app-menu': 'Ocultar el menú (Solo Windows y Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Activar Proxy', 'proxy-bypass-input-tips': 'Omitir la configuración del proxy para estos hosts y dominios, uno por línea', 'proxy-scope-download': 'Descargar', 'proxy-scope-update-app': 'Actualizar aplicación', 'proxy-scope-update-trackers': 'Actualizar Trackers', 'proxy-tips': 'Ver manual para Proxy', 'bt-tracker': 'Servidores de rastreadores', 'bt-tracker-input-tips': 'Servidores de rastreadores, uno por línea', 'bt-tracker-tips': 'Recomendado: ', 'sync-tracker-tips': 'Sincronizar', 'auto-sync-tracker': 'Actualice la lista de rastreadores todos los días automáticamente', 'port': 'Puertos de Escucha', 'bt-port': 'Puerto de escucha BT', 'dht-port': 'Puerto de escucha DHT', 'security': 'Seguridad', 'rpc': 'RPC', 'rpc-listen-port': 'Puerto de Escucha RPC', 'rpc-secret': 'Clave RPC', 'rpc-secret-tips': 'Ver manual de la clave RPC', 'developer': 'Desarrollador', 'user-agent': 'User-Agent', 'mock-user-agent': 'Falsear Agente de Usuario', 'aria2-conf-path': 'Ruta incorporada de aria2.conf', 'app-log-path': 'Ruta del registro', 'download-session-path': 'Ruta de descarga de la sesión', 'factory-reset': 'Restauración de fábrica', 'factory-reset-confirm': '¿Estás seguro que quieres restaurar de fábrica?', 'lab-warning': '¡Habilitar las característticas "Lab" puede resultar en errores y pérdida de datos!', 'download-protocol': 'Protocolos', 'protocols-default-client': 'Establecer como cliente por defecto de los siguientes protocolos', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Extensiones', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Proporcionados por la comunidad, ', 'baidu-exporter-help': 'Presiona aqui para ver el uso', 'auto-update': 'Auto-actualizar', 'auto-check-update': 'Revisar automáticamente por actualizaciones', 'last-check-update-time': 'Última revisión de actualizaciones', 'not-saved': 'Preferencias no guardadas', 'not-saved-confirm': 'Las preferencias cambiadas se perderán, ¿está seguro de irse?' } ================================================ FILE: src/shared/locales/es/subnav.js ================================================ export default { 'task-list': 'Tareas', 'preferences': 'Preferencias' } ================================================ FILE: src/shared/locales/es/task.js ================================================ export default { 'active': 'Descargando', 'waiting': 'Esperando', 'stopped': 'Detenida', 'new-task': 'Nueva Tarea', 'new-bt-task': 'Nueva Tarea BT', 'open-file': 'Abrir archivo de Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Una url de tarea por línea (soporta magnet)', 'thunder-link-tips': 'Tip: Es posible que los enlaces Thunder no se puedan descargar después de la decodificación.', 'new-task-uris-required': 'Por favor, introduzca al menos una url de recurso válida', 'new-task-torrent-required': 'Seleccione un archivo torrent', 'file-name': 'Nombre', 'file-extension': 'Extensión', 'file-size': 'Tamaño', 'file-completed-size': 'Terminado', 'selected-files-sum': 'Seleccionado: {{selectedFilesCount}} archivos, tamaño total {{selectedFilesTotalSize}}', 'select-at-least-one': 'Seleccione al menos un archivo', 'task-gid': 'GID', 'task-name': 'Nombre de la tarea', 'task-out': 'Renombrar', 'task-out-tips': 'Opcional', 'task-split': 'Dividir', 'task-dir': 'Guardar en', 'pause-task': 'Pausar Tarea', 'task-ua': 'UA', 'task-user-agent': 'Agente de Usuario', 'task-authorization': 'Autorización', 'task-referer': 'Referente', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Error', 'task-piece': 'Trozo', 'task-piece-length': 'Tamaño de pieza', 'task-num-pieces': 'Piezas', 'task-bittorrent-info': 'Información de Torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Fecha de creación', 'task-bittorrent-comment': 'Comentario', 'task-progress-info': 'Progreso', 'task-status': 'Estado', 'task-num-seeders': 'Semillas', 'task-connections': 'Conexiones', 'task-file-size': 'Tamaño', 'task-download-speed': 'Velocidad de Descarga', 'task-upload-speed': 'Velocidad de subida', 'task-download-length': 'Descargado', 'task-upload-length': 'Subido', 'task-ratio': 'Proporción', 'task-peer-host': 'Anfitrión', 'task-peer-ip': 'IP', 'task-peer-client': 'Cliente', 'navigate-to-downloading': 'Navegar a Descargas', 'show-advanced-options': 'Opciones avanzadas', 'copyright-warning': 'Advertencia sobre derechos de autor', 'copyright-warning-message': 'El archivo que desea descargar puede tener derechos de autor de audio o video, por favor asegúrese de que tiene permiso para acceder a él.', 'copyright-yes': 'Sí, tengo permiso', 'copyright-no': 'No, no tengo permiso.', 'copyright-error-message': 'No se pudo agregar una tarea debido a un problema de derechos de autor', 'pause-task-success': 'Se ha pausado la tarea "{{taskName}}"', 'pause-task-fail': 'Hubo un fallo al pausar la tarea "{{taskName}}"', 'resume-task': 'Resumir tarea', 'resume-task-success': 'Se ha reanudado la tarea "{{taskName}}"', 'resume-task-fail': 'Hubo un error al resumir la tarea "{{taskName}}"', 'delete-task': 'Eliminar tarea', 'delete-selected-tasks': 'Eliminar tareas seleccionadas', 'delete-task-confirm': '¿Está seguro que desea eliminar la tarea "{{taskName}}"?', 'batch-delete-task-confirm': '¿Está seguro de que desea eliminar {{count}} tareas de descarga en lote?', 'delete-task-label': 'Eliminar con archivos', 'delete-task-success': 'Tarea eliminada exitosamente "{{taskName}}"', 'delete-task-fail': 'Hubo un error al eliminar la tarea "{{taskName}}"', 'remove-task-file-fail': 'No se han eliminado los archivos de tareas, por favor, elimínelos manualmente.', 'remove-task-config-file-fail': 'No se ha podido eliminar el archivo de configuración de la tarea, por favor, elimínelo manualmente.', 'move-task-up': 'Desplazar tarea hacia arriba', 'move-task-down': 'Desplazar tarea hacia abajo', 'pause-all-task': 'Pausar todas las tareas', 'pause-all-task-success': 'Se pausaron todas las tareas exitosamente', 'pause-all-task-fail': 'Hubo un error al pausar todas las tareas', 'resume-all-task': 'Reanudar todas las tareas', 'resume-all-task-success': 'Se reanudaron exitosamente todas las tareas', 'resume-all-task-fail': 'Hubo un error al reanudar todas las tareas', 'select-all-task': 'Seleccionar toda la tarea', 'clear-recent-tasks': 'Limpiar tareas recientes', 'purge-record': 'Registros de tareas purgados con éxito', 'purge-record-success': 'Se eliminaron los registros de tareas de manera exitosa', 'purge-record-fail': 'No se pudieron eliminar los registros de tareas', 'batch-delete-task-success': 'Eliminar con éxito las tareas en lote', 'batch-delete-task-fail': 'Error al eliminar tareas en lote', 'refresh-list': 'Actualizar lista de tareas', 'no-task': 'No hay tareas actuales', 'copy-link': 'Copiar enlace', 'copy-link-success': 'Enlace copiado exitosamente', 'remove-record': 'Eliminar tarea', 'remove-record-confirm': '¿Está seguro que desea eliminar la tarea "{{taskName}}"?', 'remove-record-label': 'Eliminar con archivos', 'remove-record-success': 'Se ha removido exitosamente la tarea "{{taskName}}"', 'remove-record-fail': 'Hubo un fallo al eliminar la tarea "{{taskName}}"', 'show-in-folder': 'Mostrar la carpeta de la tarea', 'file-not-exist': 'El archivo objetivo no existe o ha sido eliminado', 'file-path-error': 'Error en la ruta del archivo', 'opening-task-message': 'Abriendo "{{taskName}}"...', 'get-task-name': 'Obteniendo el nombre de la tarea...', 'remaining-prefix': 'Faltante', 'select-torrent': 'Arrastrar un archivo Torrent aquí o clic para seleccionar', 'task-info-dialog-title': 'Detalles de {{title}}', 'download-start-message': 'Se inicio la descarga de {{taskName}}', 'download-pause-message': 'Se pausó la descarga de {{taskName}}', 'download-stop-message': 'Se detuvo la descarga de {{taskName}}', 'download-error-message': 'Ha ocurrido un error al descargar {{taskName}}', 'download-complete-message': 'Se termino de descargar {{taskName}}', 'download-complete-notify': 'Descarga completada', 'bt-download-complete-message': 'Descarga completada {{taskName}}, compartiendo', 'bt-download-complete-notify': 'Descarga BT completa, compartiendo...', 'bt-download-complete-tips': 'Consejo: Puede detener una tarea para dejar de compartir', 'bt-stopping-seeding-tip': 'Deteniendo la siembra, tomará un tiempo desconectarse, por favor espere...', 'download-fail-message': 'No se pudo descargar {{taskName}}', 'download-fail-notify': 'Descarga fallida' } ================================================ FILE: src/shared/locales/es/window.js ================================================ export default { 'reload': 'Recargar', 'close': 'Cerrar', 'minimize': 'Minimizar', 'zoom': 'Zoom', 'toggle-fullscreen': 'Entrar a Pantalla Completa', 'front': 'Pasar Todo al frente' } ================================================ FILE: src/shared/locales/fa/about.js ================================================ export default { 'engine-version': 'نسخهٔ موتور', 'license': 'مجوز', 'about': 'دربارهٔ ما', 'release': 'نسخه‌های منتشر شده', 'support': 'حمایت' } ================================================ FILE: src/shared/locales/fa/app.js ================================================ export default { 'task-list': 'وظایف', 'add-task': 'افزودن وظیفه', 'about': 'دربارهٔ ماتریکس', 'preferences': 'ترجیحات...', 'check-for-updates': 'بررسی برای به‌روز رسانی...', 'check-updates-now': 'اکنون بررسی کن', 'checking-for-updates': 'درحال بررسی برای به‌روز رسانی...', 'check-for-updates-title': 'بررسی برای به‌روز رسانی', 'update-available-message': 'نسخهٔ جدید ماتریکس در دسترس است. به‌روز رسانی شود؟', 'update-not-available-message': 'شما به‌روز هستید!', 'update-downloaded-message': 'آمادهٔ نصب...', 'update-error-message': 'خطا در به‌روز رسانی', 'engine-damaged-message': 'موتور صدمه دیده است! لطفا دوباره نصب کنید : (', 'engine-missing-message': 'موتور گم شده است! لطفا دوباره نصب کنید : (', 'system-error-title': 'خطای سیستم', 'system-error-message': 'برنامه نتوانست شروع به کار کند: {{message}}', 'hide': 'ماتریکس را پنهان کن', 'hide-others': 'بقیه را پنهان کن', 'unhide': 'همه را نشان بده', 'show': 'ماتریکس را نشان بده', 'quit': 'از ماتریکس خارج شو', 'under-development-message': 'متأسفیم، این قابلیت در حال توسعه است...', 'yes': 'بله', 'no': 'خیر', 'save': 'ذخیره', 'reset': 'دور انداختن', 'cancel': 'لغو', 'submit': 'ثبت', 'gt1d': '> یک روز', 'hour': 'ساعت', 'minute': 'دقیقه', 'second': 'ثانیه' } ================================================ FILE: src/shared/locales/fa/edit.js ================================================ export default { 'undo': 'برگردان', 'redo': 'انجام دوباره', 'cut': 'برش', 'copy': 'رونوشت', 'paste': 'جای‌گذاری', 'delete': 'حذف', 'select-all': 'گزینش همه' } ================================================ FILE: src/shared/locales/fa/help.js ================================================ export default { 'official-website': 'سایت ماتریکس', 'manual': 'کتابچهٔ راهنما', 'release-notes': 'یادداشت‌های انتشار...', 'report-problem': 'گزارش مشکل', 'toggle-dev-tools': 'تغییر حالت ابزارهای توسعه‌دهنده' } ================================================ FILE: src/shared/locales/fa/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/fa/menu.js ================================================ export default { 'app': 'ماتریکس', 'file': 'پرونده', 'task': 'وظیفه', 'edit': 'ویرایش', 'window': 'پنجره', 'help': 'راهنما' } ================================================ FILE: src/shared/locales/fa/preferences.js ================================================ export default { 'basic': 'پایه', 'advanced': 'پیشرفته', 'lab': 'آزمایشگاه', 'save': 'ذخیره و اعمال', 'save-success-message': 'ترجیحات با موفقیت ذخیره شد', 'save-fail-message': 'ذخیرهٔ ترجیحات شکست خورد', 'discard': 'دور انداختن', 'startup': 'شروع برنامه', 'open-at-login': 'گشودن هنگام ورود به سیستم', 'keep-window-state': 'نگه‌داشتن اندازه و موقعیت پنجره هنگام خروج', 'auto-resume-all': 'از سرگیری خودکار همه وظایف ناتمام', 'default-dir': 'مسیر پیش‌فرض', 'mas-default-dir-tips': 'بخاطر محدودیت‌های دسترسی سندباکس از اپ استور، پیشنهاد می‌شود شاخه پیش‌فرض دانلود بر روی ~/Downloads تنظیم شده باشد.', 'transfer-settings': 'انتقال', 'transfer-speed-upload': 'محدودیت بارگذاری', 'transfer-speed-download': 'محدودیت بارگیری', 'transfer-speed-unlimited': 'نامحدود', 'bt-settings': 'بیت‌تورنت', 'bt-save-metadata': 'ذخیره پیوند مگنت به عنوان پرونده تورنت', 'bt-auto-download-content': 'بارگیری خودکار مگنت و محتوای تورنت', 'bt-force-encryption': 'رمزگذاری اجباری ب‌ت', 'keep-seeding': 'بذرپاشی را تا هنگامی که به صورت دستی قطع نشده، ادامه بده', 'seed-ratio': 'نسبت دانه', 'seed-time': 'زمان دانه', 'seed-time-unit': 'دقیقه', 'task-manage': 'مدیریت وظایف', 'max-concurrent-downloads': 'بیشینهٔ وظایف فعال', 'max-connection-per-server': 'بیشینهٔ اتصال برای هر سرور', 'new-task-show-downloading': 'بعد افزودن وظیفه به صورت خودکار بارگیری را نشان بده', 'no-confirm-before-delete-task': 'قبل از حذف وظیفه نیازی به تأیید نیست', 'continue': 'ادامه', 'task-completed-notify': 'پس از تکمیل بارگیری اطلاع رسانی کن', 'auto-purge-record': 'به صورت خودکار سابقهٔ بارگیری را هنگام خروج پاک کن', 'ui': 'رابط کاربری', 'appearance': 'ظاهر', 'theme-auto': 'خودکار', 'theme-light': 'روشن', 'theme-dark': 'تاریک', 'auto-hide-window': 'پنهان کردن خودکار پنجره', 'run-mode': 'اجرا به عنوان', 'run-mode-standard': 'برنامه استاندارد', 'run-mode-tray': 'برنامه سینی', 'run-mode-hide-tray': 'مخفی کردن برنامه سینی', 'tray-speedometer': 'سینی نوار منو سرعت زمان واقعی را نشان می‌دهد', 'show-progress-bar': 'نمایش نوار پیشرفت دانلود', 'language': 'زبان', 'change-language': 'تغییر زبان', 'hide-app-menu': 'پنهان کردن منوی برنامه (تنها در ویندوز و لینوکس)', 'proxy': 'پیشکار', 'enable-proxy': 'فعال کردن پیشکار', 'proxy-bypass-input-tips': 'تنظیمات پیشکار را برای این میزبان‌ها و دامنه‌ها استفاده نکن، یکی در هر خط', 'proxy-scope-download': 'دانلود', 'proxy-scope-update-app': 'به‌روزرسانی برنامه', 'proxy-scope-update-trackers': 'به‌روزرسانی پیگیر‌ها', 'proxy-tips': 'مشاهده راهنمای پیشکار', 'bt-tracker': 'سرورهای ردیاب', 'bt-tracker-input-tips': 'سرورهای ردیاب، یکی در هر خط', 'bt-tracker-tips': 'توصیه شده: ', 'sync-tracker-tips': 'همگام‌سازی', 'auto-sync-tracker': 'لیست ردیاب را هر روز به طور خودکار به‌روز کنید', 'port': 'درگاه', 'bt-port': 'درگاه BT', 'dht-port': 'درگاه DHT', 'security': 'امنیت', 'rpc': 'RPC', 'rpc-listen-port': 'پورت گوش دادن به RPC', 'rpc-secret': 'رمز RPC', 'rpc-secret-tips': 'مشاهده راهنمای رمز RPC', 'developer': 'توسعه‌دهنده', 'user-agent': 'User-Agent', 'mock-user-agent': 'جعل عامل کاربر', 'aria2-conf-path': 'مسیر aria2.conf درونی', 'app-log-path': 'مسیر گزارش برنامه', 'download-session-path': 'مسیر نشست بارگیری', 'session-reset': 'بازنشانی نشست بارگیری', 'session-reset-confirm': 'آیا برای بازنشانی نشست بارگیری مطمئنید؟', 'factory-reset': 'بازنشانی به تنظیمات کارخانه', 'factory-reset-confirm': 'آیا برای بازنشانی تنظیمات کارخانه مطمئنید؟', 'lab-warning': '⚠️ فعال سازی قابلیت آزمایشگاه ممکن است باعث خرابی برنامه یا از دست دادن داده شود، با مسئولیت خود تصمیم بگیرید!', 'download-protocol': 'شیوه‌نامه', 'protocols-default-client': 'تنظیم به عنوان کارخواه پیش‌گزیده برای شیوه‌نامه‌های زیر', 'protocols-magnet': 'آهن‌ربا [ magnet:// ]', 'protocols-thunder': 'تندر [ thunder:// ]', 'browser-extensions': 'افزونه‌ها', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'تهیه شده توسط انجمن، ', 'baidu-exporter-help': 'برای استفاده اینجا کلیک کنید', 'auto-update': 'به‌روز رسانی خودکار', 'auto-check-update': 'به صورت خودکار برای به‌روز رسانی بررسی کن', 'last-check-update-time': 'آخرین زمان بررسی برای به‌روز رسانی', 'not-saved': 'تنظیمات برگزیده ذخیره نشد', 'not-saved-confirm': 'تنظیمات برگزیده تغییر یافته از بین خواهند رفت، آیا مطمئن هستید که می روید؟' } ================================================ FILE: src/shared/locales/fa/subnav.js ================================================ export default { 'task-list': 'وظایف', 'preferences': 'ترجیحات' } ================================================ FILE: src/shared/locales/fa/task.js ================================================ export default { 'active': 'در حال بارگیری', 'waiting': 'در انتظار', 'stopped': 'متوقف شد', 'new-task': 'وظیفهٔ جدید', 'new-bt-task': 'وظیفهٔ بیت‌تورنت جدید', 'open-file': 'بازکردن پروندهٔ تورنت...', 'uri-task': 'آدرس اینترنتی', 'torrent-task': 'تورنت', 'uri-task-tips': 'یک آدرس در هر خط(از مگنت پشتیبانی می‌شود)', 'thunder-link-tips': 'نکته: پیوند‌های تاندر ممکن هست بعد کدگشایی قابل بارگیری نباشند', 'new-task-uris-required': 'لطفاً حداقل یک آدرس معتبر وارد کنید', 'new-task-torrent-required': 'لطفاً یک پروندهٔ تورنت را انتخاب کنید', 'file-name': 'نام', 'file-extension': 'پسوند', 'file-size': 'اندازه', 'file-completed-size': 'تکمیل شده', 'selected-files-sum': 'گزیده: {{selectedFilesCount}} پرونده، اندازه کل {{selectedFilesTotalSize}}', 'select-at-least-one': 'لطفاً حداقل یک پرونده را انتخاب کنید', 'task-gid': 'GID', 'task-name': 'نام وظیفه', 'task-out': 'تغییرنام', 'task-out-tips': 'اختیاری', 'task-split': 'برش‌ها', 'task-dir': 'ذخیره در', 'pause-task': 'مکث', 'task-ua': 'UA', 'task-user-agent': 'عامل کاربر', 'task-authorization': 'مجوز', 'task-referer': 'ارجاع دهنده', 'task-cookie': 'کوکی', 'task-proxy': 'پیشکار', 'task-error-info': 'خطا', 'task-piece': 'قطعه', 'task-piece-length': 'اندازه قطعه', 'task-num-pieces': 'قطعات', 'task-bittorrent-info': 'اطلاعات تورنت', 'task-info-hash': 'هش', 'task-bittorrent-creation-date': 'تاریخ ایجاد', 'task-bittorrent-comment': 'توضیح', 'task-progress-info': 'پیشرفت', 'task-status': 'وضعیت', 'task-num-seeders': 'بذرپاش', 'task-connections': 'اتصالات', 'task-file-size': 'اندازه', 'task-download-speed': 'سرعت بارگیری', 'task-upload-speed': 'سرعت بارگذاری', 'task-download-length': 'بارگیری شد', 'task-upload-length': 'بارگذاری شد', 'task-ratio': 'نسبت', 'task-peer-host': 'میزبان', 'task-peer-ip': 'آی‌پی', 'task-peer-client': 'کارخواه', 'navigate-to-downloading': 'برو به دانلودها', 'show-advanced-options': 'گزینه‌های پیشرفته', 'copyright-warning': 'هشدار حق تکثیر', 'copyright-warning-message': 'پرونده‌ای که می‌خواهید بارگیری کنید ممکن است صوت یا ویدیوی تحت حمایت قانون حق تکثیر باشد، مطمئن شوید که اجازه دسترسی به آن را دارید.', 'copyright-yes': 'بله، من اجازه دارم', 'copyright-no': 'خیر، من اجازه ندارم', 'copyright-error-message': 'به دلیل حق تکثیر، افزودن وظیفه انجام نشد', 'pause-task-success': 'توقف وظیفه "{{taskName}}" با موفقیت انجام شد', 'pause-task-fail': 'توقف وظیفه "{{taskName}}" شکست خورد', 'resume-task': 'از سرگیری وظیفه', 'resume-task-success': 'از سرگیری وظیفه "{{taskName}}" با موفقیت انجام شد', 'resume-task-fail': 'از سرگیری وظیفه "{{taskName}}" شکست خورد', 'delete-task': 'حذف وظیفه', 'delete-selected-tasks': 'وظایف گزیده را حذف کن', 'delete-task-confirm': 'آیا از حذف وظیفه "{{taskName}}" مطمئنید؟', 'batch-delete-task-confirm': 'آیا از حذف {{count}} وظیفه به صورت دسته جمعی مطمئنید؟', 'delete-task-label': 'حذف به همراه پرونده‌ها', 'delete-task-success': 'حذف وظیفه "{{taskName}}" با موفقیت انجام شد', 'delete-task-fail': 'حذف وظیفه "{{taskName}}" شکست خورد', 'remove-task-file-fail': 'حذف وظیفه(ها) شکست خورد، لطفاً به صورت دستی حذفشان کنید', 'remove-task-config-file-fail': 'حذف پروندهٔ پیکربندی وظیفه شکست خورد، لطفاً به صورت دستی حذفشان کنید', 'move-task-up': 'جابجایی وظیفه به بالا', 'move-task-down': 'جابجایی وظیفه به پایین', 'pause-all-task': 'توقف همهٔ وظایف', 'pause-all-task-success': 'توقف همهٔ وظایف با موفقیت انجام شد', 'pause-all-task-fail': 'توقف همهٔ وظایف شکست خورد', 'resume-all-task': 'از سرگیری همهٔ وظایف', 'resume-all-task-success': 'از سرگیری همهٔ وظایف با موفقیت انجام شد', 'resume-all-task-fail': 'از سرگیری همهٔ وظایف شکست خورد', 'select-all-task': 'انتخاب همه وظایف', 'clear-recent-tasks': 'پاک کردن وظایف اخیر', 'purge-record': 'پاک کردن سابقهٔ وظایف', 'purge-record-success': 'پاک کردن سابقهٔ وظایف با موفقیت انجام شد', 'purge-record-fail': 'پاک کردن سابقهٔ وظایف شکست خورد', 'batch-delete-task-success': 'حذف دسته جمعی وظایف با موفقیت انجام شد', 'batch-delete-task-fail': 'حذف دسته جمعی وظایف شکست خورد', 'refresh-list': 'تازه کردن لیست وظایف', 'no-task': 'فعلا وظیفه‌ای وجود ندارد', 'copy-link': 'رونوشت از پیوند', 'copy-link-success': 'رونوشت از پیوند با موفقیت انجام شد', 'remove-record': 'پاک کردن سابقهٔ وظیفه', 'remove-record-confirm': 'آیا از حذف سابقه "{{taskName}}" مطمئنید؟', 'remove-record-label': 'حذف به همراه پرونده‌ها', 'remove-record-success': 'پاک کردن سابقهٔ وظیفه "{{taskName}}" با موفقیت انجام شد', 'remove-record-fail': 'پاک کردن سابقهٔ وظیفه "{{taskName}}" شکست خورد', 'show-in-folder': 'نمایش وظیفه در پوشه', 'file-not-exist': 'پروندهٔ هدف وجود ندارد یا حذف شده است', 'file-path-error': 'خطای مسیر پرونده', 'opening-task-message': 'گشودن "{{taskName}}" ...', 'get-task-name': 'گرفتن نام وظیفه...', 'remaining-prefix': 'باقیمانده', 'select-torrent': 'پروندهٔ تورنت را اینجا بکشید، یا برای گزینش کلیک کنید', 'task-info-dialog-title': '{{title}} جزئیات', 'download-start-message': 'بارگیری {{taskName}} شروع شد', 'download-pause-message': 'مکث در بارگیری {{taskName}}', 'download-stop-message': 'بارگیری {{taskName}} متوقف شد', 'download-error-message': 'هنگام بارگیری {{taskName}} خطای رخ داد', 'download-complete-message': 'بارگیری {{taskName}} تکمیل شد', 'download-complete-notify': 'بارگیری تکمیل شد', 'bt-download-complete-message': 'بارگیری {{taskName}} تکمیل شد، بذرپاشی', 'bt-download-complete-notify': 'بارگیری بیت‌تورنت تکمیل شد، بذرپاشی...', 'bt-download-complete-tips': 'نکات: شما می‌توانید با توقف یک وظیفه، به بذرپاشی آن را پایان دهید', 'bt-stopping-seeding-tip': 'متوقف کردن بذرپاشی، قطع ارتباط مدتی طول خواهد کشید، لطفاً صبر کنید ...', 'download-fail-message': 'بارگیری {{taskName}} شکست خورد', 'download-fail-notify': 'بارگیری شکست خورد' } ================================================ FILE: src/shared/locales/fa/window.js ================================================ export default { 'reload': 'بارگزاری مجدد', 'close': 'بستن', 'minimize': 'کمینه', 'zoom': 'بزرگنمایی', 'toggle-fullscreen': 'حالت تمام صفحه', 'front': 'همه را جلو بیار' } ================================================ FILE: src/shared/locales/fr/about.js ================================================ export default { 'engine-version': 'Version du moteur', 'license': 'Licence', 'about': 'À Propos', 'release': 'Release', 'support': 'Support' } ================================================ FILE: src/shared/locales/fr/app.js ================================================ export default { 'task-list': 'Tâches', 'add-task': 'Ajouter une tâche', 'about': 'À Propos de Motrix', 'preferences': 'Préférences...', 'check-for-updates': 'Vérifier les mises à jour...', 'check-updates-now': 'Vérifier maintenant', 'checking-for-updates': 'Vérification des mises à jour ...', 'check-for-updates-title': 'Vérifier les mises à jour', 'update-available-message': 'Une nouvelle version de Motrix est disponible, mise à jour maintenant?', 'update-not-available-message': 'Vous êtes à jour!', 'update-downloaded-message': 'Prêt à installer...', 'update-error-message': 'Erreur de mise à jour', 'engine-damaged-message': 'Le moteur est endommagé, veuillez réinstaller : (', 'engine-missing-message': 'Le moteur est manquant, veuillez réinstaller : (', 'system-error-title': 'Erreur système', 'system-error-message': 'L\'application n\'a pas pu démarrer: {{message}}', 'hide': 'Cacher Motrix', 'hide-others': 'Cacher les autres', 'unhide': 'Tout montrer', 'show': 'Montrer Motrix', 'quit': 'Quitter Motrix', 'under-development-message': 'Désolé, cette fonctionnalité est en cours de développement...', 'yes': 'Oui', 'no': 'Non', 'save': 'Sauvegarder', 'reset': 'Jeter', 'cancel': 'Annuler', 'submit': 'Envoyer', 'gt1d': '> 1 jour', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/fr/edit.js ================================================ export default { 'undo': 'Annuler la dernière action', 'redo': 'Rétablir la dernière action', 'cut': 'Couper', 'copy': 'Copier', 'paste': 'Coller', 'delete': 'Supprimer', 'select-all': 'Tout sélectionner' } ================================================ FILE: src/shared/locales/fr/help.js ================================================ export default { 'official-website': 'Site web de Motrix ', 'manual': 'Manuel', 'release-notes': 'Notes de version...', 'report-problem': 'Signaler un problème', 'toggle-dev-tools': 'Activer les outils pour développeurs' } ================================================ FILE: src/shared/locales/fr/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/fr/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Fichier', 'task': 'Tâche', 'edit': 'Editer', 'window': 'Fenêtre', 'help': 'Aide' } ================================================ FILE: src/shared/locales/fr/preferences.js ================================================ export default { 'basic': 'Basique', 'advanced': 'Avancé', 'lab': 'Labo', 'save': 'Sauver et appliquer', 'save-success-message': 'Enregistrer les préférences avec succès', 'save-fail-message': 'La sauvegarde des préférences a échoué', 'discard': 'Annuler les changement', 'startup': 'Démarrage', 'open-at-login': 'Ouvrir à la connexion', 'keep-window-state': 'Restaurez la taille et la position de la fenêtre', 'auto-resume-all': 'Reprendre les tâches non terminées', 'default-dir': 'Répertoire par défaut', 'mas-default-dir-tips': 'En raison des restrictions d\'autorisations du bac à sable de l\'App Store, il est recommandé de définir le répertoire de téléchargement par défaut sur le répertoire Téléchargements.', 'transfer-settings': 'Transmission', 'transfer-speed-upload': 'Limite de téléversement', 'transfer-speed-download': 'Limite de téléchargement', 'transfer-speed-unlimited': 'Illimité', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Enregistrer le lien de l\'aimant en tant que fichier torrent', 'bt-auto-download-content': 'Télécharger automatiquement l\'aimant et le contenu du torrent', 'bt-force-encryption': 'Forcer le chiffrement de BT', 'keep-seeding': 'Continuez à semer jusqu\'à ce que vous l\'arrêtiez manuellement', 'seed-ratio': 'Ratio de semences', 'seed-time': 'Temps de semence', 'seed-time-unit': 'minutes', 'task-manage': 'Tâches', 'max-concurrent-downloads': 'Nombre de tâches active au maximum', 'max-connection-per-server': 'Nombre maximum de connexions par serveurs', 'new-task-show-downloading': 'Montrer automatiquement les téléchargements après l\'ajout d\'une tâche', 'no-confirm-before-delete-task': 'Aucune confirmation n\'est requise avant de supprimer la tâche', 'continue': 'Continuer', 'task-completed-notify': 'Notifier à la fin d\'un téléchargement', 'auto-purge-record': 'Purger l\'historique de téléchargement lorsque vous quittez l\'application', 'ui': 'UI', 'appearance': 'Mode d\'apparence', 'theme-auto': 'Automatique', 'theme-light': 'Clair', 'theme-dark': 'Sombre', 'auto-hide-window': 'Masquer automatiquement les fenêtres', 'run-mode': 'Courir comme', 'run-mode-standard': 'Application standard', 'run-mode-tray': 'Application de la zone de notification', 'run-mode-hide-tray': 'Masquer l\'application de la barre d\'état système', 'tray-speedometer': 'La barre de menus affiche la vitesse en temps réel', 'show-progress-bar': 'Afficher la barre de progression du téléchargement', 'language': 'Langues', 'change-language': 'Changer la langue', 'hide-app-menu': 'Cacher le menu de l\'application (Windows & Linux uniquement)', 'proxy': 'Proxy', 'enable-proxy': 'Activer le Proxy', 'proxy-bypass-input-tips': 'Contourner les paramètres de proxy pour ces hôtes et domaines, un par ligne', 'proxy-scope-download': 'Télécharger', 'proxy-scope-update-app': 'Mettre à jour l\'application', 'proxy-scope-update-trackers': 'Mettre à jour les trackers', 'proxy-tips': 'Afficher le manuel du proxy', 'bt-tracker': 'Serveurs Tracker', 'bt-tracker-input-tips': 'Serveur de suivi, un par ligne', 'bt-tracker-tips': 'Recommander:', 'sync-tracker-tips': 'Sync', 'auto-sync-tracker': 'Mettre à jour automatiquement la liste des trackers chaque jour', 'port': 'Ports d\'écoute', 'bt-port': 'Ports d\'écoute BT', 'dht-port': 'Ports d\'écoute DHT', 'security': 'Sécurité', 'rpc': 'RPC', 'rpc-listen-port': 'Port d\'écoute RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Voir le manuel secret RPC', 'developer': 'Développeur', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Chemin intégré de aria2.conf', 'app-log-path': 'Chemin des logs', 'download-session-path': 'Chemin de la session de téléchargement', 'factory-reset': 'Réinitialisation', 'factory-reset-confirm': 'Êtes vous sûr de vouloir réinitialiser les paramètres', 'lab-warning': '⚠️ Activer les fonctionalités labo peut causer des crash ou la perte de données !', 'download-protocol': 'Protocole', 'protocols-default-client': 'Définir comme client par défaut pour les protocoles suivants', 'protocols-magnet': 'Aimant [ magnet:// ]', 'protocols-thunder': 'Tonnerre [ thunder:// ]', 'browser-extensions': 'Extensions', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Fourni par la communauté, ', 'baidu-exporter-help': 'Cliquez ici pour voir l\'utilisation', 'auto-update': 'Mettre à jour', 'auto-check-update': 'Mise à jour automatique', 'last-check-update-time': 'dernier contrôle la mise à jour du temps', 'not-saved': 'Préférences non enregistrées', 'not-saved-confirm': 'Les préférences modifiées seront perdues, êtes-vous sûr de partir ?' } ================================================ FILE: src/shared/locales/fr/subnav.js ================================================ export default { 'task-list': 'Tâches', 'preferences': 'Préférences' } ================================================ FILE: src/shared/locales/fr/task.js ================================================ export default { 'active': 'Actives', 'waiting': 'En attente', 'stopped': 'Stopées', 'new-task': 'Nouvelle Tâche', 'new-bt-task': 'Nouvelle Tâche BT', 'open-file': 'Ouvrir un fichier torrent...', 'uri-task': 'Lien', 'torrent-task': 'Torrent', 'uri-task-tips': 'Un lien par ligne (supporte les magnets)', 'thunder-link-tips': 'Astuce: Les liens Thunder ne doivent pas être téléchargés après décodage', 'new-task-uris-required': 'Veuillez entrer au moins une URL de ressource valide', 'new-task-torrent-required': 'Veuillez sélectionner un fichier torrent', 'file-name': 'Nom de fichier', 'file-extension': 'Ext', 'file-size': 'Taille', 'file-completed-size': 'Téléchargé', 'selected-files-sum': 'Sélectionné: {{selectedFilesCount}} fichiers, total {{selectedFilesTotalSize}}', 'select-at-least-one': 'Veuillez sélectionner au moins un fichier', 'task-gid': 'GID', 'task-name': 'Nom de la tâche', 'task-out': 'Renommer', 'task-out-tips': 'Optionel', 'task-split': 'Découpages', 'task-dir': 'Répertoire', 'pause-task': 'Mettre en Pause', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorisation', 'task-referer': 'Référent', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Erreur', 'task-piece': 'Pièce', 'task-piece-length': 'Taille de la pièce', 'task-num-pieces': 'Pièces', 'task-bittorrent-info': 'Informations sur le torrent', 'task-info-hash': 'Hacher', 'task-bittorrent-creation-date': 'Date de création', 'task-bittorrent-comment': 'Commenter', 'task-progress-info': 'Le progrès', 'task-status': 'Statut', 'task-num-seeders': 'Semoirs', 'task-connections': 'Connexions', 'task-file-size': 'Taille', 'task-download-speed': 'Vitesse de téléchargement', 'task-upload-speed': 'Vitesse de téléchargement', 'task-download-length': 'Téléchargé', 'task-upload-length': 'Téléchargé', 'task-ratio': 'Rapport', 'task-peer-host': 'Hôte', 'task-peer-ip': 'IP', 'task-peer-client': 'Client', 'navigate-to-downloading': 'Aller au Téléchargement', 'show-advanced-options': 'Options Avancées', 'copyright-warning': 'Avertissement Copyright', 'copyright-warning-message': 'Le fichier que vous voulez télécharger peut être une vidéo ou de l\'audio soumie aux copyright, verifiez que vous possédez bien sa license.', 'copyright-yes': 'Oui, je l\'ai', 'copyright-no': 'Non', 'copyright-error-message': 'Ajout de la tâche annulé, pas de copyright', 'pause-task-success': 'Mise en pause de "{{taskName}}" avec succés', 'pause-task-fail': 'Mise en pause de "{{taskName}}" échouée', 'resume-task': 'Reprendre', 'resume-task-success': 'Reprise de "{{taskName}}" avec succés', 'resume-task-fail': 'Reprise de "{{taskName}}" échouée', 'delete-task': 'Supprimer', 'delete-selected-tasks': 'Supprimer la séléction', 'delete-task-confirm': 'Étes vous sûr de vouloir supprimer "{{taskName}}" ?', 'batch-delete-task-confirm': 'Voulez-vous vraiment supprimer {{count}} tâches de téléchargement par lot?', 'delete-task-label': 'Supprimer avec les fichiers', 'delete-task-success': 'Suppression de "{{taskName}}" avec succés', 'delete-task-fail': 'Suppression de "{{taskName}}" échouée', 'remove-task-file-fail': 'Impossible de supprimer la tâche, s\'il vous plait supprimez la manuellement', 'remove-task-config-file-fail': 'Impossible de supprimer le fichier de configuration de la tâche, s\'il vous plait supprimez le manuellement', 'move-task-up': 'Déplacer vers le haut', 'move-task-down': 'Déplacer vers le bas', 'pause-all-task': 'Tout mettre en pause', 'pause-all-task-success': 'Mise en pause réussie', 'pause-all-task-fail': 'Mise en pause échouée', 'resume-all-task': 'Tout reprendre', 'resume-all-task-success': 'Reprise réussie', 'resume-all-task-fail': 'Reprise échouée', 'select-all-task': 'Sélectionnez toutes les tâches', 'clear-recent-tasks': 'Effacer les tâches récentes', 'purge-record': 'Purger les tâches récentes', 'purge-record-success': 'Purge des tâches récentes réussie', 'purge-record-fail': 'Purge des tâches récentes échouée', 'batch-delete-task-success': 'Supprimez avec succès les tâches du lot', 'batch-delete-task-fail': 'Impossible de supprimer les tâches du lot', 'refresh-list': 'Rafraichir la liste des tâches', 'no-task': 'Aucun téléchargement en cours', 'copy-link': 'Copier le lien', 'copy-link-success': 'Lien copié', 'remove-record': 'Effacer l\'enregistrement des tâches', 'remove-record-confirm': 'Étes vous sûr de vouloir effacer "{{taskName}}" ?', 'remove-record-label': 'Supprimer avec les fichiers', 'remove-record-success': '"{{taskName}}" éffacée', 'remove-record-fail': '"{{taskName}}" n\'a pas été éffacée', 'show-in-folder': 'Montrer la tâche dans le dossier', 'file-not-exist': 'Le fichier n\'existe pas ou a été supprimé', 'file-path-error': 'Erreur de lien sur le fichier', 'opening-task-message': 'Ouverture de "{{taskName}}" ...', 'get-task-name': 'Récupération du nom de la tâche...', 'remaining-prefix': 'Restant', 'select-torrent': 'Glissez-déposez un torrent ici, ou cliquez pour en choisir un', 'task-info-dialog-title': '{{title}} Details', 'download-start-message': 'Téléchargment de {{taskName}}', 'download-pause-message': 'Mise en pause de {{taskName}}', 'download-stop-message': '{{taskName}} téléchargment arrété', 'download-error-message': '{{taskName}} une erreur c\'est produite', 'download-complete-message': '{{taskName}} téléchargement terminé', 'download-complete-notify': 'Téléchargement Terminé', 'bt-download-complete-message': '{{taskName}} téléchargement terminé, ensemencement...', 'bt-download-complete-notify': 'BT Télécharger Terminé, Ensemencement...', 'bt-download-complete-tips': 'Astuces: Vous pouvez arrêter la tâche pour mettre fin à l\'ensemencement', 'bt-stopping-seeding-tip': 'Arrêt de l\'ensemencement, la déconnexion prendra un certain temps, veuillez patienter ...', 'download-fail-message': '{{taskName}} téléchargement échoué', 'download-fail-notify': 'Téléchargement Échoué' } ================================================ FILE: src/shared/locales/fr/window.js ================================================ export default { 'reload': 'Recharger', 'close': 'Fermer', 'minimize': 'Réduire', 'zoom': 'Zoom', 'toggle-fullscreen': 'Passer en mode Plein écran', 'front': 'Tout mettre au premier plan' } ================================================ FILE: src/shared/locales/hu/about.js ================================================ export default { 'engine-version': 'Motor verziója', 'license': 'Licensz', 'about': 'Névjegy', 'release': 'Kiadások', 'support': 'Segítség kérés' } ================================================ FILE: src/shared/locales/hu/app.js ================================================ export default { 'task-list': 'Feladatok', 'add-task': 'Feladat hozzáadasa', 'about': 'Motrix névjegye', 'preferences': 'Beálittasok...', 'check-for-updates': 'Frissitések keresése...', 'check-updates-now': 'Elenörzés most', 'checking-for-updates': 'Frissitések keresése...', 'check-for-updates-title': 'Frissitések keresése', 'update-available-message': 'Egy újabb Motrix verzió elérhető. Telepitsem most?', 'update-not-available-message': 'Nincs elérhető frissités!', 'update-downloaded-message': 'Telepitésre kész...', 'update-error-message': 'Hiba történt frissités közben', 'engine-damaged-message': 'A motor meghibásodot, kérjük telepitse újra a Motrix-ot : (', 'engine-missing-message': 'A motor hiányzik, kérjük telepitse újra a Motrix-ot : (', 'system-error-title': 'Rendszer hiba', 'system-error-message': 'Motrix inditása sikertelen: {{message}}', 'hide': 'Motrix elrejtése', 'hide-others': 'Egyebek elrejtése', 'unhide': 'Minden megjelenitése', 'show': 'Motrix mutatása', 'quit': 'Kilépés', 'under-development-message': 'Sájnaljuk, de ez a funkció feljesztés allat...', 'yes': 'Igen', 'no': 'Nem', 'save': 'Mentés', 'reset': 'Eldobni', 'cancel': 'Mégse', 'submit': 'Beküldés', 'gt1d': '> 1 nap', 'hour': 'ó', 'minute': 'p', 'second': 'm' } ================================================ FILE: src/shared/locales/hu/edit.js ================================================ export default { 'undo': 'Visszavonás', 'redo': 'Mégis', 'cut': 'Kivágas', 'copy': 'Másolas', 'paste': 'Beilesztés', 'delete': 'Törlés', 'select-all': 'Minden kijelölés' } ================================================ FILE: src/shared/locales/hu/help.js ================================================ export default { 'official-website': 'Motrix Weboldala', 'manual': 'Kézikönyv', 'release-notes': 'Valtózások...', 'report-problem': 'Hiba jelentés', 'toggle-dev-tools': 'Fejlesztöi eszközök' } ================================================ FILE: src/shared/locales/hu/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/hu/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Fájl', 'task': 'Feladat', 'edit': 'Szerkesztés', 'window': 'Ablak', 'help': 'Segitség' } ================================================ FILE: src/shared/locales/hu/preferences.js ================================================ export default { 'basic': 'Alap', 'advanced': 'Haladó', 'lab': 'Lab', 'save': 'Mentés és alkalmazás', 'save-success-message': 'Mentés sikeres volt', 'save-fail-message': 'Mentés sikertelen volt', 'discard': 'Elvetés', 'startup': 'Inditás', 'open-at-login': 'Megnyitás bejelenkezéskor', 'keep-window-state': 'Ablak méretének megtartása kilépéskor', 'auto-resume-all': 'Automatikusan folytasa a félbehagyot feladatokat', 'default-dir': 'Alap mappa', 'mas-default-dir-tips': 'A App Store korlatozása miatt ájanlott a ~/Downloads mappaba tenni a letöltéseket', 'transfer-settings': 'Transmission', 'transfer-speed-upload': 'Feltöltés limit', 'transfer-speed-download': 'Letöltés limit', 'transfer-speed-unlimited': 'Végtelen', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Mágneses link mentése torrent fájlként', 'bt-auto-download-content': 'A mágnes és a torrent tartalma automatikus letöltése', 'bt-force-encryption': 'BT erőszakos titkosítása', 'keep-seeding': 'Addig folytassa a vetéset, amíg manuálisan le nem állítja', 'seed-ratio': 'Magarány', 'seed-time': 'Seed Time', 'seed-time-unit': 'percek', 'task-manage': 'Feladatok kezelése', 'max-concurrent-downloads': 'Maximum feladatok', 'max-connection-per-server': 'Maximum csatlakozás szerverenként', 'new-task-show-downloading': 'Automatikusan mutasa meg a letöltéseket befejezésnél', 'no-confirm-before-delete-task': 'Ne legyen megerösités a törlésnél', 'continue': 'Folytatás', 'task-completed-notify': 'Értesités a feladat befejezésnél', 'auto-purge-record': 'Automatikusan tisztitsa a feladatokat kilépéskor', 'ui': 'UI', 'appearance': 'Személyre szabás', 'theme-auto': 'Automatikus', 'theme-light': 'Villágos', 'theme-dark': 'Sötét', 'auto-hide-window': 'Automatikusan rejtse el a ablakot', 'run-mode': 'Inditás néven...', 'run-mode-standard': 'Alap alkalmazás', 'run-mode-tray': 'Tálcás alkalmazás', 'run-mode-hide-tray': 'Tálca alkalmazás elrejtése', 'tray-speedometer': 'Menu csik mutasa a letöltés és feltöltés sebbeséget', 'show-progress-bar': 'Letöltési előrehaladási sáv megjelenítése', 'language': 'Nyelv', 'change-language': 'Nyelv váltas', 'hide-app-menu': 'Alkalmazás csik elrejtése (csak Windows és Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Proxy engedélyezése', 'proxy-bypass-input-tips': 'Proxy beálitassok elvetése ezek url és domain-nél (/sor)', 'proxy-scope-download': 'Letöltés', 'proxy-scope-update-app': 'Alkalmazás frissítése', 'proxy-scope-update-trackers': 'Nyomkövetők frissítése', 'proxy-tips': 'Proxy kéziköny megnyitasa', 'bt-tracker': 'Lekövetö szerverek', 'bt-tracker-input-tips': 'Lekövetö szerverek (/sor)', 'bt-tracker-tips': 'Ájanlass: ', 'sync-tracker-tips': 'Szinkronizalás', 'auto-sync-tracker': 'Lekövetö szerverek listaja frissitése naponta', 'port': 'Listen Ports', 'bt-port': 'BT Listen Port', 'dht-port': 'DHT Listen Port', 'security': 'Biztonság', 'rpc': 'RPC', 'rpc-listen-port': 'RPC-hallgató-port', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'RPC Secret kézikönyv megnyitasa', 'developer': 'feljesztö', 'user-agent': 'User-Agent', 'mock-user-agent': 'User-Agent-t', 'aria2-conf-path': 'Beépített aria2.conf útvonal', 'app-log-path': 'Alkalmazásnapló helye', 'download-session-path': 'Letöltés folyamat helye', 'factory-reset': 'Gyári viszaálitas', 'factory-reset-confirm': 'Biztos vézel gyári viszaálitas?', 'lab-warning': '⚠️ Ha engedélyezed a feljesztésben lévő funkciokat akkor lehetséges a adat sérülés', 'download-protocol': 'Protokol', 'protocols-default-client': 'Alap kliens Protokol', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Bővítmény', 'baidu-exporter': 'BaiduExportálo', 'browser-extensions-tips': 'Közzönség adat, ', 'baidu-exporter-help': 'Kattincs ide a hasznalati naplohoz', 'auto-update': 'Automatikus frissités', 'auto-check-update': 'Automatikus keresen frissitéseket', 'last-check-update-time': 'Legutolsó frissités:', 'not-saved': 'A beállítások nincsenek mentve', 'not-saved-confirm': 'A megváltozott beállítások elvesznek. Biztosan kilép?' } ================================================ FILE: src/shared/locales/hu/subnav.js ================================================ export default { 'task-list': 'Feladat', 'preferences': 'Beallitás' } ================================================ FILE: src/shared/locales/hu/task.js ================================================ export default { 'active': 'Letöltés', 'waiting': 'Varakozás', 'stopped': 'Lealitva', 'new-task': 'Feladat hozzáadasa', 'new-bt-task': 'Új BT Feladat hozzáadasa', 'open-file': 'Torrent fájl megnyitása...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Egy feladat (/sor) (magnet tamogátas)', 'thunder-link-tips': 'Tip: Thunder linkek nem letölthetöek decoding után', 'new-task-uris-required': 'Kérjük addjon meg egy valós eröforrás linket', 'new-task-torrent-required': 'Kérjük válasszon egy valós torrent fájlt', 'file-name': 'Név', 'file-extension': 'Kiterjesztés', 'file-size': 'Méret', 'file-completed-size': 'Letöltött', 'selected-files-sum': 'Kijelölve: {{selectedFilesCount}} fájl, teljes méret {{selectedFilesTotalSize}}', 'select-at-least-one': 'Válasszon legalább egy fájlt', 'task-gid': 'GID', 'task-name': 'Feladat név', 'task-out': 'Átnevezés', 'task-out-tips': 'Opcionális', 'task-split': 'Elosztás', 'task-dir': 'Mentés ide', 'pause-task': 'Feladat szüneteltetése', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Felhatalmazás', 'task-referer': 'Átíranyitó', 'task-cookie': 'Süti', 'task-proxy': 'Proxy', 'task-error-info': 'Hiba', 'task-piece': 'Darab', 'task-piece-length': 'Darabméret', 'task-num-pieces': 'Darabok', 'task-bittorrent-info': 'Torrent információ', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Létrehozás dátuma', 'task-bittorrent-comment': 'Megjegyzés', 'task-progress-info': 'Előrehalad', 'task-status': 'Állapot', 'task-num-seeders': 'Vetőgépek', 'task-connections': 'Kapcsolatok', 'task-file-size': 'Méret', 'task-download-speed': 'Letöltési sebesség', 'task-upload-speed': 'Feltöltési sebesség', 'task-download-length': 'Letöltött', 'task-upload-length': 'Feltöltve', 'task-ratio': 'Hányados', 'task-peer-host': 'Házigazda', 'task-peer-ip': 'IP', 'task-peer-client': 'Ügyfél', 'navigate-to-downloading': 'Navigálas a letöltéshez', 'show-advanced-options': 'Haladó beallitás', 'copyright-warning': 'Szerzöi jogi figyelmeztetés', 'copyright-warning-message': 'A fájl Szerzöi jogi akkor gyözödjön meg arol hogy van joga hozzá.', 'copyright-yes': 'Igen, van jogom hozzá', 'copyright-no': 'Nem, nincs jogom hozzá', 'copyright-error-message': 'Nem sikerült hozzá addni a feladatot szerzöi jog miatt', 'pause-task-success': 'Sikeresen szüneteltettve "{{taskName}}"', 'pause-task-fail': 'Sikeresen volt a szünetelés "{{taskName}}"', 'resume-task': 'Feladat folytatása', 'resume-task-success': 'Feladat folytatása sikeres "{{taskName}}"', 'resume-task-fail': 'Feladat folytatása sikertelen "{{taskName}}"', 'delete-task': 'Feladat törlése', 'delete-selected-tasks': 'Kijelölt feladatok törlése', 'delete-task-confirm': 'Biztosan törlod a "{{taskName}}"?', 'batch-delete-task-confirm': 'Biztosan törölsz {{count}} feladatot?', 'delete-task-label': 'Törlés fájlokal', 'delete-task-success': 'Sikeresen törölve "{{taskName}}"', 'delete-task-fail': 'Sikertelen volt a törlés "{{taskName}}"', 'remove-task-file-fail': 'Sikertelen volt a fájlok törlés, kérjük törölje manualisan', 'remove-task-config-file-fail': 'Sikertelen volt a feladat konfiguráció törlése, kérjük törölje manualisan', 'move-task-up': 'Feladat mozgatása fel', 'move-task-down': 'Feladat mozgatása le', 'pause-all-task': 'Minden feladat szünetelése', 'pause-all-task-success': 'Minden feladat szünetelése sikeres volt', 'pause-all-task-fail': 'Minden feladat szünetelése sikertelen volt', 'resume-all-task': 'Minden feladat folytatása', 'resume-all-task-success': 'Minden feladat folytatása sikeres volt', 'resume-all-task-fail': 'Minden feladat folytatása sikertelen volt', 'select-all-task': 'Összes feladat Kijelölése', 'clear-recent-tasks': 'Elözö feladat törlése', 'purge-record': 'Feladatok tisztitása', 'purge-record-success': 'Feladatok tisztitása sikeres volt', 'purge-record-fail': 'Feladatok tisztitása sikertelen volt', 'batch-delete-task-success': 'Feladat törlése sikeres volt', 'batch-delete-task-fail': 'Feladat törlése sikertelen volt', 'refresh-list': 'Feladat lista frissétése', 'no-task': 'Nincs aktív feladat', 'copy-link': 'Link másolasa', 'copy-link-success': 'Link másolasa sikeres volt', 'remove-record': 'Feladat törlése', 'remove-record-confirm': 'Biztos törlöd a "{{taskName}}"-et?', 'remove-record-label': 'Fájl törlése', 'remove-record-success': 'Feladat törlése sikeres volt "{{taskName}}"', 'remove-record-fail': 'Feladat törlése sikertelen volt "{{taskName}}"', 'show-in-folder': 'Feladat mappa megnyitása', 'file-not-exist': 'A fájl törölve vagy áthelyezve', 'file-path-error': 'Fájl hely hiba', 'opening-task-message': '"{{taskName}}"Megynitasa...', 'get-task-name': 'Feladat név megnyitása...', 'remaining-prefix': 'Maradt', 'select-torrent': 'Húzz ide torrent fájl vagy talloz', 'task-info-dialog-title': '{{title}} részletek', 'download-start-message': 'Letöltés inditása {{taskName}}', 'download-pause-message': 'Letöltés szüneteltettve {{taskName}}', 'download-stop-message': 'Letöltés megallitva {{taskName}}', 'download-error-message': 'Hiba történk letöltés közben {{taskName}}', 'download-complete-message': 'Letöltés befejezve {{taskName}}', 'download-complete-notify': 'Letöltés befejezve', 'bt-download-complete-message': 'Letöltés befejezve {{taskName}}, tovvábadás', 'bt-download-complete-notify': 'BT Letöltés befejezve, tovvábadás...', 'bt-download-complete-tips': 'Tips: Be tudod fejezni a tovvábadás', 'bt-stopping-seeding-tip': 'tovvábadás befejezése, Ez eltarthat par percig...', 'download-fail-message': 'Letöltés sikertelen volt {{taskName}}', 'download-fail-notify': 'Letöltés sikertelen volt' } ================================================ FILE: src/shared/locales/hu/window.js ================================================ export default { 'reload': 'Újratöltés', 'close': 'Bezáras', 'minimize': 'Kis méret', 'zoom': 'Zoom', 'toggle-fullscreen': 'Belépés teljes képernyöbe', 'front': 'Minden elöre' } ================================================ FILE: src/shared/locales/id/about.js ================================================ export default { 'engine-version': 'Versi Mesin', 'license': 'Lisensi', 'about': 'Tentang', 'release': 'Rilis', 'support': 'Bantuan' } ================================================ FILE: src/shared/locales/id/app.js ================================================ export default { 'task-list': 'Tugas', 'add-task': 'Tambah Tugas', 'about': 'Tentang Motrix', 'preferences': 'Preferensi...', 'check-for-updates': 'Periksa Pembaruan...', 'check-updates-now': 'Periksa Sekarang', 'checking-for-updates': 'Memeriksa pembaruan...', 'check-for-updates-title': 'Periksa Pembaruan', 'update-available-message': 'Versi Motrix terbaru telah tersedia, perbarui sekarang?', 'update-not-available-message': 'Aplikasi dalam kondisi ter-update!', 'update-downloaded-message': 'siap meng-install...', 'update-error-message': 'Update Gagal', 'engine-damaged-message': 'Mesin rusak, silahkan install ulang : (', 'engine-missing-message': 'Mesin hilang, silahkan install ulang : (', 'system-error-title': 'System Error', 'system-error-message': 'Gagal Menjalankan Aplikasi: {{message}}', 'hide': 'Sembungikan Motrix', 'hide-others': 'Sembunyikan yang lain', 'unhide': 'Tunjukan Semua', 'show': 'Tunjukan Motrix', 'quit': 'Keluarkan Motrix', 'under-development-message': 'Maaf, fitur ini dalam tahap development...', 'yes': 'Ya', 'no': 'Tidak', 'save': 'Menyimpan', 'reset': 'Membuang', 'cancel': 'Batal', 'submit': 'Kirim', 'gt1d': '> 1 hari', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/id/edit.js ================================================ export default { 'undo': 'Urungkan', 'redo': 'Ulangi', 'cut': 'Potong', 'copy': 'Salin', 'paste': 'Tempel', 'delete': 'Hapus', 'select-all': 'Pilih Semua' } ================================================ FILE: src/shared/locales/id/help.js ================================================ export default { 'official-website': 'Motrix Website', 'manual': 'Panduan', 'release-notes': 'Catatan Rilis...', 'report-problem': 'Laporkan Masalah', 'toggle-dev-tools': 'Alihkan Alat Pengembang' } ================================================ FILE: src/shared/locales/id/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/id/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Berlas', 'task': 'Tugas', 'edit': 'Edit', 'window': 'Window', 'help': 'Bantuan' } ================================================ FILE: src/shared/locales/id/preferences.js ================================================ export default { 'basic': 'Pengaturan Dasar', 'advanced': 'Pengaturan Lanjut', 'lab': 'Lab', 'save': 'Simpan & Terapkan', 'save-success-message': 'Berhasil menyimpan pengaturan', 'save-fail-message': 'Gagal menyimpan pengaturan', 'discard': 'Batal', 'startup': 'Memulai', 'open-at-login': 'Buka saat login', 'keep-window-state': 'Pertahankan ukuran dan posisi jendela aplikasi saat keluar', 'auto-resume-all': 'Otomatis lanjutkan semua tugas yang belum selesai', 'default-dir': 'Lokasi Bawaan', 'mas-default-dir-tips': 'Karena pembatasan dari App Store, direktori unduhan default direkomendasikan untuk disetel ke ~/Downloads', 'transfer-settings': 'Transfer Setting', 'transfer-speed-upload': 'Limit Unggah', 'transfer-speed-download': 'Limit Unduh', 'transfer-speed-unlimited': 'Tak Terbatas', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Simpan tautan magnet sebagai file torrent', 'bt-auto-download-content': 'Secara otomatis mengunduh magnet dan konten torrent', 'bt-force-encryption': 'Memaksa enkripsi paksa BT', 'keep-seeding': 'Terus lakukan penyemaian sampai menghentikannya secara manual', 'seed-ratio': 'Rasio Benih', 'seed-time': 'Waktu Benih', 'seed-time-unit': 'menit', 'task-manage': 'Pengelola Tugas', 'max-concurrent-downloads': 'Maksimal tugas aktif', 'max-connection-per-server': 'Maksimal koneksi per server', 'new-task-show-downloading': 'Tampilkan pengunduhan secara otomatis setelah menambahkan tugas', 'no-confirm-before-delete-task': 'Konfirmasi tidak diperlukan sebelum menghapus tugas', 'continue': 'Lanjutkan', 'task-completed-notify': 'Pemberitahuan setelah pengunduhan selesai', 'auto-purge-record': 'Otomatis bersihkan catatan unduhan saat keluar dari aplikasi', 'ui': 'UI', 'appearance': 'Penampilan', 'theme-auto': 'Auto', 'theme-light': 'Terang', 'theme-dark': 'Gelap', 'auto-hide-window': 'Sembunyikan Otomatis Jendela', 'run-mode': 'Jalankan Sebagai', 'run-mode-standard': 'Aplikasi Standar', 'run-mode-tray': 'Aplikasi Tray', 'run-mode-hide-tray': 'Sembunyikan Aplikasi Tray', 'tray-speedometer': 'Baki menu bar menunjukkan kecepatan waktu-nyata', 'show-progress-bar': 'Tampilkan bilah kemajuan unduhan', 'language': 'Bahasa', 'change-language': 'Ubah Bahasa', 'hide-app-menu': 'Sembunyikan Menu Aplikasi (hanya: Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Aktifkan Proxy', 'proxy-bypass-input-tips': 'Abaikan pengaturan proxy untuk Host dan Domain ini, satu per baris', 'proxy-scope-download': 'Unduh', 'proxy-scope-update-app': 'Perbarui Aplikasi', 'proxy-scope-update-trackers': 'Perbarui Pelacak', 'proxy-tips': 'Lihat Manual Proxy', 'bt-tracker': 'Server Pelacak', 'bt-tracker-input-tips': 'Server pelacak, satu per baris', 'bt-tracker-tips': 'Direkomendasikan: ', 'sync-tracker-tips': 'Sinkronkan', 'auto-sync-tracker': 'Perbarui daftar pelacak setiap hari secara otomatis', 'port': 'Dengarkan Ports', 'bt-port': 'Dengarkan Port BT', 'dht-port': 'Dengarkan Port DHT', 'security': 'Keamanan', 'rpc': 'RPC', 'rpc-listen-port': 'Port Dengar RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Lihat Petunjuk RPC Secret', 'developer': 'Developer', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Jalur aria2.conf Bawaan', 'app-log-path': 'Lokasi Log Aplikasi', 'download-session-path': 'Lokasi Session Unduhan', 'factory-reset': 'Reset Pabrik', 'factory-reset-confirm': 'Anda yakin ingin kembali ke pengaturan pabrik?', 'lab-warning': 'Mengaktifkan fitur lab dapat menyebabkan aplikasi tidak berjalan semestinya atau kehilangan data, risiko ditanggung Anda sendiri!', 'download-protocol': 'Protocols', 'protocols-default-client': 'Tetapkan sebagai klien untuk Protocol berikut', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Ekstensi', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Disediakan oleh komunitas, ', 'baidu-exporter-help': 'Klik di sini untuk penggunaan', 'auto-update': 'Pembaruan Otomatis', 'auto-check-update': 'Secara otomatis memeriksa pembaruan', 'last-check-update-time': 'Terakhir Kali Memeriksa Pembaruan', 'not-saved': 'Preferensi tidak disimpan', 'not-saved-confirm': 'Preferensi yang dimodifikasi akan hilang, apakah Anda yakin untuk keluar?' } ================================================ FILE: src/shared/locales/id/subnav.js ================================================ export default { 'task-list': 'Daftar Tugas', 'preferences': 'Pengaturan' } ================================================ FILE: src/shared/locales/id/task.js ================================================ export default { 'active': 'Mengunduh', 'waiting': 'Mengunggu', 'stopped': 'Terhenti', 'new-task': 'Tugas Baru', 'new-bt-task': 'Tugas BT baru', 'open-file': 'Buka Berkas Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Satu tugas per baris (mendukung magnet)', 'thunder-link-tips': 'Tip: Thunder tautan mungkin tidak dapat diunduh setelah decoding', 'new-task-uris-required': 'Silakan masukkan setidaknya satu url yang valid', 'new-task-torrent-required': 'Silahkan pilih berkas torrent', 'file-name': 'Nama', 'file-extension': 'Perpanjangan', 'file-size': 'Ukuran', 'file-completed-size': 'Ukuran domplet', 'selected-files-sum': 'Terpilih: {{selectedFilesCount}} berkas, total ukuran {{selectedFilesTotalSize}}', 'select-at-least-one': 'Pilih setidaknya satu file', 'task-gid': 'GID', 'task-name': 'Nama Tugas', 'task-out': 'Ubah Nama', 'task-out-tips': 'Opsional', 'task-split': 'Pecahan', 'task-dir': 'Simpan Ke', 'pause-task': 'Tunda Tugas', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Otorisasi', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Kesalahan', 'task-piece': 'Bagian', 'task-piece-length': 'Ukuran Potongan', 'task-num-pieces': 'Potongan', 'task-bittorrent-info': 'Info Torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Tanggal Pembuatan', 'task-bittorrent-comment': 'Komentar', 'task-progress-info': 'Kemajuan', 'task-status': 'Status', 'task-num-seeders': 'Seeders', 'task-connections': 'Koneksi', 'task-file-size': 'Ukuran', 'task-download-speed': 'Kecepatan Download', 'task-upload-speed': 'Kecepatan unggah', 'task-download-length': 'Telah diunduh', 'task-upload-length': 'Diupload', 'task-ratio': 'Perbandingan', 'task-peer-host': 'Tuan rumah', 'task-peer-ip': 'AKU P', 'task-peer-client': 'Klien', 'navigate-to-downloading': 'Beralih ke Unduhan', 'show-advanced-options': 'Setting Lanjutan', 'copyright-warning': 'Peringatan Hak Cipta', 'copyright-warning-message': 'File yang ingin Anda unduh mungkin berupa audio atau video yang dilindungi hak cipta, pastikan Anda memiliki izin untuk mengaksesnya.', 'copyright-yes': 'Ya, Saya punya izin', 'copyright-no': 'Tidak, Saya tidak punya izin', 'copyright-error-message': 'Gagal menambahkan tugas karena masalah hak cipta', 'pause-task-success': 'Tugas berhasil ditunda "{{taskName}}"', 'pause-task-fail': 'Gagal menunda tugas "{{taskName}}"', 'resume-task': 'Lanjutkan tugas', 'resume-task-success': 'Berhasil melanjutkan tugas "{{taskName}}"', 'resume-task-fail': 'Gagal melanjutkan tugas "{{taskName}}"', 'delete-task': 'Hapus tugas', 'delete-selected-tasks': 'Hapus tugas terpilih', 'delete-task-confirm': 'Anda yakin ingin menghapus tugas unduhan "{{taskName}}"?', 'batch-delete-task-confirm': 'Anda yakin ingin menghapus {{count}} tugas unduhan (batch)?', 'delete-task-label': 'Hapus dengan File', 'delete-task-success': 'Tugas "{{taskName}}" berhasil dihapus', 'delete-task-fail': 'Tugas "{{taskName}}" gagal dihapus', 'remove-task-file-fail': 'Gagal menghapus berkas tugas, silahkan hapus secara manual', 'remove-task-config-file-fail': 'Gagal menghapus pengaturan berkas tugas, silahkan hapus secara manual', 'move-task-up': 'Pidahkan Tugas ke Atas', 'move-task-down': 'Pidahkan Tugas ke Bawah', 'pause-all-task': 'Tunda Semua Tugas', 'pause-all-task-success': 'Berhasil menunda semua tugas', 'pause-all-task-fail': 'Gagal menunda semua tugas', 'resume-all-task': 'Lanjutkan Semua Tugas', 'resume-all-task-success': 'Berhasil melanjutkan semua tugas', 'resume-all-task-fail': 'Gagal melanjutkan semua tugas', 'select-all-task': 'Pilih Semua Tugas', 'clear-recent-tasks': 'Bersihkan tugas terakhir', 'purge-record': 'Bersihkan Catatan Tugas', 'purge-record-success': 'Berhasil membersihkan catatan tugas', 'purge-record-fail': 'Gagal membersihkan catatan tugas', 'batch-delete-task-success': 'Berhasil menghapus tugas (batch)', 'batch-delete-task-fail': 'Gagal menghapus tugas (batch)', 'refresh-list': 'Muat Ulang Daftar', 'no-task': 'Tidak ada tugas', 'copy-link': 'Salin Link', 'copy-link-success': 'Berhasil menyalin link', 'remove-record': 'Hapus Data Tugas', 'remove-record-confirm': 'Anda yakin ingin menghapus data unduhan "{{taskName}}"?', 'remove-record-label': 'Hapus dengan Berkas', 'remove-record-success': 'Catatan tugas berhasil dihapus untuk "{{taskName}}"', 'remove-record-fail': 'Gagal menghapus catatan tugas untuk "{{taskName}}"', 'show-in-folder': 'Tampilkan Tugas Di Folder', 'file-not-exist': 'Berkas target tidak ada atau telah dihapus', 'file-path-error': 'Lokasi berkas error', 'opening-task-message': 'Membuka "{{taskName}}" ...', 'get-task-name': 'Mendapatkan nama tugas...', 'remaining-prefix': 'Tersisa', 'select-torrent': 'Seret berkas torrent ke sini, atau klik untuk memilih', 'task-info-dialog-title': '{{title}} Detail', 'download-start-message': 'Memulai mengunduh {{taskName}}', 'download-pause-message': 'Menunda mengunduh {{taskName}}', 'download-stop-message': 'Berhenti mengunduh {{taskName}}', 'download-error-message': 'Terjadi kesalahan saat mengunduh {{taskName}}', 'download-complete-message': 'Selesai mengunduh {{taskName}}', 'download-complete-notify': 'Unduh Selesai', 'bt-download-complete-message': 'Selesai mengunduh {{taskName}}, penyemaian', 'bt-download-complete-notify': 'BT Unduh Selesai, penyemaian...', 'bt-download-complete-tips': 'Tips: Anda dapat menghentikan tugas untuk mengakhiri penyemaian', 'bt-stopping-seeding-tip': 'Menghentikan penyemaian, perlu beberapa saat untuk memutuskan, harap tunggu...', 'download-fail-message': 'Gagal mengunduh {{taskName}}', 'download-fail-notify': 'Unduhan Gagal' } ================================================ FILE: src/shared/locales/id/window.js ================================================ export default { 'reload': 'Muat Ulang', 'close': 'Keluar', 'minimize': 'Perkecil', 'zoom': 'Zoom', 'toggle-fullscreen': 'Layar penuh', 'front': 'Bawa Semua ke Depan' } ================================================ FILE: src/shared/locales/index.js ================================================ /** * Welcome to translate to more versions in other languages. * Please read the translation guide before starting work. * https://github.com/agalwood/Motrix/blob/master/CONTRIBUTING.md#-translation-guide * * Please keep the locale key in alphabetical order. */ export const availableLanguages = [ { value: 'ar', label: 'عربي' }, { value: 'bg', label: 'Българският език' }, { value: 'ca', label: 'Català' }, { value: 'de', label: 'Deutsch' }, { value: 'el', label: 'Ελληνικά' }, { value: 'en-US', label: 'English' }, { value: 'es', label: 'Español' }, { value: 'fa', label: 'فارسی' }, { value: 'fr', label: 'Français' }, { value: 'hu', label: 'Hungarian' }, { value: 'id', label: 'Indonesia' }, { value: 'it', label: 'Italiano' }, { value: 'ja', label: '日本語' }, { value: 'ko', label: '한국어' }, { value: 'nb', label: 'Norsk Bokmål' }, { value: 'nl', label: 'Nederlands' }, { value: 'pl', label: 'Polski' }, { value: 'pt-BR', label: 'Português (Brasil)' }, { value: 'ro', label: 'Română' }, { value: 'ru', label: 'Русский' }, { value: 'th', label: 'แบบไทย' }, { value: 'tr', label: 'Türkçe' }, { value: 'uk', label: 'Українська' }, { value: 'vi', label: 'Tiếng Việt' }, { value: 'zh-CN', label: '简体中文' }, { value: 'zh-TW', label: '繁體中文' } ] const checkLngIsAvailable = (locale) => { return availableLanguages.some(lng => lng.value === locale) } /** * getLanguage * @param { String } locale * https://www.electronjs.org/docs/api/app#appgetlocale * * Only these locales need to add a `startsWith` fallback * when there are with the same prefix * * ar, ar-XB * de, de-AT, de-CH, de-DE * en, en-AU, en-CA, en-GB, en-IN, en-NZ, en-US, en-XA, en-ZA * es, es-419, es-AR, es-CL, es-CO, es-CR, es-ES, es-HN, es-MX, es-PE, es-US, es-UY, es-VE * fr, fr-CA, fr-CH, fr-FR * it, it-CH, it-IT * pt, pt-BR, pt-PT * zh, zh-CN, zh-HK, zh-TW */ export const getLanguage = (locale = 'en-US') => { if (checkLngIsAvailable(locale)) { return locale } if (locale.startsWith('ar')) { return 'ar' } if (locale.startsWith('de')) { return 'de' } if (locale.startsWith('en')) { return 'en-US' } if (locale.startsWith('es')) { return 'es' } if (locale.startsWith('fr')) { return 'fr' } if (locale.startsWith('it')) { return 'it' } // If there is a pt-PT translation in the future, // here will fallback to pt-PT. if (locale.startsWith('pt')) { return 'pt-BR' } if (locale === 'zh-HK') { return 'zh-TW' } if (locale.startsWith('zh')) { return 'zh-CN' } } ================================================ FILE: src/shared/locales/it/about.js ================================================ export default { 'engine-version': 'Versione Engine', 'license': 'Licenza', 'about': 'Chi siamo', 'release': 'Rilasci', 'support': 'Supporto' } ================================================ FILE: src/shared/locales/it/app.js ================================================ export default { 'task-list': 'Attività', 'add-task': 'Aggiungi Attività', 'about': 'A proposito di Motrix', 'preferences': 'Preferenze...', 'check-for-updates': 'Verifica la disponibilità di aggiornamenti...', 'check-updates-now': 'Verifica ora', 'checking-for-updates': 'Sto verificando la disponibilità di aggiornamenti ...', 'check-for-updates-title': 'Verifica la disponibilità di aggiornamenti', 'update-available-message': 'Una nuova versione di Motrix è disponibile, aggiornare ora?', 'update-not-available-message': 'Applicazione già aggiornata!', 'update-downloaded-message': 'Pronto per l\'installazione...', 'update-error-message': 'Errore nell\'aggiornamento', 'engine-damaged-message': 'Il motore è danneggiato, per favore, reinstalla l\'app : (', 'engine-missing-message': 'Il motore è assente, per favore, reinstalla l\'app : (', 'system-error-title': 'Errore di sistema', 'system-error-message': 'L\'applicazione non si è avviata: {{message}}', 'hide': 'Nascondi Motrix', 'hide-others': 'Nascondi Altro', 'unhide': 'Mostra Tutto', 'show': 'Mostra Motrix', 'quit': 'Esci da Motrix', 'under-development-message': 'Scusa, questa funzione è in fase di sviluppo...', 'yes': 'Si', 'no': 'No', 'save': 'Salva', 'reset': 'Scartare', 'cancel': 'Annulla', 'submit': 'Invia', 'gt1d': '> 1 giorno', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/it/edit.js ================================================ export default { 'undo': 'Annulla', 'redo': 'Ripeti', 'cut': 'Taglia', 'copy': 'Copia', 'paste': 'Incolla', 'delete': 'Elimina', 'select-all': 'Seleziona Tutto' } ================================================ FILE: src/shared/locales/it/help.js ================================================ export default { 'official-website': 'Sito di Motrix', 'manual': 'Manuale', 'release-notes': 'Note di rilascio...', 'report-problem': 'Segnala un\'problema', 'toggle-dev-tools': 'Attiva/disatttiva gli Strumenti di sviluppo' } ================================================ FILE: src/shared/locales/it/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/it/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'File', 'task': 'Attività', 'edit': 'Modifica', 'window': 'Finestra', 'help': 'Aiuto' } ================================================ FILE: src/shared/locales/it/preferences.js ================================================ export default { 'basic': 'Base', 'advanced': 'Avanzate', 'lab': 'Sperimentali', 'save': 'Salva e applica', 'save-success-message': 'Preferenze salvate con successo', 'save-fail-message': 'Preferenze non salvate', 'discard': 'Scarta modifiche', 'startup': 'Avvio', 'open-at-login': 'Apri al login', 'keep-window-state': 'Mantieni le dimensioni e la posizione della finestra quando l\'app viene chiusa', 'auto-resume-all': 'Ricomincia tutti le attività non finite alla riapertura dell\'app', 'default-dir': 'Posizione di default per i download', 'mas-default-dir-tips': 'A causa delle restrizioni imposte dall\'App Store, è raccomandato impostare la directory di default su ~/Downloads', 'transfer-settings': 'Transmissione dati', 'transfer-speed-upload': 'Limite di uplooad', 'transfer-speed-download': 'Limite di download', 'transfer-speed-unlimited': 'Illimitata', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Salva magnet link come file torrent', 'bt-auto-download-content': 'Scarica automaticamente il contenuto di magnete e torrent', 'bt-force-encryption': 'Forzare la crittografia di BT', 'keep-seeding': 'Continua a seminare fino a interromperlo manualmente', 'seed-ratio': 'Rapporto di semina', 'seed-time': 'Tempo di semi', 'seed-time-unit': 'minuti', 'task-manage': 'Gestione attività', 'max-concurrent-downloads': 'Massimo numero di attività eseguibili contemporaneamente', 'max-connection-per-server': 'Massimo numero di connessioni simultanee per server', 'new-task-show-downloading': 'Mostra automaticamente il download quando aggiungo una nuova attività', 'no-confirm-before-delete-task': 'Nessuna conferma richiesta prima di eliminare un\'attività', 'continue': 'Continua', 'task-completed-notify': 'Notifica quando un download è finito', 'auto-purge-record': 'Elimina automaticamente la cronologia di download quando esco l\'app viene chiusa', 'ui': 'UI', 'appearance': 'Aspetto', 'theme-auto': 'Auto', 'theme-light': 'Chiaro', 'theme-dark': 'Scuro', 'auto-hide-window': 'Nascondi automaticamente la finestra', 'run-mode': 'Avvia come', 'run-mode-standard': 'Applicazione Standard ', 'run-mode-tray': 'Applicazione della barra delle applicazioni', 'run-mode-hide-tray': 'Nascondi applicazione della barra delle applicazioni', 'tray-speedometer': 'La barra dei menu mostra la velocità in tempo reale', 'show-progress-bar': 'Mostra la barra di progresso del download', 'language': 'Lingua', 'change-language': 'Cambia lingua', 'hide-app-menu': 'Nascondi dal menu delle app (Solo Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Usa Proxy', 'proxy-bypass-input-tips': 'Non usare proxy per questi Host e Domini, uno per linea', 'proxy-scope-download': 'Scarica', 'proxy-scope-update-app': 'Aggiorna applicazione', 'proxy-scope-update-trackers': 'Aggiorna tracker', 'proxy-tips': 'Guida all\'uso dei proxy (In Inglese)', 'bt-tracker': 'Server di monitoraggio', 'bt-tracker-input-tips': 'Tracker servers, uno per linea', 'bt-tracker-tips': 'Raccomandati: ', 'sync-tracker-tips': 'Sincronizza', 'auto-sync-tracker': 'Aggiorna automaticamente la lista dei tracker ogni giorno', 'port': 'Porte in ascolto', 'bt-port': 'Porte in ascolto BT', 'dht-port': 'Porte in ascolto DHT', 'security': 'Sicurezza', 'rpc': 'RPC', 'rpc-listen-port': 'Porta di Ascolto RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Guida sull\'uso degli rpc secret (in Inglese)', 'developer': 'Sviluppatore', 'user-agent': 'User-Agent', 'mock-user-agent': 'Cambia User-Agent', 'aria2-conf-path': 'Percorso incorporato di aria2.conf', 'app-log-path': 'Posizione log dell\'app', 'download-session-path': 'Posizione sessione di download', 'factory-reset': 'Reset di fabbrica', 'factory-reset-confirm': 'Sei sicuro di voler riportare alle impostazioni di fabbrica l\'app?', 'lab-warning': '⚠️ Ablilitare le funzioni sperimentali potrebbe risultare in un crash o una perdita di dati, decidi a tuo rischio e pericolo!', 'download-protocol': 'Protocolli', 'protocols-default-client': 'Imposta di Default i seguenti protocolli', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Estensione per browser', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Fornita dalla community, ', 'baidu-exporter-help': 'Clicca qui per scoprire il funzionamento', 'auto-update': 'Auto Update', 'auto-check-update': 'Verifica automaticamente la disponibilità di aggiornamenti', 'last-check-update-time': 'Ultima volta quando gli aggiornamenti sono stati verificati', 'not-saved': 'Preferenze non salvate', 'not-saved-confirm': 'Le preferenze modificate andranno perse, sei sicuro di uscire?' } ================================================ FILE: src/shared/locales/it/subnav.js ================================================ export default { 'task-list': 'Attività', 'preferences': 'Preferenze' } ================================================ FILE: src/shared/locales/it/task.js ================================================ export default { 'active': 'In corso', 'waiting': 'In Pausa', 'stopped': 'Terminate', 'new-task': 'Nuova attività', 'new-bt-task': 'Nuova attività BT', 'open-file': 'Apri un file Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Un url per linea (supporta magnet)', 'thunder-link-tips': 'Nota: I Thunder links potrebbero non essere più scaricabili dopo il decoding', 'new-task-uris-required': 'Per favore, inserisci almeno un url risorsa valido', 'new-task-torrent-required': 'Per favore, inserisci un file torrent', 'file-name': 'Nome', 'file-extension': 'Estensione', 'file-size': 'Dimensione', 'file-completed-size': 'Completato', 'selected-files-sum': 'Selezionati: {{selectedFilesCount}} files, dimensione totale {{selectedFilesTotalSize}}', 'select-at-least-one': 'Seleziona almeno un file', 'task-gid': 'GID', 'task-name': 'Nome attività', 'task-out': 'Rinomina', 'task-out-tips': '(opzionale)', 'task-split': 'Dividi', 'task-dir': 'Posizione file', 'pause-task': 'Metti in pausa attività', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorizzazione', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Errore', 'task-piece': 'Pezzo', 'task-piece-length': 'Dimensione del pezzo', 'task-num-pieces': 'Pezzi', 'task-bittorrent-info': 'Informazioni sul torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Data di creazione', 'task-bittorrent-comment': 'Commento', 'task-progress-info': 'Progresso', 'task-status': 'Stato', 'task-num-seeders': 'Seminatrici', 'task-connections': 'Connessioni', 'task-file-size': 'Dimensione', 'task-download-speed': 'Velocità di download', 'task-upload-speed': 'Velocità di caricamento', 'task-download-length': 'Scaricato', 'task-upload-length': 'Caricato', 'task-ratio': 'Rapporto', 'task-peer-host': 'Ospite', 'task-peer-ip': 'IP', 'task-peer-client': 'Cliente', 'navigate-to-downloading': 'Naviga per scaricare', 'show-advanced-options': 'Opzioni avanzate', 'copyright-warning': 'Avviso sul Copyright', 'copyright-warning-message': 'Il file che stai cercando di scaricare potrebbe esssere un audio o video soggetto a Copyright, per favore, assicurati che tu abbia il permesso per accedervi.', 'copyright-yes': 'Si, ho il permesso', 'copyright-no': 'No, non ho il permesso', 'copyright-error-message': 'Impossibile scaricare il file a causa di un problema di Copyright', 'pause-task-success': '"{{taskName}}" messo in pausa con successo', 'pause-task-fail': 'Impossibile mettere in pausa "{{taskName}}"', 'resume-task': 'Ricomincia Task', 'resume-task-success': 'Attività "{{taskName}}" ricominciata con successo', 'resume-task-fail': 'Impossibile ricominciare l\'attività: "{{taskName}}"', 'delete-task': 'Elimina attività', 'delete-selected-tasks': 'Elimina attività selezionate', 'delete-task-confirm': 'Sei sicuro di voler rimuovere l\'attività "{{taskName}}"?', 'batch-delete-task-confirm': 'Sei sicuro di voler rimuovere {{count}} attività in batch?', 'delete-task-label': 'Elimina con i file', 'delete-task-success': 'attività "{{taskName}}" eliminata con successo', 'delete-task-fail': 'Impossibile eliminare l\'attività "{{taskName}}"', 'remove-task-file-fail': 'Imossibile eliminare i file(s) delle attività, per favore, eliminali manualmente', 'remove-task-config-file-fail': 'Impossibile eliminare i file di configutazine delle attività, per favore, eliminali manualmente', 'move-task-up': 'Muovi l\'attività più in alto', 'move-task-down': 'Muovi l\'attività più in basso', 'pause-all-task': 'Metti in pausa tutte le attività', 'pause-all-task-success': 'Tutte le attività messe in pausa con successo', 'pause-all-task-fail': 'Impossibile mettere in pausa tutte le attività', 'resume-all-task': 'Ricomincia tutte le attività', 'resume-all-task-success': 'Tutte le attività ricominciate con successo', 'resume-all-task-fail': 'Impossibile ricominciate tutte le attività', 'select-all-task': 'Seleziona tutte le attività', 'clear-recent-tasks': 'Elimina le attività recenti', 'purge-record': 'Elimina la cronologia delle attività', 'purge-record-success': 'Cronologia delle attività eliminata con successo', 'purge-record-fail': 'Impossibile eliminare la cronologia delle attività', 'batch-delete-task-success': 'Attività eliminate in batch con successo', 'batch-delete-task-fail': 'Impossibile eliminare in batch le attività', 'refresh-list': 'Aggiorna la lista delle attività', 'no-task': 'Non ci sono attività', 'copy-link': 'Copia link', 'copy-link-success': 'Link copiato con successo', 'remove-record': 'Rimuovi cronologia delle attività', 'remove-record-confirm': 'Sei sicuro di voler eliminare la cronologia di download di "{{taskName}}"?', 'remove-record-label': 'Elimina con i file', 'remove-record-success': 'cronologia dell\'attività "{{taskName}}" eliminata con successo', 'remove-record-fail': 'Imposibile eliminare la cronologia dell\'attività "{{taskName}}"', 'show-in-folder': 'Mostra le attività nela cartella', 'file-not-exist': 'File target non esistente o eliminato', 'file-path-error': 'Errore path del file', 'opening-task-message': 'Apertura attività "{{taskName}}" ...', 'get-task-name': 'Ottengo il nome del file...', 'remaining-prefix': 'Rimanente', 'select-torrent': 'Trascina il file torrent qua o clicca per selezionarlo', 'task-info-dialog-title': 'Dettagli {{title}}', 'download-start-message': 'Iniziato il download di {{taskName}}', 'download-pause-message': 'Download di {{taskName}} in pausa', 'download-stop-message': 'Download di {{taskName}} stoppato', 'download-error-message': 'Errore durante il download di {{taskName}}', 'download-complete-message': 'Dowload di {{taskName}} completato', 'download-complete-notify': 'Download completato', 'bt-download-complete-message': 'Completed downloading {{taskName}}, seeding', 'bt-download-complete-notify': 'Dowload BT completato, seeding...', 'bt-download-complete-tips': 'Suggerimento: È possibile interrompere un\'attività per fermare il seeding', 'bt-stopping-seeding-tip': 'Seeding fermato, ci vorrà un po\' di tempo per disconnettersi, per favore, aspetta...', 'download-fail-message': 'Impossibile scaricare {{taskName}}', 'download-fail-notify': 'Download Fallito' } ================================================ FILE: src/shared/locales/it/window.js ================================================ export default { 'reload': 'Ricarica', 'close': 'Chiudi', 'minimize': 'Riduci a icona', 'zoom': 'Zoo', 'toggle-fullsmcreen': 'Modalità a schermo intero', 'front': 'Riporta tutto davanti' } ================================================ FILE: src/shared/locales/ja/about.js ================================================ export default { 'engine-version': 'バージョンを確認', 'license': 'ライセンス', 'about': '私たちについて', 'release': 'リリースノート', 'support': 'サポート' } ================================================ FILE: src/shared/locales/ja/app.js ================================================ export default { 'task-list': 'タスクリスト', 'add-task': 'タスクを追加', 'about': 'Motrix について', 'preferences': '環境設定...', 'check-for-updates': '更新を確認...', 'check-updates-now': '今すぐチェック', 'checking-for-updates': 'アップデートをチェックしています...', 'check-for-updates-title': 'アップデートをチェック', 'update-available-message': '新しいバージョンのMotrixが利用可能です、今すぐ更新しますか?', 'update-not-available-message': 'あなたは最新です!', 'update-downloaded-message': 'インストールする準備ができています...', 'update-error-message': '更新エラー', 'engine-damaged-message': 'エンジンが破損しています、再インストールしてください : (', 'engine-missing-message': 'エンジンが見つかりません。再インストールしてください : (', 'system-error-title': 'システムエラー', 'system-error-message': 'アプリケーションの起動に失敗しました:{{message}}', 'hide': 'Motrix を隠す', 'hide-others': 'ほかを隠す', 'unhide': 'すべてを表示', 'quit': 'Motrix を終了', 'under-development-message': 'この機能は開発中です...', 'yes': 'はい', 'no': 'いいえ', 'save': 'セーブ', 'reset': '放棄', 'cancel': 'キャンセル', 'submit': '確認', 'gt1d': '一日を超える', 'hour': '時', 'minute': '分', 'second': '秒' } ================================================ FILE: src/shared/locales/ja/edit.js ================================================ export default { 'undo': '元に戻す', 'redo': 'やり直す', 'cut': '切り取り', 'copy': 'コピー', 'paste': '貼り付け', 'delete': '削除', 'select-all': 'すべてを選択' } ================================================ FILE: src/shared/locales/ja/help.js ================================================ export default { 'official-website': 'Motrix 公式サイト', 'manual': '使用説明', 'release-notes': 'リリースノート...', 'report-problem': '問題を報告', 'toggle-dev-tools': '開発者ツール' } ================================================ FILE: src/shared/locales/ja/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ja/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'ファイル', 'task': 'タスク', 'edit': '編集', 'window': 'ウィンドウ', 'help': 'ヘルプ' } ================================================ FILE: src/shared/locales/ja/preferences.js ================================================ export default { 'basic': '基本設定', 'advanced': '詳細設定', 'lab': '実験室', 'save': '保存して適用', 'save-success-message': '設定を保存します', 'save-fail-message': '設定を保存できませんでした', 'discard': '放棄', 'startup': '起動', 'open-at-login': '自動的に起動します', 'auto-resume-all': '起動後自動的に未完了タスクを再開', 'keep-window-state': 'ウィンドウのサイズと位置を元に戻します', 'default-dir': '既定の保存先', 'mas-default-dir-tips': 'App Store の Sandbox 制限のため,デフォルトディレクトリ設定は「Download」を推奨します。', 'transfer-settings': '転送設定', 'transfer-speed-upload': 'アップロード制限', 'transfer-speed-download': 'ダウンロード制限', 'transfer-speed-unlimited': '無制限', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'マグネットリンクをトレントファイルとして保存', 'bt-auto-download-content': '自動的に磁石と急流のコンテンツをダウンロードします', 'bt-force-encryption': 'BT 強制暗号化', 'keep-seeding': '手動で停止するまでシードを続けます', 'seed-ratio': 'シード比率', 'seed-time': 'シードタイム', 'seed-time-unit': '分', 'task-manage': 'タスク管理', 'max-concurrent-downloads': '最大同時タスク数', 'max-connection-per-server': '最大サーバ接続数', 'new-task-show-downloading': '新規タスクを作成後自動的にタスク画面に移る', 'no-confirm-before-delete-task': 'タスクを削除する前に確認は必要ありません', 'continue': '続ける', 'task-completed-notify': 'タスク完了後に通知する', 'auto-purge-record': 'アプリケーション終了後自動的にタスク履歴を削除', 'ui': 'UI', 'appearance': 'テーマ', 'theme-auto': '自動', 'theme-light': 'ライト', 'theme-dark': 'ダーク', 'auto-hide-window': '自動非表示ウィンドウ', 'run-mode': '実行者', 'run-mode-standard': '標準アプリケーション', 'run-mode-tray': 'トレイアプリケーション', 'run-mode-hide-tray': 'トレイアプリケーションを非表示にする', 'tray-speedometer': 'メニューバートレイは、リアルタイムの速度を示します', 'show-progress-bar': 'ダウンロード進行状況の表示', 'language': '言語', 'change-language': '言語を切り替え', 'hide-app-menu': 'メニューバーを隠す(Windows と Linux のみサポート)', 'proxy': 'プロキシ', 'enable-proxy': 'プロキシを使う', 'proxy-bypass-input-tips': 'これらのホストおよびドメインのプロキシ設定を1行に1つずつバイパスします', 'proxy-scope-download': 'ダウンロード', 'proxy-scope-update-app': 'アプリケーションの更新', 'proxy-scope-update-trackers': 'トラッカーを更新する', 'proxy-tips': 'プロキシマニュアルを表示', 'bt-tracker': 'トラッカーサーバー', 'bt-tracker-input-tips': 'トラッカーサーバ、一行に一つ', 'bt-tracker-tips': 'お勧め:', 'sync-tracker-tips': '同期する', 'auto-sync-tracker': 'トラッカーリストを毎日自動的に更新します', 'port': 'リスンポート', 'bt-port': 'BT リスンポート', 'dht-port': 'DHT リスンポート', 'security': 'セキュリティ', 'rpc': 'RPC', 'rpc-listen-port': 'RPCリッスンポート', 'rpc-secret': 'RPCシークレット', 'rpc-secret-tips': 'RPCシークレットマニュアルの閲覧', 'developer': '開発者', 'user-agent': 'User-Agent', 'mock-user-agent': '偽装ユーザーエージェント(UA)', 'aria2-conf-path': '組み込みの aria2.conf パス', 'app-log-path': 'ログディレクトリを適用', 'download-session-path': 'セッションパスをダウンロード', 'factory-reset': '初期設定に戻す', 'factory-reset-confirm': '本当に初期設定に戻しますか?', 'lab-warning': '⚠️ベータ機能をオンにするとアプリケーションの強制終了やデータが損失する可能性があります。自己責任でお願いします。', 'download-protocol': 'プロトコル', 'protocols-default-client': '以下のプロトコルのデフォルトクライアントとして設定', 'protocols-magnet': '磁石 [ magnet:// ]', 'protocols-thunder': 'サンダー [ thunder:// ]', 'browser-extensions': 'ブラウザ拡張機能', 'baidu-exporter': 'バイドゥオンラインストレージ拡張機能', 'browser-extensions-tips': '他のユーザによって作成されたものです。動作は保証できません。', 'baidu-exporter-help': 'ここをクリックし使用説明を見る', 'auto-update': '自動更新', 'auto-check-update': '更新を自動で確認する', 'last-check-update-time': '前回更新確認時間', 'not-saved': '設定が保存されていません', 'not-saved-confirm': '変更された設定は失われます、よろしいですか?' } ================================================ FILE: src/shared/locales/ja/subnav.js ================================================ export default { 'task-list': 'タスクリスト', 'preferences': '環境設定' } ================================================ FILE: src/shared/locales/ja/task.js ================================================ export default { 'active': 'ダウンロード中', 'waiting': '待機中', 'stopped': '一時停止中', 'new-task': '新規タスク', 'new-bt-task': '新規torrentタスク', 'open-file': 'torrentファイルを開く...', 'uri-task': 'URLタスク', 'torrent-task': 'torrentタスク', 'uri-task-tips': 'URLを複数追加したとき、一行につき一つのリンクとなります(マグネットリンクをサポート)', 'thunder-link-tips': '注意:Thunder(Xunlei)リンク解析後、ダウンロードできるかは保証できません', 'new-task-uris-required': '少なくとも1つの有効なリソースURLを入力してください', 'new-task-torrent-required': 'トレントファイルを選択してください', 'file-name': 'ファイル名', 'file-extension': '拡張子', 'file-size': 'サイズ', 'file-completed-size': '完成サイズ', 'selected-files-sum': '選択済み:{{selectedFilesCount}}ファイル、合計{{selectedFilesTotalSize}}', 'select-at-least-one': '少なくとも1つのファイルを選択してください', 'task-gid': 'GID', 'task-name': 'タスク名', 'task-out': '名前を変更', 'task-out-tips': 'オプション', 'task-split': 'タスク分割', 'task-dir': 'タスク保存先', 'task-ua': 'UA', 'task-user-agent': 'ユーザーエージェント', 'task-authorization': '認可', 'task-referer': 'リファラ', 'task-cookie': 'クッキー', 'task-proxy': 'プロキシ', 'task-error-info': 'エラー', 'task-piece': 'ピース', 'task-piece-length': 'ピースサイズ', 'task-num-pieces': 'ピース', 'task-bittorrent-info': 'トレント情報', 'task-info-hash': 'ハッシュ', 'task-bittorrent-creation-date': '作成日', 'task-bittorrent-comment': 'コメント', 'task-progress-info': '進捗', 'task-status': '状態', 'task-num-seeders': 'シーダー', 'task-connections': '接続', 'task-file-size': 'サイズ', 'task-download-speed': 'ダウンロード速度', 'task-upload-speed': 'アップロードスピード', 'task-download-length': 'ダウンロード済み', 'task-upload-length': 'アップロード済み', 'task-ratio': '播種率', 'task-peer-host': 'ホスト', 'task-peer-ip': 'IP', 'task-peer-client': 'クライアント', 'navigate-to-downloading': 'タスク画面に進む', 'show-advanced-options': '詳細設定', 'copyright-warning': '著作権警告', 'copyright-warning-message': 'あなたがダウンロードしようとしているファイルは著作権のある音声・動画の可能性があります。著作権があるかどうか確認してください', 'copyright-yes': 'はい、著作権があります', 'copyright-no': 'いいえ', 'copyright-error-message': '著作権の問題で、タスク追加に失敗', 'pause-task': 'タスクを一時停止', 'pause-task-success': 'タスク "{{taskName}}" の一時停止に成功', 'pause-task-fail': 'タスク "{{taskName}}" の一時停止に失敗', 'resume-task': 'タスクを再開', 'resume-task-success': 'タスク "{{taskName}}" の再開に成功', 'resume-task-fail': '恢复任务 "{{taskName}}" の再開に失敗', 'delete-task': 'タスクを削除', 'delete-selected-tasks': '選択中のタスクを削除', 'delete-task-confirm': '本当にタスク "{{taskName}}" を削除しますか?', 'batch-delete-task-confirm': '{{count}}つのダウンロードタスクをバッチで削除してもよろしいですか?', 'delete-task-label': '同時にタスクを削除', 'delete-task-success': 'タスク "{{taskName}}" の削除に成功', 'delete-task-fail': 'タスク "{{taskName}}" の削除に失敗', 'remove-task-file-fail': 'ダウンロードファイルの削除に失敗、手動で削除してください', 'remove-task-config-file-fail': 'タスク設定ファイルの削除に失敗、手動で削除してください', 'move-task-up': 'タスクを上に移す', 'move-task-down': 'タスクを下に移す', 'pause-all-task': 'すべてのタスクを一時停止', 'pause-all-task-success': 'すべてのタスクの一時停止に成功', 'pause-all-task-fail': 'すべてのタスクの一時停止に失敗', 'resume-all-task': 'すべてのタスクを再開', 'resume-all-task-success': 'すべてのタスクを再開に成功', 'resume-all-task-fail': 'すべてのタスクを再開に失敗', 'select-all-task': 'すべてのタスクを選択します', 'clear-recent-tasks': '最近のタスク履歴を削除', 'purge-record': 'タスク履歴をクリア', 'purge-record-success': 'タスク履歴のクリアに成功', 'purge-record-fail': 'タスク履歴のクリアに失敗', 'batch-delete-task-success': 'タスクをバッチで正常に削除しました', 'batch-delete-task-fail': 'タスクをバッチで削除できませんでした', 'refresh-list': 'タスクリストを更新', 'no-task': 'タスクはありません', 'copy-link': 'リンクをコピー', 'copy-link-success': 'リンクのコピーに成功', 'remove-record': 'タスク履歴を削除', 'remove-record-confirm': '本当に "{{taskName}}" のタスク履歴を削除しますか?', 'remove-record-label': 'ダウンロードファイルも同時に削除', 'remove-record-success': ' "{{taskName}}" のタスク履歴の削除に成功', 'remove-record-fail': ' "{{taskName}}" のタスク履歴の削除に失敗', 'show-in-folder': 'フォルダを表示', 'file-not-exist': 'お探しのファイルは存在しないか、削除されています。', 'file-path-error': 'ファイルパスエラー', 'opening-task-message': '現在 "{{taskName}}" を開いています...', 'get-task-name': 'タスク名を取得しています...', 'remaining-prefix': '残り', 'select-torrent': 'torrentファイルをドラッグ&ドロップするか、ここをクリック', 'task-info-dialog-title': '{{title}} の詳細', 'download-start-message': '{{taskName}}のダウンロードを開始', 'download-pause-message': '{{taskName}}のダウンロードを一時停止', 'download-stop-message': '{{taskName}} のダウンロードを中止', 'download-error-message': '{{taskName}} のダウンロードにエラーが発生', 'download-complete-message': '{{taskName}} のダウンロードに成功', 'download-complete-notify': 'ダウンロード成功', 'bt-download-complete-message': '{{taskName}} のダウンロードに成功、seedを作成中...', 'bt-download-complete-notify': 'torrentタスクのダウンロードに成功,seedを作成中...', 'bt-download-complete-tips': 'ヒント:タスクを停止しseedの作成を終了することができます', 'bt-stopping-seeding-tip': 'シードを停止しています。切断するにはしばらく時間がかかります。お待ちください...', 'download-fail-message': '{{taskName}} のダウンロードに失敗', 'download-fail-notify': 'ダウンロード失敗' } ================================================ FILE: src/shared/locales/ja/window.js ================================================ export default { 'reload': 'リロード', 'close': '閉じる', 'minimize': '最小化', 'zoom': '拡大', 'toggle-fullscreen': 'フルスクリーンにする', 'front': '全てを手前に移動' } ================================================ FILE: src/shared/locales/ko/about.js ================================================ export default { 'engine-version': '엔진 버전', 'license': '라이선스', 'about': '정보', 'release': '릴리스', 'support': '지원' } ================================================ FILE: src/shared/locales/ko/app.js ================================================ export default { 'task-list': '작업 목록', 'add-task': '작업 추가', 'about': 'Motrix 정보', 'preferences': '설정...', 'check-for-updates': '업데이트 확인...', 'check-updates-now': '지금 확인', 'checking-for-updates': '업데이트 확인 중...', 'check-for-updates-title': '업데이트 확인', 'update-available-message': '새 버전의 Motrix를 사용할 수 있습니다. 지금 업데이트하시겠습니까?', 'update-not-available-message': '최신 버전을 사용 중입니다!', 'update-downloaded-message': '설치 준비 완료...', 'update-error-message': '업데이트 오류', 'engine-damaged-message': '엔진이 손상되었으므로 다시 설치하십시오: (', 'engine-missing-message': '엔진이 누락되었으므로 다시 설치하십시오: (', 'system-error-title': '시스템 오류', 'system-error-message': '애플리케이션 시작 실패: {{message}}', 'hide': 'Motrix 숨기기', 'hide-others': '다른 항목 숨기기', 'unhide': '모두 보기', 'show': 'Motrix 보기', 'quit': 'Motrix 종료', 'under-development-message': '죄송합니다, 이 기능은 개발 중입니다...', 'yes': '예', 'no': '아니요', 'save': '저장', 'reset': '취소', 'cancel': '취소', 'submit': '확인', 'gt1d': '> 1일', 'hour': '시간', 'minute': '분', 'second': '초' } ================================================ FILE: src/shared/locales/ko/edit.js ================================================ export default { 'undo': '실행 취소', 'redo': '다시 실행', 'cut': '잘라내기', 'copy': '복사', 'paste': '붙여넣기', 'delete': '삭제', 'select-all': '모두 선택' } ================================================ FILE: src/shared/locales/ko/help.js ================================================ export default { 'official-website': 'Motrix 웹 사이트', 'manual': '매뉴얼', 'release-notes': '릴리스 정보...', 'report-problem': '문제 보고', 'toggle-dev-tools': '개발자 도구' } ================================================ FILE: src/shared/locales/ko/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ko/menu.js ================================================ export default { 'app': 'Motrix', 'file': '파일', 'task': '작업', 'edit': '편집', 'window': '창', 'help': '도움말' } ================================================ FILE: src/shared/locales/ko/preferences.js ================================================ export default { 'basic': '기본', 'advanced': '고급', 'lab': '실험실', 'save': '저장 및 적용', 'save-success-message': '설정을 성공적으로 저장했습니다', 'save-fail-message': '설정을 저장하지 못했습니다', 'discard': '취소', 'startup': '시작', 'open-at-login': '로그인 시 실행', 'keep-window-state': '창 크기 및 위치 기억', 'auto-resume-all': '완료되지 않은 모든 작업 자동 재개', 'default-dir': '기본 폴더', 'mas-default-dir-tips': 'App Store의 샌드박스 권한 제한으로 인해 기본 다운로드 폴더는 ~/Downloads로 설정하는 것이 좋습니다.', 'transfer-settings': '전송', 'transfer-speed-upload': '업로드 제한', 'transfer-speed-download': '다운로드 제한', 'transfer-speed-unlimited': '무제한', 'bt-settings': 'BitTorrent', 'bt-save-metadata': '마그넷 링크를 토렌트 파일로 저장', 'bt-auto-download-content': '마그넷 및 토렌트 내용 자동 다운로드', 'bt-force-encryption': 'BT 강제 암호화', 'keep-seeding': '수동으로 멈출 때까지 계속 배포', 'seed-ratio': '배포 비율', 'seed-time': '배포 시간', 'seed-time-unit': '분', 'task-manage': '작업 관리', 'max-concurrent-downloads': '최대 활성 작업', 'max-connection-per-server': '서버 당 최대 연결 수', 'new-task-show-downloading': '작업 추가 후 자동으로 다운로드 표시', 'no-confirm-before-delete-task': '작업을 삭제하기 전에 확인하지 않기', 'continue': '계속', 'task-completed-notify': '다운로드 완료 후 알림', 'auto-purge-record': '앱 종료 시 다운로드 기록 자동 삭제', 'ui': 'UI', 'appearance': '테마', 'theme-auto': '자동', 'theme-light': '밝게', 'theme-dark': '어둡게', 'auto-hide-window': '창 자동으로 숨기기', 'run-mode': '실행 모드', 'run-mode-standard': '표준 애플리케이션', 'run-mode-tray': '트레이 애플리케이션', 'run-mode-hide-tray': '트레이 응용 프로그램 숨기기', 'tray-speedometer': '메뉴 막대 트레이에 실시간 속도가 표시됩니다', 'show-progress-bar': '다운로드 진행률 막대기 보이기', 'language': '언어', 'change-language': '언어 변경', 'hide-app-menu': '앱 메뉴 숨기기 (Windows 및 Linux)', 'proxy': '프록시', 'enable-proxy': '프록시 사용', 'proxy-bypass-input-tips': '프록시 설정을 우회할 호스트 및 도메인 (한 줄에 하나)', 'proxy-scope-download': '다운로드', 'proxy-scope-update-app': '애플리케이션 업데이트', 'proxy-scope-update-trackers': '추적기 업데이트', 'proxy-tips': '프록시 매뉴얼 보기', 'bt-tracker': '트래커 서버', 'bt-tracker-input-tips': '트래커 서버 (한 줄에 하나)', 'bt-tracker-tips': '권장: ', 'sync-tracker-tips': '동기화', 'auto-sync-tracker': '매일 자동으로 트래커 목록 업데이트', 'port': '청취 포트', 'bt-port': 'BT 청취 포트', 'dht-port': 'DHT 청취 포트', 'security': '보안', 'rpc': 'RPC', 'rpc-listen-port': 'RPC 청취 포트', 'rpc-secret': 'RPC 비밀', 'rpc-secret-tips': 'RPC 비밀 매뉴얼 보기', 'developer': '개발자', 'user-agent': 'User-Agent', 'mock-user-agent': '모의 사용자 에이전트', 'aria2-conf-path': '내장 된 aria2.conf 경로', 'app-log-path': '앱 로그 경로', 'download-session-path': '다운로드 세션 경로', 'session-reset': '다운로드 세션 초기화', 'session-reset-confirm': '다운로드 세션을 초기화하시겠습니까?', 'factory-reset': '공장 초기화', 'factory-reset-confirm': '초기 설정으로 되돌리시겠습니까?', 'lab-warning': '⚠️ 실험실 기능을 사용 설정하면 앱 충돌 또는 데이터 손실이 발생할 수 있으므로 신중히 결정하십시오!', 'download-protocol': '프로토콜', 'protocols-default-client': '다음 프로토콜의 기본 클라이언트로 설정', 'protocols-magnet': '마그넷 [ magnet:// ]', 'protocols-thunder': '썬더 [ thunder:// ]', 'browser-extensions': '확장 프로그램', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': '커뮤니티에서 제공하는, ', 'baidu-exporter-help': '이곳을 클릭하여 사용법 확인', 'auto-update': '자동 업데이트', 'auto-check-update': '자동으로 업데이트 확인', 'last-check-update-time': '마지막 업데이트 확인 시간', 'not-saved': '설정이 저장되지 않았습니다', 'not-saved-confirm': '수정된 설정이 손실됩니다. 나가시겠습니까?' } ================================================ FILE: src/shared/locales/ko/subnav.js ================================================ export default { 'task-list': '작업', 'preferences': '설정' } ================================================ FILE: src/shared/locales/ko/task.js ================================================ export default { 'active': '다운로드 중', 'waiting': '대기 중', 'stopped': '중단됨', 'new-task': '새 작업', 'new-bt-task': '새 BT 작업', 'open-file': '토렌트 파일 열기...', 'uri-task': 'URL', 'torrent-task': '토렌트', 'uri-task-tips': '한 줄에 작업 URL 하나 (마그넷 지원)', 'thunder-link-tips': '도움말: 디코딩 후 썬더 링크가 다운로드되지 않을 수 있습니다', 'new-task-uris-required': '하나 이상의 유효한 리소스 URL을 입력하십시오', 'new-task-torrent-required': '토렌트 파일을 선택하십시오', 'file-name': '파일 이름', 'file-extension': '파일 확장자', 'file-size': '파일 크기', 'file-completed-size': '전체 크기', 'selected-files-sum': '파일 {{selectedFilesCount}}개 선택됨, 총 {{selectedFilesTotalSize}}', 'select-at-least-one': '하나 이상의 파일을 선택하십시오', 'task-gid': 'GID', 'task-name': '작업 이름', 'task-out': '이름 변경', 'task-out-tips': '선택', 'task-split': '분할', 'task-dir': '폴더', 'pause-task': '작업 일시정지', 'task-ua': 'UA', 'task-user-agent': '사용자 에이전트', 'task-authorization': '권한 부여', 'task-referer': '리퍼러', 'task-cookie': '쿠키', 'task-proxy': '프록시', 'task-error-info': '오류', 'task-piece': '조각', 'task-piece-length': '조각 크기', 'task-num-pieces': '조각', 'task-bittorrent-info': '토렌트 정보', 'task-info-hash': '해시', 'task-bittorrent-creation-date': '제작일', 'task-bittorrent-comment': '코멘트', 'task-progress-info': '진행', 'task-status': '상태', 'task-num-seeders': '시더', 'task-connections': '연결', 'task-file-size': '크기', 'task-download-speed': '다운로드 속도', 'task-upload-speed': '업로드 속도', 'task-download-length': '다운로드됨', 'task-upload-length': '업로드됨', 'task-ratio': '비율', 'task-peer-host': '호스트', 'task-peer-ip': 'IP', 'task-peer-client': '클라이언트', 'navigate-to-downloading': '다운로드로 이동', 'show-advanced-options': '고급 옵션', 'copyright-warning': '저작권 경고', 'copyright-warning-message': '다운로드하려는 파일은 저작권이 있는 오디오 또는 비디오일 수 있으므로 사용 권한이 있는지 확인하십시오.', 'copyright-yes': '예, 권한이 있습니다', 'copyright-no': '아니요, 권한이 없습니다', 'copyright-error-message': '저작권 문제로 인해 작업 추가 실패', 'pause-task-success': '"{{taskName}}" 작업 일시정지 성공', 'pause-task-fail': '"{{taskName}}" 작업 일시정지 실패', 'resume-task': '작업 재개', 'resume-task-success': '"{{taskName}}" 작업 재개 성공', 'resume-task-fail': '"{{taskName}}" 작업 재개 실패', 'delete-task': '작업 삭제', 'delete-selected-tasks': '선택된 작업 삭제', 'delete-task-confirm': '"{{taskName}}" 다운로드 작업을 삭제하시겠습니까?', 'batch-delete-task-confirm': '{{count}}개의 다운로드 작업을 일괄 삭제하시겠습니까?', 'delete-task-label': '파일을 같이 삭제', 'delete-task-success': '"{{taskName}}" 작업 삭제 성공', 'delete-task-fail': '"{{taskName}}" 작업 삭제 실패', 'remove-task-file-fail': '작업 파일을 제거하지 못했으므로 수동으로 제거하십시오.', 'remove-task-config-file-fail': '작업 구성 파일을 제거하지 못했으므로 수동으로 제거하십시오.', 'move-task-up': '작업을 위로 이동', 'move-task-down': '작업을 아래로 이동', 'pause-all-task': '모든 작업 일시정지', 'pause-all-task-success': '모든 작업 일시정지 성공', 'pause-all-task-fail': '모든 작업 일시정지 실패', 'resume-all-task': '모든 작업 재개', 'resume-all-task-success': '모든 작업 재개 성공', 'resume-all-task-fail': '모든 작업 재개 실패', 'select-all-task': '모든 작업 선택', 'clear-recent-tasks': '최근 작업 지우기', 'purge-record': '작업 기록 삭제', 'purge-record-success': '작업 기록 삭제 성공', 'purge-record-fail': '작업 기록 삭제 실패', 'batch-delete-task-success': '작업 일괄 삭제 성공', 'batch-delete-task-fail': '작업 일괄 삭제 실패', 'refresh-list': '작업 목록 새로 고침', 'no-task': '현재 다운로드 없음', 'copy-link': '링크 복사', 'copy-link-success': '링크 복사 성공', 'remove-record': '작업 기록 제거', 'remove-record-confirm': '"{{taskName}}" 다운로드 기록을 제거하시겠습니까?', 'remove-record-label': '파일을 같이 삭제', 'remove-record-success': '"{{taskName}}" 작업 기록 제거 성공', 'remove-record-fail': '"{{taskName}}" 작업 기록 제거 실패', 'show-in-folder': '작업 폴더 표시', 'file-not-exist': '파일이 존재하지 않거나 삭제되었습니다', 'file-path-error': '파일 경로 오류', 'opening-task-message': '"{{taskName}}" 여는 중...', 'get-task-name': '작업 이름 가져오기...', 'remaining-prefix': '남음', 'select-torrent': '토렌트 파일을 여기로 드래그하거나 클릭해서 선택하십시오', 'task-detail-title': '작업 정보', 'task-info-dialog-title': '{{title}} 정보', 'download-start-message': '{{taskName}} 다운로드 시작', 'download-pause-message': '{{taskName}} 다운로드 일시정지', 'download-stop-message': '{{taskName}} 다운로드 중지', 'download-error-message': '{{taskName}} 다운로드 오류 발생', 'download-complete-message': '{{taskName}} 다운로드 완료', 'download-complete-notify': '다운로드 완료', 'bt-download-complete-message': '{{taskName}} 다운로드 완료, 배포 중', 'bt-download-complete-notify': 'BT 다운로드 완료, 배포 중...', 'bt-download-complete-tips': '도움말: 작업을 중지하여 배포를 종료할 수 있습니다', 'bt-stopping-seeding-tip': '배포를 중지하면 연결을 끊는 데 시간이 걸립니다. 잠시 기다려 주십시오...', 'download-fail-message': '{{taskName}} 다운로드 실패', 'download-fail-notify': '다운로드 실패' } ================================================ FILE: src/shared/locales/ko/window.js ================================================ export default { 'reload': '다시 불러오기', 'close': '닫기', 'minimize': '최소화', 'zoom': '확대', 'toggle-fullscreen': '전체 화면', 'front': '모두 앞으로 가져오기' } ================================================ FILE: src/shared/locales/nb/about.js ================================================ export default { 'engine-version': 'Motorversjon', 'license': 'Tillatelse', 'about': 'Om', 'release': 'Utgivelser', 'support': 'Brukerstøtte' } ================================================ FILE: src/shared/locales/nb/app.js ================================================ export default { 'task-list': 'Oppgaver', 'add-task': 'Legg til oppgave', 'about': 'Om Motrix', 'preferences': 'Preferanser...', 'check-for-updates': 'Se etter oppdateringer...', 'check-updates-now': 'Sjekk nå', 'checking-for-updates': 'Ser etter oppdateringer...', 'check-for-updates-title': 'Se etter oppdateringer', 'update-available-message': 'En nyere versjon av Motrix er tilgjengelig, oppdater nå?', 'update-not-available-message': 'Du er oppdatert!', 'update-downloaded-message': 'Klar til å installere...', 'update-error-message': 'Oppdateringsfeil', 'engine-damaged-message': 'Motoren er skadet. Vennligst installer den på nytt :(', 'engine-missing-message': 'Motoren mangler. Vennligst installer den på nytt :(', 'system-error-title': 'Systemfeil', 'system-error-message': 'Oppstart av applikasjon mislyktes: {{message}}', 'hide': 'Skjul Motrix', 'hide-others': 'Skjul andre', 'unhide': 'Vis alt', 'show': 'Vis Motrix', 'quit': 'Avslutt Motrix', 'under-development-message': 'Beklager, denne funksjonen er under utvikling...', 'yes': 'Ja', 'no': 'Nei', 'save': 'Lagre', 'reset': 'Nullstill', 'cancel': 'Avbryt', 'submit': 'Send inn', 'gt1d': '> 1 dag', 'hour': 't', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/nb/edit.js ================================================ export default { 'undo': 'Angre', 'redo': 'Utfør igjen', 'cut': 'Klipp', 'copy': 'Kopier', 'paste': 'Lim inn', 'delete': 'Fjern', 'select-all': 'Velg alle' } ================================================ FILE: src/shared/locales/nb/help.js ================================================ export default { 'official-website': 'Motrix\' nettside', 'manual': 'Brukerhåndbok', 'release-notes': 'Utgivelsesnotater...', 'report-problem': 'Rapporter problem', 'toggle-dev-tools': 'Vis/skjul utviklerverktøy' } ================================================ FILE: src/shared/locales/nb/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/nb/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Fil', 'task': 'Oppgave', 'edit': 'Rediger', 'window': 'Vindu', 'help': 'Hjelp' } ================================================ FILE: src/shared/locales/nb/preferences.js ================================================ export default { 'basic': 'Grunnleggende', 'advanced': 'Avansert', 'lab': 'Lab', 'save': 'Lagre og bruk', 'save-success-message': 'Lagre innstillinger vellykket', 'save-fail-message': 'Lagring av innstillinger mislyktes', 'discard': 'Forkast', 'startup': 'Oppstart', 'open-at-login': 'Åpne ved pålogging', 'keep-window-state': 'Behold vinduets størrelse og posisjon når du går ut', 'auto-resume-all': 'Gjenoppta automatisk alle uferdige oppgaver', 'default-dir': 'Standard bane', 'mas-default-dir-tips': 'På grunn av begrensninger for tillatelse av sandkasse i App Store, anbefales standard nedlastningskatalog å settes til ~ / Nedlastinger', 'transfer-settings': 'Overføring', 'transfer-speed-upload': 'Opplastingsgrense', 'transfer-speed-download': 'Nedlastingsgrense', 'transfer-speed-unlimited': 'Ubegrenset', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Lagre magnetkobling som torrentfil', 'bt-auto-download-content': 'Last ned Magnet og Torrent-innholdet automatisk', 'bt-force-encryption': 'TVANGSKRYPTERING AV BT', 'keep-seeding': 'Fortsett seeding til du stopper manuelt', 'seed-ratio': 'Seed-forhold', 'seed-time': 'Seed-tid', 'seed-time-unit': 'minutter', 'task-manage': 'Oppgavebehandling', 'max-concurrent-downloads': 'Maksimum aktive oppgaver', 'max-connection-per-server': 'Maksimal tilkobling per server', 'new-task-show-downloading': 'Vis nedlasting automatisk etter at oppgaven er lagt til', 'no-confirm-before-delete-task': 'Ingen bekreftelse er nødvendig før du sletter oppgaven', 'continue': 'Fortsette', 'task-completed-notify': 'Varsling etter nedlasting er fullført', 'auto-purge-record': 'Tøm nedlastingsposter automatisk når du avslutter appen', 'ui': 'UI', 'appearance': 'Utseende', 'theme-auto': 'Auto', 'theme-light': 'Lys', 'theme-dark': 'Mørk', 'auto-hide-window': 'Skjul automatisk vindu', 'run-mode': 'Applikasjonsmodus', 'run-mode-standard': 'Standard applikasjon', 'run-mode-tray': 'Tray-applikasjon', 'run-mode-hide-tray': 'Skjul statusfeltprogram', 'tray-speedometer': 'Menylinje viser sanntidshastighet', 'show-progress-bar': 'Vis fremgangslinjen for nedlastning', 'language': 'Språk', 'change-language': 'Skifte språk', 'hide-app-menu': 'Skjul appmeny (kun Windows og Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Aktiver proxy', 'proxy-bypass-input-tips': 'Omgå proxy-innstillinger for disse vertene og domenene, en per linje', 'proxy-scope-download': 'Nedlasting', 'proxy-scope-update-app': 'Oppdater applikasjonen', 'proxy-scope-update-trackers': 'Oppdater sporingskapsler', 'proxy-tips': 'Se Proxy Manual', 'bt-tracker': 'Tracker-servere', 'bt-tracker-input-tips': 'Tracker-servere, en per linje', 'bt-tracker-tips': 'Anbefalt:', 'sync-tracker-tips': 'Synkroniser', 'auto-sync-tracker': 'Oppdater trackerlisten hver dag automatisk', 'port': 'Lytt på porter', 'bt-port': 'BT-lytteport', 'dht-port': 'DHT-lytteport', 'security': 'Sikkerhet', 'rpc': 'RPC', 'rpc-listen-port': 'RPC lytteport', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Se RPC Secret Manual', 'developer': 'Utvikler', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Innebygd aria2.conf-sti', 'app-log-path': 'Apploggbane', 'download-session-path': 'Last ned øktstien', 'session-reset': 'Tilbakestill nedlastingsøkten', 'session-reset-confirm': 'Er du sikker på at du vil tilbakestille nedlastingsøkten?', 'factory-reset': 'Fabrikkinnstillinger', 'factory-reset-confirm': 'Er du sikker på at du vil gå tilbake til fabrikkinnstillingene?', 'lab-warning': '⚠️ Aktivering av laboratoriefunksjoner kan føre til appkrasj eller tap av data, gjør det på egen risiko!', 'download-protocol': 'Protokoller', 'protocols-default-client': 'Angi som standardklient for følgende protokoller', 'protocols-magnet': 'Magnet [magnet: //]', 'protocols-thunder': 'Torden [torden: //]', 'browser-extensions': 'Utvidelser', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Levert av samfunnet,', 'baidu-exporter-help': 'Klikk her for bruk', 'auto-update': 'Automatisk oppdatering', 'auto-check-update': 'Sjekk automatisk for oppdatering', 'last-check-update-time': 'Siste gang sjekket for oppdatering', 'not-saved': 'Preferansene er ikke lagret', 'not-saved-confirm': 'De endrede preferansene vil gå tapt, er du sikker på at du vil forlate?' } ================================================ FILE: src/shared/locales/nb/subnav.js ================================================ export default { 'task-list': 'Oppgaver', 'preferences': 'Innstillinger' } ================================================ FILE: src/shared/locales/nb/task.js ================================================ export default { 'active': 'Laster ned', 'waiting': 'Venter', 'stopped': 'Stoppet', 'new-task': 'Ny oppgave', 'new-bt-task': 'Ny BT-oppgave', 'open-file': 'Åpne torrentfil...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Én oppgave-URL per linje (støtter magnet)', 'thunder-link-tips': 'Tips: Tordenlinker kan ikke lastes ned etter dekoding', 'new-task-uris-required': 'Angi minst én gyldig ressurs-URL', 'new-task-torrent-required': 'Velg en torrentfil', 'file-name': 'Navn', 'file-extension': 'Filetternavn', 'file-size': 'Størrelse', 'file-completed-size': 'Fullført', 'selected-files-sum': 'Valgt: {{selectedFilesCount}} filer, total størrelse {{selectedFilesTotalSize}}', 'select-at-least-one': 'Velg minst én fil', 'task-gid': 'GID', 'task-name': 'Oppgavenavn', 'task-out': 'Gi nytt navn', 'task-out-tips': 'Valgfri', 'task-split': 'Deler', 'task-dir': 'Lagre til', 'pause-task': 'Pause oppgave', 'task-ua': 'UA', 'task-user-agent': 'Brukeragent', 'task-authorization': 'Autorisasjon', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Fullmektig', 'task-error-info': 'Feil', 'task-piece': 'Stykke', 'task-piece-length': 'Stykkestørrelse', 'task-num-pieces': 'Stykker', 'task-bittorrent-info': 'Torrentinformasjon', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Opprettelsesdato', 'task-bittorrent-comment': 'Kommentar', 'task-progress-info': 'Framgang', 'task-status': 'Status', 'task-num-seeders': 'Seeders', 'task-connections': 'Tilkoblinger', 'task-file-size': 'Størrelse', 'task-download-speed': 'Nedlastningshastighet', 'task-upload-speed': 'Opplastningshastighet', 'task-download-length': 'Lastet ned', 'task-upload-length': 'Lastet opp', 'task-ratio': 'Forhold', 'task-peer-host': 'Vert', 'task-peer-ip': 'IP', 'task-peer-client': 'Klient', 'navigate-to-downloading': 'Naviger til Nedlasting', 'show-advanced-options': 'Avanserte instillinger', 'copyright-warning': 'Copyright Advarsel', 'copyright-warning-message': 'Filen du vil laste ned kan være lydbeskyttet eller beskyttet av copyright, vær sikker på at du har tillatelse til å få tilgang til den.', 'copyright-yes': 'Ja, jeg har tillatelse', 'copyright-no': 'Nei, jeg har ikke tillatelse', 'copyright-error-message': 'Kunne ikke legge til oppgave på grunn av problem med opphavsrett', 'pause-task-success': 'Oppgave "{{taskName}}" er midlertidig stoppet', 'pause-task-fail': 'Kunne ikke pause oppgaven "{{taskName}}"', 'resume-task': 'Fortsett oppgave', 'resume-task-success': 'Oppgaven "{{taskName}}" ble gjenopptatt', 'resume-task-fail': 'Kunne ikke gjenoppta oppgaven "{{taskName}}"', 'delete-task': 'Slett oppgave', 'delete-selected-tasks': 'Slett valgte oppgaver', 'delete-task-confirm': 'Er du sikker på at du vil fjerne nedlastingsoppgaven "{{taskName}}"?', 'batch-delete-task-confirm': 'Er du sikker på at du vil fjerne {{count}} nedlastingsoppgaver i batch?', 'delete-task-label': 'Slett med filer', 'delete-task-success': 'Oppgave "{{taskName}}" er slettet', 'delete-task-fail': 'Kunne ikke slette oppgaven "{{taskName}}"', 'remove-task-file-fail': 'Kunne ikke slette oppgavefil (er), vennligst slett dem manuelt', 'remove-task-config-file-fail': 'Kunne ikke slette oppgavekonfigurasjonsfilen. Slett den manuelt', 'move-task-up': 'Flytt oppgaven', 'move-task-down': 'Flytt oppgave ned', 'pause-all-task': 'Sett alle oppgaver på pause', 'pause-all-task-success': 'Pause alle oppgaver', 'pause-all-task-fail': 'Kunne ikke stoppe alle oppgaver midlertidig', 'resume-all-task': 'Fortsett alle oppgaver', 'resume-all-task-success': 'Gjenopptatt alle oppgaver', 'resume-all-task-fail': 'Kunne ikke gjenoppta alle oppgaver', 'select-all-task': 'Velg alle oppgaver', 'clear-recent-tasks': 'Fjern siste oppgaver', 'purge-record': 'Tøm oppgavelogg', 'purge-record-success': 'Tømming av oppgavelogger var vellykket', 'purge-record-fail': 'Kunne ikke tømme oppgavelogger', 'batch-delete-task-success': 'Slett oppgaver i batch', 'batch-delete-task-fail': 'Kunne ikke slette oppgaver i batch', 'refresh-list': 'Oppdater oppgaveliste', 'no-task': 'Det er ingen aktuelle oppgaver', 'copy-link': 'Kopier link', 'copy-link-success': 'Koblingen ble kopiert', 'remove-record': 'Fjern oppgaveliste', 'remove-record-confirm': 'Er du sikker på at du vil fjerne nedlastingsposten for "{{taskName}}"?', 'remove-record-label': 'Slett med filer', 'remove-record-success': 'Nedlastingsposten for "{{taskName}}" er fjernet', 'remove-record-fail': 'Kunne ikke fjerne nedlastingsposten for "{{taskName}}"', 'show-in-folder': 'Vis oppgave i mappe', 'file-not-exist': 'Målfilen eksisterer ikke eller er slettet', 'file-path-error': 'Filsti feil', 'opening-task-message': 'Åpner "{{taskName}}"...', 'get-task-name': 'Henter oppgavens navn...', 'remaining-prefix': 'Gjenstående', 'select-torrent': 'Dra torrentfil hit, eller klikk for å velge', 'task-detail-title': 'Oppgavedetaljer', 'task-info-dialog-title': '{{title}} Detaljer', 'download-start-message': 'Lastet ned {{taskName}}', 'download-pause-message': 'Nedlasting av {{taskName}} er midlertidig stoppet', 'download-stop-message': 'Nedlastingen stoppet ikke {{taskName}}', 'download-error-message': 'Det oppstod en feil under nedlasting av {{taskName}}', 'download-complete-message': 'Nedlastingen er fullført {{taskName}}', 'download-complete-notify': 'Last ned fullført', 'bt-download-complete-message': 'Nedlastingen er fullført, {{taskName}}, seeding', 'bt-download-complete-notify': 'BT-nedlasting fullført, seeding...', 'bt-download-complete-tips': 'Tips: Du kan stoppe en oppgave for å avslutte seedingen', 'bt-stopping-seeding-tip': 'Når du stopper seedingen, vil det ta litt tid å koble fra, vent...', 'download-fail-message': 'Kunne ikke laste ned {{taskName}}', 'download-fail-notify': 'Nedlasting feilet' } ================================================ FILE: src/shared/locales/nb/window.js ================================================ export default { 'reload': 'Last inn på nytt', 'close': 'Lukk', 'minimize': 'Minimer', 'zoom': 'Forstør', 'toggle-fullscreen': 'Gå til fullskjerm', 'front': 'Send alle til forgrunnen' } ================================================ FILE: src/shared/locales/nl/about.js ================================================ export default { 'engine-version': 'Engine Versie', 'license': 'Licentie', 'about': 'Over', 'release': 'Versie', 'support': 'Ondersteuning' } ================================================ FILE: src/shared/locales/nl/app.js ================================================ export default { 'task-list': 'Taken', 'add-task': 'Taak toevoegen', 'about': 'Over Motrix', 'preferences': 'Voorkeuren...', 'check-for-updates': 'Naar updates zoeken...', 'check-updates-now': 'Jetzt prüfen', 'checking-for-updates': 'Updates zoeken ...', 'check-for-updates-title': 'Naar updates zoeken...', 'update-available-message': 'Er is een nieuwe versie van Motrix beschikbaar, nu updaten?', 'update-not-available-message': 'U heeft de laatste versie!', 'update-downloaded-message': 'Klaar om te installeren...', 'update-error-message': 'Update fout', 'engine-damaged-message': 'De engine is beschadigd, opnieuw installeren a.u.b. :(', 'engine-missing-message': 'De engine mist, opnieuw installeren a.u.b. :(', 'system-error-title': 'Systeemfout', 'system-error-message': 'Applicatie kon niet worden gestart: {{message}}', 'hide': 'Motrix verbergen', 'hide-others': 'Verberg anderen', 'unhide': 'Laat alles zien', 'show': 'Laat Motrix zien', 'quit': 'Motrix afsluiten', 'under-development-message': 'Sorry, deze functie is nog in ontwikkeling...', 'yes': 'Ja', 'no': 'Nee', 'save': 'Opslaan', 'reset': 'Resetten', 'cancel': 'Annuleren', 'submit': 'Verzenden', 'gt1d': '> 1 dag', 'hour': 'u', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/nl/edit.js ================================================ export default { 'undo': 'Ongedaan maken', 'redo': 'Opnieuw doen', 'cut': 'Knipplen', 'copy': 'Kopiëren', 'paste': 'Plakken', 'delete': 'Verwijderen', 'select-all': 'Alles selecteren' } ================================================ FILE: src/shared/locales/nl/help.js ================================================ export default { 'official-website': 'Motrix Website', 'manual': 'Handleiding', 'release-notes': 'Versie informatie...', 'report-problem': 'Probleem melden', 'toggle-dev-tools': 'Ontwikkelaarstools aan/uit-schakelen' } ================================================ FILE: src/shared/locales/nl/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/nl/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Bestand', 'task': 'Taak', 'edit': 'Bewerken', 'window': 'Venster', 'help': 'Help' } ================================================ FILE: src/shared/locales/nl/preferences.js ================================================ export default { 'basic': 'Basis', 'advanced': 'Geavanceerd', 'lab': 'Experimenteel', 'save': 'Opslaan & Toepassen', 'save-success-message': 'Instellingen succesvol opgeslagen', 'save-fail-message': 'Instellingen opslaan mislukt', 'discard': 'Weggooien', 'startup': 'Opstarten', 'open-at-login': 'Bij login openen', 'keep-window-state': 'Behoud grootte en positie van venster bij afsluiten', 'auto-resume-all': 'Automatisch alle niet voltooide taken hervatten', 'default-dir': 'Standaard map', 'mas-default-dir-tips': 'Door de sandbox permissie restricties van de App Store wordt aangeraden om ~/Downloads als standaard downloadmap in te stellen', 'transfer-settings': 'Tranmissie', 'transfer-speed-upload': 'Upload limiet', 'transfer-speed-download': 'Download limiet', 'transfer-speed-unlimited': 'Onbeperkt', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Magnetlink opslaan als torrent bestand', 'bt-auto-download-content': 'Automatisch magnet en torrent content downloaden', 'bt-force-encryption': 'BT dwngwyrddysgu gorfodol', 'keep-seeding': 'Blijf seeden tot het het handmatig wordt gestopt', 'seed-ratio': 'Seed ratio', 'seed-time': 'Starttijd', 'seed-time-unit': 'Protocol', 'task-manage': 'Taakbeheer', 'max-concurrent-downloads': 'Maximaal actieve taken', 'max-connection-per-server': 'Maximale verbindingen per server', 'new-task-show-downloading': 'Automatisch downloaden tonen na toevoegen taak', 'no-confirm-before-delete-task': 'Geen bevestiging nodig voor verwijderen taak', 'continue': 'Verder gaan', 'task-completed-notify': 'Notificatie wanneer download klaar is', 'auto-purge-record': 'Automatisch wissen van downloadrecords bij verlaten van app', 'ui': 'UI', 'appearance': 'Uiterlijk', 'theme-auto': 'Automatisch', 'theme-light': 'Licht', 'theme-dark': 'Donker', 'auto-hide-window': 'Venster automatisch verbergen', 'run-mode': 'Uitvoeren als', 'run-mode-standard': 'Standaard uitvoering', 'run-mode-tray': 'Toepassing voor systeemvak', 'run-mode-hide-tray': 'Toepassing in systeemvak verbergen', 'tray-speedometer': 'Menubalk toon real-time snelheid', 'show-progress-bar': 'Downloadvoortgangsbalk weergeven', 'language': 'Taal', 'change-language': 'Taal veranderen', 'hide-app-menu': 'App menu verbergen (alleen Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Proxy aanzetten', 'proxy-bypass-input-tips': 'Proxy instellingen omzeilen voor deze hosts en domeinen (één per regel)', 'proxy-scope-download': 'Downloaden', 'proxy-scope-update-app': 'Toepassing bijwerken', 'proxy-scope-update-trackers': 'Trackers bijwerken', 'proxy-tips': 'Proxy handleiding bekijken', 'bt-tracker': 'Tracker Servers', 'bt-tracker-input-tips': 'Tracker Servers, één per regel', 'bt-tracker-tips': 'Aanbevolen:', 'sync-tracker-tips': 'Synchronisieren', 'auto-sync-tracker': 'Update de tracker lijst elke dag automatisch', 'port': 'Luister poort', 'bt-port': 'BT luister poort', 'dht-port': 'DHT luister poort', 'security': 'Beveiliging', 'rpc': 'RPC', 'rpc-listen-port': 'RPC Luisterpoort', 'rpc-secret': 'RPC geheim', 'rpc-secret-tips': 'bekijk RPC geheim handleiding', 'developer': 'Ontwikkelaar', 'user-agent': 'User-Agent', 'mock-user-agent': 'User-Agent nabootsen', 'aria2-conf-path': 'Ingebed pad voor aria2.conf', 'app-log-path': 'Applicatie log pad', 'download-session-path': 'Downloadsessie pad', 'session-reset': 'Reset download sessie', 'session-reset-confirm': 'Weet u zeker dat u de downloadsessie instellingen wilt resetten?', 'factory-reset': 'Fabrieksinstellingen', 'factory-reset-confirm': 'Weet u zeker dat u terug wilt gaan naar fabrieksinstellingen?', 'lab-warning': '⚠️ Het activeren van experimentele functies kan zorgen voor onstabiliteit of dataverlies. Kies op eigen risico!', 'download-protocol': 'Protocollen', 'protocols-default-client': 'Als Standardclient voor de volgende protocollen instellen', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Donner [ thunder:// ]', 'browser-extensions': 'Browserextensies', 'baidu-exporter': 'Baidu Exporter', 'browser-extensions-tips': 'Beschikbaar gesteld door de community', 'baidu-exporter-help': 'Klik hier voor hulp', 'auto-update': 'Auto-Update', 'auto-check-update': 'Automatische naar updates zoeken', 'last-check-update-time': 'Laatste keer gecontroleerd op updates' } ================================================ FILE: src/shared/locales/nl/subnav.js ================================================ export default { 'task-list': 'Taken', 'preferences': 'Voorkeuren' } ================================================ FILE: src/shared/locales/nl/task.js ================================================ export default { 'active': 'Actief', 'waiting': 'Wachten', 'stopped': 'Gestopt', 'new-task': 'Nieuwe taak', 'new-bt-task': 'Nieuwe BT taak', 'open-file': 'Torrentbestand openen...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Een taak URL per regel (ondersteunt magnet)', 'thunder-link-tips': 'Tip: Thunder links zijn mogelijk niet downloadbaar na decodering', 'new-task-uris-required': 'Geef a.u.b. tenminste een geldige URL', 'new-task-torrent-required': 'Kies a.u.b. een torrentbestand', 'file-name': 'Bestandsnaam', 'file-extension': 'Bestandstype', 'file-size': 'Bestandsgrootte', 'file-completed-size': 'Gedownload', 'selected-files-sum': 'Geselecteerd: {{selectedFilesCount}} bestanden, totale grootte {{selectedFilesTotalSize}}', 'select-at-least-one': 'Kies a.u.b. ten minste 1 bestand', 'task-gid': 'GID', 'task-name': 'Taak naam', 'task-out': 'Hernoemen', 'task-out-tips': 'Optioneel', 'task-split': 'Splits', 'task-dir': 'Map', 'pause-task': 'Taak pauzeren', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorisatie', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Error', 'task-piece': 'Deel', 'task-piece-length': 'Deelgrootte', 'task-num-pieces': 'Delen', 'task-bittorrent-info': 'Torrent Info', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Aanmaakdatum', 'task-bittorrent-comment': 'Opmerking', 'task-progress-info': 'Voortgang', 'task-status': 'Status', 'task-num-seeders': 'Seerders', 'task-connections': 'Verbindingen', 'task-file-size': 'Grootte', 'task-download-speed': 'Downloadsnelheid', 'task-upload-speed': 'Uploadsnelheid', 'task-download-length': 'Gedownload', 'task-upload-length': 'Geupload', 'task-ratio': 'Verhouding', 'task-peer-host': 'Host', 'task-peer-ip': 'IP', 'task-peer-client': 'Client', 'navigate-to-downloading': 'Navigeer naar downloaden', 'show-advanced-options': 'Geavanceerde opties', 'copyright-warning': 'Copyright waarschuwing', 'copyright-warning-message': 'Het bestand dat u wilt te downloaden kan onder het auteursrecht vallen. Controleer of u in het bezit bent van de benodigde licentie', 'copyright-yes': 'Ja, ik heb toestemming', 'copyright-no': 'Nee, ik heb geen toestemming', 'copyright-error-message': 'Kon taak niet toevoegen vanwege copyright probleem', 'pause-task-success': 'Taak "{{taskName}}" succesvol gepauzeerd', 'pause-task-fail': 'Taak "{{taskName}}" pauzeren mislukt', 'resume-task': 'Taak hervatten', 'resume-task-success': 'Taak "{{taskName}}" succesvol hervat', 'resume-task-fail': 'Taak "{{taskName}}" hervatten mislukt', 'delete-task': 'Taak verwijderen', 'delete-selected-tasks': 'Geselecteerde taken verwijderen', 'delete-task-confirm': 'Weet u zeker dat u de download van "{{taskName}}" wilt verwijderen?', 'batch-delete-task-confirm': 'Weet u zeker dat u {{count}} taken in 1 keer verwijderen?', 'delete-task-label': 'Bestand ook verwijderen', 'delete-task-success': 'Taak "{{taskName}}" succesvol verwijderd', 'delete-task-fail': 'Taak "{{taskName}}" verwijderen mislukt', 'remove-task-file-fail': 'Bestand verwijderen mislukt, gelieve handmatig verwijderen', 'remove-task-config-file-fail': 'Taakconfiguratiebestand verwijderen mislukt, gelieve handmatig verwijderenn', 'move-task-up': 'Taak naar boven verplaatsen', 'move-task-down': 'Taak naar beneden verplaatsen', 'pause-all-task': 'Alle taken pauzeren', 'pause-all-task-success': 'Alle taken succesvol gepauzeerd', 'pause-all-task-fail': 'De taken konden niet worden gepauzeerd', 'resume-all-task': 'Alle taken hervatten', 'resume-all-task-success': 'Alle taken succesvol hervat', 'resume-all-task-fail': 'Taken hervatten mislukt', 'select-all-task': 'Selecteer alle taken', 'clear-recent-tasks': 'Verwijder recente taken', 'purge-record': 'Taakrecord verwijderen', 'purge-record-success': 'Taakrecord succesvol verwijderd', 'purge-record-fail': 'Taakrecord kon niet worden verwijderd', 'batch-delete-task-success': 'Taken succesvol in batch verwijderd', 'batch-delete-task-fail': 'Taken in batch konden niet worden verwijderd', 'refresh-list': 'Takenlijst verversen', 'no-task': 'Geen taken actief', 'copy-link': 'Link kopiëren', 'copy-link-success': 'Link succesvol gekopieerd', 'remove-record': 'Verwijder taak record', 'remove-record-confirm': 'Weet u zeker dat u het downloadrecord wilt verwijderen voor "{{taskName}}"?', 'remove-record-label': 'Bestand ook verwijderen', 'remove-record-success': 'Taakrecord voor "{{taskName}}" succesvol verwijderd', 'remove-record-fail': 'Taakrecord voor "{{taskName}}" kon niet verwijderd worden', 'show-in-folder': 'Taak in map bekijken', 'file-not-exist': 'Doelbestand bestaad niet of is verwijderd', 'file-path-error': 'Bestandpad fout', 'opening-task-message': '"{{taskName}}" openen...', 'get-task-name': 'Taak naam ophalen...', 'remaining-prefix': 'Resterend', 'select-torrent': 'Sleep torrentbestand hier naartoe of klik om te selecteren', 'task-info-dialog-title': '{{title}} Details', 'download-start-message': 'Start downloaden van {{taskName}}', 'download-pause-message': 'Pauzeeer downloaden van {{taskName}}', 'download-stop-message': 'Download van{{taskName}} gestopt', 'download-error-message': 'Fout bij downloaden van {{taskName}} opgetreden', 'download-complete-message': 'Download van {{taskName}} geannuleerd', 'download-complete-notify': 'Download geannuleerd', 'bt-download-complete-message': 'Download van {{taskName}} voltooid, seeden...', 'bt-download-complete-notify': 'BT Download voltooid, seeden...', 'bt-download-complete-tips': 'Tip: U kunt een taak stoppen om het seeden de beëindigen', 'bt-stopping-seeding-tip': 'Seeden stoppen. Het duurt even voor de verbindingen zijn verbroken, even geduld...', 'download-fail-message': 'Download van {{taskName}} mislukt', 'download-fail-notify': 'Download mislukt' } ================================================ FILE: src/shared/locales/nl/window.js ================================================ export default { 'reload': 'Vervesen', 'close': 'Sluiten', 'minimize': 'Minimaliseren', 'zoom': 'Zoomen', 'toggle-fullscreen': 'Volledig scherm tonen', 'front': 'Alles naar voorgrond brengen' } ================================================ FILE: src/shared/locales/pl/about.js ================================================ export default { 'engine-version': 'Wersja silnika', 'license': 'Licencja', 'about': 'O programie', 'release': 'Wydania', 'support': 'Wsparcie' } ================================================ FILE: src/shared/locales/pl/app.js ================================================ export default { 'task-list': 'Zadania', 'add-task': 'Dodaj zadanie', 'about': 'O Motrix', 'preferences': 'Ustawienia...', 'check-for-updates': 'Sprawdź aktualizacje...', 'check-updates-now': 'Sprawdź teraz', 'checking-for-updates': 'Sprawdzanie aktualizacji ...', 'check-for-updates-title': 'Sprawdź aktualizację', 'update-available-message': 'Dostępna jest nowa wersja Motrix, czy chcesz zaktualizować program?', 'update-not-available-message': 'Masz zainstalowaną najnowszą wersję!', 'update-downloaded-message': 'Gotowe do instalacji...', 'update-error-message': 'Błąd aktualizacji', 'engine-damaged-message': 'Silnik został uszkodzony, spróbuj zreinstalować program : (', 'engine-missing-message': 'Nie można znaleźć silnika, spróbuj zreinstalować program : (', 'system-error-title': 'Błąd systemu', 'system-error-message': 'Aplikacja nie mogła zostać uruchomiona: {{message}}', 'hide': 'Schowaj Motrix', 'hide-others': 'Schowaj pozostałe', 'unhide': 'Pokaż wszystkie', 'show': 'Pokaż Motrix', 'quit': 'Opuść Motrix', 'under-development-message': 'Przepraszamy ale ta funkcja nie jest jeszcze gotowa...', 'yes': 'Tak', 'no': 'Nie', 'save': 'Zapisać', 'reset': 'Odrzucać', 'cancel': 'Anuluj', 'submit': 'Dodaj', 'gt1d': '> 1 dzień', 'hour': 'g', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/pl/edit.js ================================================ export default { 'undo': 'Cofnij', 'redo': 'Ponów', 'cut': 'Wytnij', 'copy': 'Skopiuj', 'paste': 'Wklej', 'delete': 'Usuń', 'select-all': 'Wybierz wszystkie ' } ================================================ FILE: src/shared/locales/pl/help.js ================================================ export default { 'official-website': 'Oficjalna strona internetowa', 'manual': 'Instrukcja', 'release-notes': 'Dziennik wydań...', 'report-problem': 'Zgłoś problem', 'toggle-dev-tools': 'Włącz narzędzia dla developerów' } ================================================ FILE: src/shared/locales/pl/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/pl/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Plik', 'task': 'Zadanie', 'edit': 'Edycja', 'window': 'Okno', 'help': 'Pomoc' } ================================================ FILE: src/shared/locales/pl/preferences.js ================================================ export default { 'basic': 'Podstawowe', 'advanced': 'Zaawansowane', 'lab': 'Laboratorium', 'save': 'Zapisz i zastosuj', 'save-success-message': 'Pomyślnie zapisano ustawienia', 'save-fail-message': 'Nie udało się zapisać ustawień', 'discard': 'Odrzuć', 'startup': 'Startup', 'open-at-login': 'Uruchom przy logowaniu', 'keep-window-state': 'Zachowaj wielkość i pozycję okna podczas wychodzenia', 'auto-resume-all': 'Automatycznie uruchom wszystkie zatrzymane zadania', 'default-dir': 'Domyślna ścieżka', 'mas-default-dir-tips': 'Z powodu restrykcji dostępu spowodowanych trybem piaskownicy w App Store, domyślną rekomendawaną lokalizacją pobrań jest ~/Downloads', 'transfer-settings': 'Przesyłanie', 'transfer-speed-upload': 'Limit prędkości przesyłania', 'transfer-speed-download': 'Limit prędkości pobierania', 'transfer-speed-unlimited': 'Nieograniczona', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Zapisz link magnetyczny jako plik torrent', 'bt-auto-download-content': 'Automatycznie pobieraj zawartość magnesu i torrenta', 'bt-force-encryption': 'Obrigar a criptografia forçada do BT', 'keep-seeding': 'Kontynuuj wysiew aż do zatrzymania go ręcznie', 'seed-ratio': 'Stosunek nasion', 'seed-time': 'Czas siewu', 'seed-time-unit': 'minuty', 'task-manage': 'Zarządzania zadaniami', 'max-concurrent-downloads': 'Maksymalna liczba aktywnych zadań', 'max-connection-per-server': 'Maksymalna liczba połączeń na serwer', 'new-task-show-downloading': 'Automatycznie pokaż pobieranie po dodaniu nowego zadania', 'no-confirm-before-delete-task': 'Usunięcie zadania nie będzie wymagać potwierdzenia', 'continue': 'Kontynuuj', 'task-completed-notify': 'Powiadomnie po ukończeniu zadania', 'auto-purge-record': 'Automatycznie usuń pobrane zadania po zamknięciu aplikacji', 'ui': 'UI', 'appearance': 'Wygląd', 'theme-auto': 'Auto', 'theme-light': 'Jasny', 'theme-dark': 'Ciemny', 'auto-hide-window': 'Automatycznie chowaj okno', 'run-mode': 'Uruchom jako', 'run-mode-standard': 'Zwykła aplikacja', 'run-mode-tray': 'Aplikacja z paska powiadomień', 'run-mode-hide-tray': 'Ukryj aplikację zasobnika', 'tray-speedometer': 'Pasek zadań będzie pokazywać aktualną prędkość', 'show-progress-bar': 'Pokaż pasek postępu pobierania', 'language': 'Język', 'change-language': 'Zmień język', 'hide-app-menu': 'Schowaj menu aplikacji (Tylko Windows i Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Włącz Proxy', 'proxy-bypass-input-tips': 'Omiń ustawiania proxy dla tych hostów i domen, jeden na linię', 'proxy-scope-download': 'Pobierz', 'proxy-scope-update-app': 'Aktualizacja aplikacji', 'proxy-scope-update-trackers': 'Aktualizacja trackera', 'proxy-tips': 'Pokaż instrukcję proxy', 'bt-tracker': 'Trackery', 'bt-tracker-input-tips': 'Trackery, jeden na linię', 'bt-tracker-tips': 'Zalecane: ', 'sync-tracker-tips': 'Synchronizowane', 'auto-sync-tracker': 'Codziennie aktualizuj listę trackerów', 'port': 'Nasłuchujące porty', 'bt-port': 'Torrent nasłuchujący port', 'dht-port': 'DHT nasłuchujący port', 'security': 'Bezpieczeństwo', 'rpc': 'RPC', 'rpc-listen-port': 'Port nasłuchu RPC', 'rpc-secret': 'Sekret RPC', 'rpc-secret-tips': 'Pokaż instrukcję sekretu RPC', 'developer': 'Developer', 'user-agent': 'User-Agent', 'mock-user-agent': 'Udawaj user-agent\'a', 'aria2-conf-path': 'Wbudowana ścieżka aria2.conf', 'app-log-path': 'Ścieżka logów', 'download-session-path': 'Ścieżka sesji pobranych', 'factory-reset': 'Przywróć domyślne ustawiania', 'factory-reset-confirm': 'Jesteś pewnien że chcesz przywrócić domyślne ustawienia', 'lab-warning': '⚠️ Włączenie laboratorium może doprowadzić do błędów i utraty danych, włącz na własne ryzyko!', 'download-protocol': 'Protokoły', 'protocols-default-client': 'Ustaw jako domyślny klient dla tych protokołów', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Dodatki', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Dostarczone przez społeczność, ', 'baidu-exporter-help': 'Naciśnij tutaj po pomoc', 'auto-update': 'Aktualizuj automatycznie', 'auto-check-update': 'Automatycznie sprawdzaj aktualizacje', 'last-check-update-time': 'Ostatni raz kiedy sprawdzono aktualizację', 'not-saved': 'Preferencje nie zostały zapisane', 'not-saved-confirm': 'Zmienione preferencje zostaną utracone, czy na pewno chcesz wyjść?' } ================================================ FILE: src/shared/locales/pl/subnav.js ================================================ export default { 'task-list': 'Zadania', 'preferences': 'Ustawienia' } ================================================ FILE: src/shared/locales/pl/task.js ================================================ export default { 'active': 'Pobieranie', 'waiting': 'Wstrzymane', 'stopped': 'Zatrzymane', 'new-task': 'Nowe zadanie', 'new-bt-task': 'Nowe zadanie Torrent', 'open-file': 'Otwórz plik torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Jeden link na linię (wspiera magnet)', 'thunder-link-tips': 'Porada: Linki Thunder mogą nie być możliwe do pobrania po zdekodowaniu', 'new-task-uris-required': 'Proszę wprowadzić przynajmniej jeden poprawny URL', 'new-task-torrent-required': 'Proszę wybrać plik torrent', 'file-name': 'Nazwa', 'file-extension': 'Rozbudowa', 'file-size': 'Rozmiar', 'file-completed-size': 'Pobrano', 'selected-files-sum': 'Wybrano: {{selectedFilesCount}} plików, Wspólny rozmiar {{selectedFilesTotalSize}}', 'select-at-least-one': 'Wybierz co najmniej jeden plik', 'task-gid': 'GID', 'task-name': 'Nazwa zadania', 'task-out': 'Zmień nazwę', 'task-out-tips': 'Opcjonalne', 'task-split': 'Części', 'task-dir': 'Zapisz jako', 'pause-task': 'Zapauzuj', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autoryzacja', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Błąd', 'task-piece': 'Kawałek', 'task-piece-length': 'Rozmiar elementu', 'task-num-pieces': 'Kawałki', 'task-bittorrent-info': 'Informacje o torrentach', 'task-info-hash': 'Haszysz', 'task-bittorrent-creation-date': 'Data utworzenia', 'task-bittorrent-comment': 'Komentarz', 'task-progress-info': 'Postęp', 'task-status': 'Status', 'task-num-seeders': 'Siewniki', 'task-connections': 'Znajomości', 'task-file-size': 'Rozmiar', 'task-download-speed': 'Prędkość pobierania', 'task-upload-speed': 'Prędkość wysyłania', 'task-download-length': 'Pobrano', 'task-upload-length': 'Przesłane', 'task-ratio': 'Stosunek', 'task-peer-host': 'Gospodarz', 'task-peer-ip': 'IP', 'task-peer-client': 'Klient', 'navigate-to-downloading': 'Automatycznie uruchom pobieranie', 'show-advanced-options': 'Opcje zaawansowane', 'copyright-warning': 'Ostrzeżenie dt. praw autorskich', 'copyright-warning-message': 'Plik który chcesz pobrać może być chroniony prawami autorskimi, proszę upewnij się że masz prawo go pobierać/używać', 'copyright-yes': 'Tak, mam prawo', 'copyright-no': 'Nie, nie mam prawa', 'copyright-error-message': 'Nie można było dodać zadania z powodu praw autorskich', 'pause-task-success': 'Pomyślnie zapauzowano "{{taskName}}"', 'pause-task-fail': 'Nie udało się zapauzować "{{taskName}}"', 'resume-task': 'Wznów zadanie', 'resume-task-success': 'Pomyślnie wznowiono "{{taskName}}"', 'resume-task-fail': 'Nie udało się wznowić "{{taskName}}"', 'delete-task': 'Usuń zadanie', 'delete-selected-tasks': 'Usuń wybrane zadania', 'delete-task-confirm': 'Czy jesteś pewien że chcesz usunąć zadanie "{{taskName}}"?', 'batch-delete-task-confirm': 'Czy jesteś pewnien że chcesz usunąć {{count}} zadań?', 'delete-task-label': 'Usuń z plikami', 'delete-task-success': 'Pomyślnie usunięto "{{taskName}}"', 'delete-task-fail': 'Nie udało sie usunąć "{{taskName}}"', 'remove-task-file-fail': 'Nie udało się usunąć plików pochodzących z zadań, proszę, usuń je manualnie.', 'remove-task-config-file-fail': 'Nie udało się usunąć pliku konfiguracyjnego zadanie, proszę, usuń je manualnie', 'move-task-up': 'Przenieś zadanie do góry', 'move-task-down': 'Przenieś zadanie do dołu', 'pause-all-task': 'Wstrzymaj wszystkie zadania', 'pause-all-task-success': 'Pomyślnie wstrzymano wszystkie zadania', 'pause-all-task-fail': 'Nie udało się wstrzymać wszystkich zadań', 'resume-all-task': 'Wznów wszystkie zadania', 'resume-all-task-success': 'Pomyślnie wznowiono wszystkie zadania', 'resume-all-task-fail': 'Nie udało się wznowić wszystkich zadań', 'select-all-task': 'Wybierz wszystkie zadania', 'clear-recent-tasks': 'Wyczyść ostatnie zadania', 'purge-record': 'Usuń wszystkie rekordy zadań', 'purge-record-success': 'Pomyślnie usunięto wszystkie rekordy zadań', 'purge-record-fail': 'Nie udało się usunąć wszystkich rekordów zadań', 'batch-delete-task-success': 'Pomyślnie usunięto wiele zadań', 'batch-delete-task-fail': 'Nie udało się usunąć wielu zadań', 'refresh-list': 'Odśwież listę zadań', 'no-task': 'Nie ma obecnie żadnych zadań', 'copy-link': 'Skopiuj link', 'copy-link-success': 'Pomyślnie skopiowano link', 'remove-record': 'Usuń rekord zadania', 'remove-record-confirm': 'Czy jesteś pewien że chcesz usunąć rekord dla zadania "{{taskName}}"?', 'remove-record-label': 'Usuń z plikami', 'remove-record-success': 'Pomyślnie usunięto rekord dla zadania "{{taskName}}"', 'remove-record-fail': 'Nie udało się usunąć rekordu dla zadania "{{taskName}}"', 'show-in-folder': 'Pokaż zadania w folderze', 'file-not-exist': 'Docelowe pliki nie istnieją lub zostały usunięte', 'file-path-error': 'Błąd ścieżki pliku', 'opening-task-message': 'Otiweranie "{{taskName}}" ...', 'get-task-name': 'Pobieranie nazwy pliku...', 'remaining-prefix': 'Pozostało', 'select-torrent': 'Przenieś tutaj plik torrent, lub naciśnij aby wybrać', 'task-info-dialog-title': 'Szczegóły {{title}}', 'download-start-message': 'Rozpoczęto pobieranie {{taskName}}', 'download-pause-message': 'Wstrzymano pobieranie {{taskName}}', 'download-stop-message': 'Zatrzymano pobieranie {{taskName}}', 'download-error-message': 'Wystąpił błąd podczas pobierania {{taskName}}', 'download-complete-message': 'Ukończono pobieranie {{taskName}}', 'download-complete-notify': 'Pobieranie ukończone', 'bt-download-complete-message': 'Ukończono pobieranie {{taskName}}, seedowanie', 'bt-download-complete-notify': 'Ukończono pobieranie pliku torrent, seedowanie...', 'bt-download-complete-tips': 'Wskazówka: Możesz zatrzymań zadanie aby wyłączyć seedowanie', 'bt-stopping-seeding-tip': 'Zatrzymywane seedowania, zajmie chwilę rozłączanie się od klientów, proszę czekać...', 'download-fail-message': 'Nie udało się pobrać {{taskName}}', 'download-fail-notify': 'Nie udało się pobrać' } ================================================ FILE: src/shared/locales/pl/window.js ================================================ export default { 'reload': 'Przeładuj', 'close': 'Zamknij', 'minimize': 'Minimalizuj', 'zoom': 'Powiększ', 'toggle-fullscreen': 'Włącz tryb pełnoekranowy', 'front': 'Przenieś wszystko na przód' } ================================================ FILE: src/shared/locales/pt-BR/about.js ================================================ export default { 'engine-version': 'Versão da Engine', 'license': 'Licença', 'about': 'Sobre', 'release': 'Release', 'support': 'Suporte' } ================================================ FILE: src/shared/locales/pt-BR/app.js ================================================ export default { 'task-list': 'Tarefas', 'add-task': 'Adicionar Tarefa', 'about': 'Sobre o Motrix', 'preferences': 'Preferências...', 'check-for-updates': 'Verificar por Atualizações...', 'check-updates-now': 'Verificar agora', 'checking-for-updates': 'Verificar atualizações ...', 'check-for-updates-title': 'Verificar por Atualizações', 'update-available-message': 'Uma nova versão do Motrix está disponível, atualize agora?', 'update-not-available-message': 'Você esta atualizado!', 'update-downloaded-message': 'Pronto para instalar...', 'update-error-message': 'Atualizar erro', 'engine-damaged-message': 'O motor está danificado, por favor, reinstale : (', 'engine-missing-message': 'O motor está faltando, por favor, reinstale : (', 'system-error-title': 'Erro do sistema', 'system-error-message': 'O aplicativo falhou ao iniciar: {{message}}', 'hide': 'Ocultar Motrix', 'hide-others': 'Ocultar Outros', 'unhide': 'Exibir Todos', 'show': 'Exibir Motrix', 'quit': 'Sair do Motrix', 'under-development-message': 'Desculpe, essa funcionalidade está em desenvolvimento...', 'yes': 'Sim', 'no': 'Não', 'save': 'Salvar', 'reset': 'Descartar', 'cancel': 'Cancelar', 'submit': 'Enviar', 'gt1d': '> 1 dia', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/pt-BR/edit.js ================================================ export default { 'undo': 'Desfazer', 'redo': 'Refazer', 'cut': 'Recortar', 'copy': 'Copiar', 'paste': 'Copiar', 'delete': 'Apagar', 'select-all': 'Selecionar Tudo' } ================================================ FILE: src/shared/locales/pt-BR/help.js ================================================ export default { 'official-website': 'Motrix Website', 'manual': 'Manual', 'release-notes': 'Notas de Lançamento...', 'report-problem': 'Reportar um Problema', 'toggle-dev-tools': 'Alternar Developer Tools' } ================================================ FILE: src/shared/locales/pt-BR/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/pt-BR/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Arquivo', 'task': 'Tarefa', 'edit': 'Editar', 'window': 'Janela', 'help': 'Ajuda' } ================================================ FILE: src/shared/locales/pt-BR/preferences.js ================================================ export default { 'basic': 'Básico', 'advanced': 'Avançado', 'lab': 'Lab', 'save': 'Salvar & Aplicar', 'save-success-message': 'Salvar preferências com sucesso', 'save-fail-message': 'Salvar preferências falhadas', 'discard': 'Descartar', 'startup': 'Inicialização', 'open-at-login': 'Abra no login', 'keep-window-state': 'Restaurar tamanho e posição da janela', 'auto-resume-all': 'Auto resumir todas as tarefas não finalizadas', 'default-dir': 'Diretório Padrão', 'mas-default-dir-tips': 'Devido às restrições de permissões do sandbox da App Store, recomenda-se que o diretório de download padrão seja definido como o diretório de downloads', 'transfer-settings': 'Transmissão', 'transfer-speed-upload': 'limite de envio', 'transfer-speed-download': 'limite de transferência', 'transfer-speed-unlimited': 'Ilimitado', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Salvar link magnético como arquivo torrent', 'bt-auto-download-content': 'Baixar automaticamente ímã e conteúdo de torrent', 'bt-force-encryption': 'Forçar criptografia BT', 'keep-seeding': 'Continue semeando até pará-lo manualmente', 'seed-ratio': 'Proporção de sementes', 'seed-time': 'Hora da Semente', 'seed-time-unit': 'minutos', 'task-manage': 'Gerenciador de Tarefas', 'max-concurrent-downloads': 'Máximo de tarefas ativas', 'max-connection-per-server': 'Máximo de coneções por servidor', 'new-task-show-downloading': 'Auto exibir progresso depois de adicionar uma tarefa', 'no-confirm-before-delete-task': 'Nenhuma confirmação é necessária antes de excluir a tarefa', 'continue': 'Continuar', 'task-completed-notify': 'Notificação após o download ser completado', 'auto-purge-record': 'Auto remover registro de download quando o app for finalizado', 'ui': 'UI', 'appearance': 'Aparência', 'theme-auto': 'Automático', 'theme-light': 'Clara', 'theme-dark': 'Escura', 'auto-auto-hide-window': 'Ocultar janelas automaticamente', 'run-mode': 'Correr como', 'run-mode-standard': 'Aplicação padrão', 'run-mode-tray': 'Aplicativo de bandeja', 'run-mode-hide-tray': 'Ocultar aplicativo da bandeja', 'tray-speedometer': 'A bandeja da barra de menus mostra a velocidade em tempo real', 'show-progress-bar': 'Mostrar barra de progresso de download', 'language': 'Idioma', 'change-language': 'Mudar o Idioma', 'hide-app-menu': 'Ocultar o Menu do App (Windows & Linux apenas)', 'proxy': 'Proxy', 'enable-proxy': 'Habilitar Proxy', 'proxy-bypass-input-tips': 'Ignorar configurações de proxy para esses hosts e domínios, um por linha', 'proxy-scope-download': 'Download', 'proxy-scope-update-app': 'Atualizar Aplicativo', 'proxy-scope-update-trackers': 'Atualizar rastreadores', 'proxy-tips': 'Exibir manual do proxy', 'bt-tracker': 'Rastreadores', 'bt-tracker-input-tips': 'servidor rastreador, um por linha', 'bt-tracker-tips': 'Recomendar:', 'sync-tracker-tips': 'Sincronizar', 'auto-sync-tracker': 'Atualize a lista de rastreadores todos os dias automaticamente', 'port': 'Portas de escuta', 'bt-port': 'Porta de escuta BT', 'dht-port': 'Porta de escuta DHT', 'security': 'Segurança', 'rpc': 'RPC', 'rpc-listen-port': 'Porta de Escuta RPC', 'rpc-secret': 'Segredo de RPC', 'rpc-secret-tips': 'Veja o manual secreto de RPC', 'developer': 'Desenvolverdor', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Caminho de aria2.conf incorporado', 'app-log-path': 'Diretório de logs', 'download-session-path': 'Diretório da sessão de Downloads', 'factory-reset': 'Configurações de Fábrica', 'factory-reset-confirm': 'Você tem certeza de que deseja resetar às configurações de fábrica?', 'lab-warning': '⚠️ Habilitar os recursos de lab pode causar perda de dados e fechar o app inesperadamete. Use por sua conta e risco!', 'download-protocol': 'Protocolo', 'protocols-default-client': 'Definir como cliente padrão para os seguintes protocolos', 'protocols-magnet': 'Magnético [ magnet:// ]', 'protocols-thunder': 'Trovão [ thunder:// ]', 'browser-extensions': 'Extensões', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Fornecido pela comunidade, ', 'baidu-exporter-help': 'Clique aqui ver as instruções de uso', 'auto-update': 'Atualização automática', 'auto-check-update': 'A verificação automática de atualizações', 'last-check-update-time': 'última verificação do tempo de atualização', 'not-saved': 'Preferências não salvas', 'not-saved-confirm': 'As preferências modificadas serão perdidas. Tem certeza de que deseja sair?' } ================================================ FILE: src/shared/locales/pt-BR/subnav.js ================================================ export default { 'task-list': 'Tarefas', 'preferences': 'Preferências' } ================================================ FILE: src/shared/locales/pt-BR/task.js ================================================ export default { 'active': 'Baixando', 'waiting': 'Aguardando', 'stopped': 'Parou', 'new-task': 'Nova Tarefa', 'new-bt-task': 'Nova Tarefa BT', 'open-file': 'Abra o arquivo Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Uma url por linha (magnet links são aceitos)', 'thunder-link-tips': 'Dica: Thunder links não podem ser baixados após decodificados', 'new-task-uris-required': 'Por favor, insira pelo menos um URL de recurso válido', 'new-task-torrent-required': 'Por favor, selecione um arquivo torrent', 'file-name': 'Nome', 'file-extension': 'Ext', 'file-size': 'Tamanho', 'file-completed-size': 'Baixado', 'selected-files-sum': 'Selecionado: {{selectedFilesCount}} arquivos, total {{selectedFilesTotalSize}}', 'select-at-least-one': 'Selecione pelo menos um arquivo', 'task-gid': 'GID', 'task-name': 'Nome da Tarefa', 'task-out': 'Renomear', 'task-out-tips': 'Opcional', 'task-split': 'Splits', 'task-dir': 'Diretório', 'pause-task': 'Pausar Tarefa', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorização', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Erro', 'task-piece': 'Artigo', 'task-piece-length': 'Tamanho da peça', 'task-num-pieces': 'Peças', 'task-bittorrent-info': 'Torrent Info', 'task-info-hash': 'Cerquilha', 'task-bittorrent-creation-date': 'Data de criação', 'task-bittorrent-comment': 'Comente', 'task-progress-info': 'Progresso', 'task-status': 'Status', 'task-num-seeders': 'Semeadores', 'task-connections': 'Conexões', 'task-file-size': 'Tamanho', 'task-download-speed': 'Velocidade de download', 'task-upload-speed': 'Velocidade de upload', 'task-download-length': 'Baixado', 'task-upload-length': 'Carregado', 'task-ratio': 'Razão', 'task-peer-host': 'Hospedeiro', 'task-peer-ip': 'IP', 'task-peer-client': 'Cliente', 'navigate-to-downloading': 'Navegar para o Downloading', 'show-advanced-options': 'Opções Avançadas', 'copyright-warning': 'Aviso de Copyright', 'copyright-warning-message': 'O arquivo que você deseja baixa pode ser protegido por direitos de copyright de áudio ou vídeo, tenha certeza que você possui os direitos de licensa.', 'copyright-yes': 'Sim, Eu Tenho', 'copyright-no': 'Não', 'copyright-error-message': 'A operação falhou devido os direitos de copyright', 'pause-task-success': 'Tarefa "{{taskName}}" pausada com sucesso', 'pause-task-fail': 'Falha ao pausar a tarefa "{{taskName}}"', 'resume-task': 'Resumir Tarefa', 'resume-task-success': 'Tarefa "{{taskName}}" resumida com sucesso', 'resume-task-fail': 'Falha ao resumir a tarefa "{{taskName}}"', 'delete-task': 'Apagar Tarefa', 'delete-selected-tasks': 'Apagar as Tarefas Selecionadas', 'delete-task-confirm': 'Você tem certeza de que deseja apagar a seguinte tarefa de download: "{{taskName}}"?', 'batch-delete-task-confirm': 'Tem certeza de que deseja remover {{count}} tarefas de download em lote?', 'delete-task-label': 'Apagar com os Arquivos', 'delete-task-success': 'Tarefa "{{taskName}}" apagada com sucesso', 'delete-task-fail': 'Falha ao apagar a tarefa "{{taskName}}"', 'remove-task-file-fail': 'Falha ao tentar deletar o arquivo da tarefa, por favor, apague manualmente', 'remove-task-config-file-fail': 'Falha ao tentar deletar o arquivo de configuração da tarefa, por favor, apague manualmente', 'move-task-up': 'Mover Tarefa para Cima', 'move-task-down': 'Mover Tarefa para Baixar', 'pause-all-task': 'Pausar Todas as Tarefas', 'pause-all-task-success': 'Todas as tarefas foram pausadas com sucesso', 'pause-all-task-fail': 'Falha ao pausar todas as tarefas', 'resume-all-task': 'Resumir todas as tarefas', 'resume-all-task-success': 'Todas as tarefas foram resumidas com sucesso', 'resume-all-task-fail': 'Falha ao resumir todas as tarefas', 'select-all-task': 'Selecione todas as tarefas', 'clear-recent-tasks': 'Limpar todas as tarefas', 'purge-record': 'Expurgar o Registro de Tarefas', 'purge-record-success': 'O registro de tarefas foi expurgado com sucesso', 'purge-record-fail': 'Falha ao expurgar o registro de tarefas', 'batch-delete-task-success': 'Exclua com êxito as tarefas em lote', 'batch-delete-task-fail': 'Falha ao excluir tarefas no lote', 'refresh-list': 'Atualizar a Lista de Tarefas', 'no-task': 'Não há downloads no momento', 'copy-link': 'Copiar Link', 'copy-link-success': 'Link copiado com sucesso', 'remove-record': 'Apagar Registro de Tarefas', 'remove-record-confirm': 'Você tem certeza de que deseja remover o registro de download da tarefa "{{taskName}}"?', 'remove-record-label': 'Deletar com arquivos', 'remove-record-success': 'Registro da tarefa "{{taskName}}" apagado com sucesso', 'remove-record-fail': 'Falha ao apagar o registro da tarefa "{{taskName}}"', 'show-in-folder': 'Exibir Tarefa na Pasta', 'file-not-exist': 'O arquivo não existe ou foi apagado', 'file-path-error': 'Erro no caminho do arquivo', 'opening-task-message': 'Abrindo "{{taskName}}" ...', 'get-task-name': 'Obter o nome da tarefa...', 'remaining-prefix': 'Restante', 'select-torrent': 'Arraste o torrent aqui, ou clique para selecionar', 'task-info-dialog-title': '{{title}} Detalhes', 'download-start-message': 'Iniciar download {{taskName}}', 'download-pause-message': 'Pausar download {{taskName}}', 'download-stop-message': '{{taskName}} download parado', 'download-error-message': '{{taskName}} ocorreu um erro no download', 'download-complete-message': '{{taskName}} download completado', 'download-complete-notify': 'Download Completado', 'bt-download-complete-message': '{{taskName}} download completado, propagação ...', 'bt-download-complete-notify': 'Download do BT concluído, Propagação ...', 'bt-download-complete-tips': 'Dicas: você pode parar a tarefa para terminar a propagação', 'bt-stopping-seeding-tip': 'Parando a propagação, levará algum tempo para desconectar, aguarde ...', 'download-fail-message': '{{taskName}} falha no download', 'download-fail-notify': 'Falha no Download' } ================================================ FILE: src/shared/locales/pt-BR/window.js ================================================ export default { 'reload': 'Recarregar', 'close': 'Fechar', 'minimize': 'Minimizar', 'zoom': 'Zoom', 'toggle-fullscreen': 'Modo Full Screen', 'front': 'Trazer Tudo Para a Frente' } ================================================ FILE: src/shared/locales/ro/about.js ================================================ export default { 'engine-version': 'Versiune Motor', 'license': 'Licență', 'about': 'Despre', 'release': 'Versiuni', 'support': 'Ajutor' } ================================================ FILE: src/shared/locales/ro/app.js ================================================ export default { 'task-list': 'Sarcini', 'add-task': 'Adaugă sarcină', 'about': 'Despre Motrix', 'preferences': 'Setări...', 'check-for-updates': 'Verifică actualizări...', 'check-updates-now': 'Verifică acum', 'checking-for-updates': 'Verific actualizări ...', 'check-for-updates-title': 'Verifică actualizări', 'update-available-message': 'O nouă versiune Motrix este disponibilă, actualizaţi acum?', 'update-not-available-message': 'Versiunea este la zi!', 'update-downloaded-message': 'Gata de instalare...', 'update-error-message': 'Eroare actualizare', 'engine-damaged-message': 'Motorul este deteriorat, vă rugăm să reinstalați : (', 'engine-missing-message': 'Motorul lipseşte, vă rugăm să reinstalați : (', 'system-error-title': 'Eroare sistem', 'system-error-message': 'Aplicaţia nu a putut porni: {{message}}', 'hide': 'Ascunde Motrix', 'hide-others': 'Ascunde celelalte', 'unhide': 'Arată toate', 'show': 'Arată Motrix', 'quit': 'Închide Motrix', 'under-development-message': 'Scuze, această caracteristică este în curs de dezvoltare...', 'yes': 'Da', 'no': 'Nu', 'save': 'Salvați', 'reset': 'Aruncați', 'cancel': 'Anuleaza', 'submit': 'Trimite', 'gt1d': '> 1 zi', 'hour': 'h', 'minute': 'm', 'second': 's' } ================================================ FILE: src/shared/locales/ro/edit.js ================================================ export default { 'undo': 'Undo', 'redo': 'Redo', 'cut': 'Taie', 'copy': 'Copiază', 'paste': 'Lipeşte', 'delete': 'Şterge', 'select-all': 'Selectează tot' } ================================================ FILE: src/shared/locales/ro/help.js ================================================ export default { 'official-website': 'Website Motrix', 'manual': 'Manual', 'release-notes': 'Notele versiunii...', 'report-problem': 'Raportează problemă', 'toggle-dev-tools': 'Deschide unelete dezvoltator' } ================================================ FILE: src/shared/locales/ro/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ro/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'File', 'task': 'Sarcină', 'edit': 'Edit', 'window': 'Fereastra', 'help': 'Ajutor' } ================================================ FILE: src/shared/locales/ro/preferences.js ================================================ export default { 'basic': 'De bază', 'advanced': 'Avansate', 'lab': 'Laborator', 'save': 'Salvează şi Aplică', 'save-success-message': 'Setările au fost salvate cu succes', 'save-fail-message': 'Nu s-au putut salva setările', 'discard': 'Renunţă', 'startup': 'Pornire', 'open-at-login': 'Deschide la logare', 'keep-window-state': 'Păstrați dimensiunea și poziția ferestrei la închiderea aplicaţiei', 'auto-resume-all': 'Reia automat toate sarcinile neterminate', 'default-dir': 'Calea implicită', 'mas-default-dir-tips': 'Datorită restricțiilor privind permisiunea sandbox-ului din App Store, se recomandă ca directorul de descărcare implicit să fie setat la ~/Downloads', 'transfer-settings': 'Transmitere', 'transfer-speed-upload': 'Limită upload', 'transfer-speed-download': 'Limită download', 'transfer-speed-unlimited': 'Nelimitat', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Salvați linkul magnet ca fișier torrent', 'bt-auto-download-content': 'Descărcați automat magnetul și conținutul torrent', 'bt-force-encryption': 'Criptare forţată BT', 'keep-seeding': 'Păstrați însămânțarea până când o opriți manual', 'seed-ratio': 'Raportul semințelor', 'seed-time': 'Timpul semințelor', 'seed-time-unit': 'minute', 'task-manage': 'Managementul sarcinilor', 'max-concurrent-downloads': 'Sarcini active maxime', 'max-connection-per-server': 'Conexiuni maxime per server', 'new-task-show-downloading': 'Afișează automat descărcarea după adăugarea sarcinii', 'no-confirm-before-delete-task': 'Nu este necesară confirmarea înainte de ștergerea sarcinii', 'continue': 'Continuă', 'task-completed-notify': 'Notifică după finalizarea sarcinii', 'auto-purge-record': 'Ștergeți automat înregistrările de descărcare la ieșirea din aplicație', 'ui': 'Interfață Utilizator', 'appearance': 'Aspect', 'theme-auto': 'Automat', 'theme-light': 'Temă luminoasă', 'theme-dark': 'Temă întunecată', 'auto-hide-window': 'Ascunde automat fereastra', 'run-mode': 'Ruleaza ca', 'run-mode-standard': 'Aplicatie standard', 'run-mode-tray': 'Aplicație pentru tava', 'run-mode-hide-tray': 'Ascundeți aplicația tăvii', 'tray-speedometer': 'Iconița din bară arată viteza în timp real', 'show-progress-bar': 'Afișează bara de progres a descărcării', 'language': 'Limba', 'change-language': 'Schimbă limba', 'hide-app-menu': 'Ascundeți meniul aplicației (numai Windows și Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Activați Proxy', 'proxy-bypass-input-tips': 'Ignorați setările proxy pentru aceste gazde și domenii, câte una pe fiecare linie', 'proxy-scope-download': 'Descărcare', 'proxy-scope-update-app': 'Actualizare aplicație', 'proxy-scope-update-trackers': 'Actualizare Trackere', 'proxy-tips': 'Vizualizați manualul proxy', 'bt-tracker': 'Severe tracker (Torrent Tracker)', 'bt-tracker-input-tips': 'Severe tracker, câte unul pe fiecare linie', 'bt-tracker-tips': 'Recomandat: ', 'sync-tracker-tips': 'Sincronizare', 'auto-sync-tracker': 'Actualizați automat lista de servere tracker în fiecare zi', 'port': 'Porturi pe care se ascultă', 'bt-port': 'Port ascultare BT', 'dht-port': 'Port ascultare DHT', 'security': 'Securitate', 'rpc': 'RPC', 'rpc-listen-port': 'Portul de Ascultare RPC', 'rpc-secret': 'Secret RPC', 'rpc-secret-tips': 'Vizualizați manualul pentru secret RPC', 'developer': 'Dezvoltator', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Calea aria2.conf încorporată', 'app-log-path': 'Calea jurnalului aplicației', 'download-session-path': 'Calea sesiunii de download', 'factory-reset': 'Resetare la setări din fabrică', 'factory-reset-confirm': 'Sigur doriți să reveniți la setările din fabrică?', 'lab-warning': '⚠️ Activarea funcțiilor de laborator poate duce la blocarea aplicației sau pierderea datelor, contiunați pe propriul risc!', 'download-protocol': 'Protocoale', 'protocols-default-client': 'Setați client implicit pentru următoarele protocoale', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Extensii', 'baidu-exporter': 'Exportator Baidu', 'browser-extensions-tips': 'Furnizat de comunitate, ', 'baidu-exporter-help': 'Faceți clic aici pentru instrucțiuni', 'auto-update': 'Actualizare automată', 'auto-check-update': 'Verificați automat dacă sunt disponibile actualizări', 'last-check-update-time': 'Ultima dată când au fost verificate actualizările disponibile', 'not-saved': 'Preferințele nu au fost salvate', 'not-saved-confirm': 'Preferințele modificate se vor pierde, sunteți sigur că plecați?' } ================================================ FILE: src/shared/locales/ro/subnav.js ================================================ export default { 'task-list': 'Sarcini', 'preferences': 'Setări' } ================================================ FILE: src/shared/locales/ro/task.js ================================================ export default { 'active': 'Se descarcă', 'waiting': 'În aşteptare', 'stopped': 'Oprite', 'new-task': 'Sarcină Nouă', 'new-bt-task': 'Sarcină Nouă BT', 'open-file': 'Deschide fişier Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'O adresă URL de sarcină pe linie (acceptă magnet)', 'thunder-link-tips': 'Sfat: Este posibil ca linkurile Thunder să nu poată fi descărcate după decodare', 'new-task-uris-required': 'Introduceți cel puțin o adresă URL validă', 'new-task-torrent-required': 'Vă rugăm să selectați un fișier torrent', 'file-name': 'Nume', 'file-extension': 'Extensie', 'file-size': 'Dimensiune', 'file-completed-size': 'Descărcat', 'selected-files-sum': 'Selectate: {{selectedFilesCount}} fișiere, dimensiune totală {{selectedFilesTotalSize}}', 'select-at-least-one': 'Vă rugăm să selectați cel puțin un fișier', 'task-gid': 'GID', 'task-name': 'Nume sarcină', 'task-out': 'Redenumeşte', 'task-out-tips': 'Opțional', 'task-split': 'Scindări', 'task-dir': 'Salvează în', 'pause-task': 'Întrerupeți sarcina', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Autorizare', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Eroare', 'task-piece': 'Bucată', 'task-piece-length': 'Dimensiunea piesei', 'task-num-pieces': 'Piese', 'task-bittorrent-info': 'Informații despre torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Data crearii', 'task-bittorrent-comment': 'cometariu', 'task-progress-info': 'Progres', 'task-status': 'stare', 'task-num-seeders': 'Semănători', 'task-connections': 'Conexiuni', 'task-file-size': 'mărimea', 'task-download-speed': 'Viteza de descărcare', 'task-upload-speed': 'Viteza de upload', 'task-download-length': 'Descărcat', 'task-upload-length': 'Încărcat', 'task-ratio': 'Raport', 'task-peer-host': 'Gazdă', 'task-peer-ip': 'IP', 'task-peer-client': 'Client', 'navigate-to-downloading': 'Mergi la Descărcări', 'show-advanced-options': 'Opțiuni avansate', 'copyright-warning': 'Avertisment privind drepturile de autor', 'copyright-warning-message': 'Fișierul pe care doriți să îl descărcați poate fi audio sau video cu drepturi de autor, vă rugăm să vă asigurați că aveți permisiunea pentru a-l accesa.', 'copyright-yes': 'Da, am permisiunea', 'copyright-no': 'Nu, nu am permisiunea', 'copyright-error-message': 'Adăugarea sarcinii nu a reușit din cauza problemei drepturilor de autor', 'pause-task-success': 'Sarcina "{{taskName}}" a fost întreruptă cu succes', 'pause-task-fail': 'Nu s-a putut întrerupe sarcina "{{taskName}}"', 'resume-task': 'Reluați sarcina', 'resume-task-success': 'Sarcina "{{taskName}}" a fost reluata cu succes', 'resume-task-fail': 'Nu s-a putut relua sarcina "{{taskName}}"', 'delete-task': 'Șterge sarcina', 'delete-selected-tasks': 'Șterge sarcinile selectate', 'delete-task-confirm': 'Sunteţi sigur că vreţi să eliminați sarcina "{{taskName}}"?', 'batch-delete-task-confirm': 'Sigur doriți să eliminați {{count}} sarcini de descărcare în lot?', 'delete-task-label': 'Ștergeți cu Fișiere', 'delete-task-success': 'Sarcina "{{taskName}}" a fost ștearsă cu succes', 'delete-task-fail': 'Nu s-a putut ștearge sarcina "{{taskName}}"', 'remove-task-file-fail': 'Nu s-au putut șterge fișierele sarcinii, ștergeți-le manual', 'remove-task-config-file-fail': 'Ștergerea fișierului de configurare a sarcinii nu a reușit, ștergeți-l manual', 'move-task-up': 'Muta sarcina în sus', 'move-task-down': 'Muta sarcina în jos', 'pause-all-task': 'Întrerupeți toate sarcinile', 'pause-all-task-success': 'Toate sarcinile au fost întrerupte cu succes', 'pause-all-task-fail': 'Nu s-au putut întrerupe toate sarcinile', 'resume-all-task': 'Reia toate sarcinile', 'resume-all-task-success': 'S-au reluat cu succes toate sarcinile', 'resume-all-task-fail': 'Nu s-au putut relua toate sarcinile', 'select-all-task': 'Selectează Toate sarcinile', 'clear-recent-tasks': 'Șterge sarcinile recente', 'purge-record': 'Șterge istoricul sarcinilor', 'purge-record-success': 'Istoricul sarcinilor a fost șters', 'purge-record-fail': 'Nu s-a putut șterge istoricul sarcinilor', 'batch-delete-task-success': 'Sarcinile în lot au fost șterse cu succes', 'batch-delete-task-fail': 'Nu s-au putut șterge sarcinile in lot', 'refresh-list': 'Reîncarcă lista de sarcini', 'no-task': 'Nu există sarcini curente', 'copy-link': 'Copiază Link', 'copy-link-success': 'Link copiat cu succes', 'remove-record': 'Șterge istoric sarcină', 'remove-record-confirm': 'Sigur doriți să ștergeți istoricul sarcinii "{{taskName}}"?', 'remove-record-label': 'Ștergeți cu Fișiere', 'remove-record-success': 'Istoricul sarcinii a fost șters cu succes pentru "{{taskName}}"', 'remove-record-fail': 'Nu s-au putut șterge istoricul sarcinii pentru "{{taskName}}"', 'show-in-folder': 'Arată sarcina în Folder', 'file-not-exist': 'Fișierul țintă nu există sau a fost șters', 'file-path-error': 'Eroare cale fișier', 'opening-task-message': 'Deschid "{{taskName}}" ...', 'get-task-name': 'Se obține numele sarcinii...', 'remaining-prefix': 'Mai rămân', 'select-torrent': 'Trageți fișierul torrent aici sau faceți clic pentru a selecta', 'task-info-dialog-title': '{{title}} Detalii', 'download-start-message': 'Descărcarea a început {{taskName}}', 'download-pause-message': 'Descărcare întreruptă {{taskName}}', 'download-stop-message': 'Descărcare oprită {{taskName}}', 'download-error-message': 'A apărut o eroare la descărcare {{taskName}}', 'download-complete-message': 'Descărcare finalizată {{taskName}}', 'download-complete-notify': 'Descărcare finalizată', 'bt-download-complete-message': 'Descărcare finalizată {{taskName}}, se stă la seed', 'bt-download-complete-notify': 'Descărcare BT finalizată, se stă la seed...', 'bt-download-complete-tips': 'Sfaturi: puteți opri o sarcină pentru a nu mai sta la "seed"', 'bt-stopping-seeding-tip': 'Se oprește statul la seed, va dura ceva timp pentru a vă deconecta, vă rugăm să așteptați ...', 'download-fail-message': 'Descărcarea nu a reușit {{taskName}}', 'download-fail-notify': 'Descărcarea nu a reușit' } ================================================ FILE: src/shared/locales/ro/window.js ================================================ export default { 'reload': 'Reîncărcă', 'close': 'Închide', 'minimize': 'Minimizează', 'zoom': 'Zoom', 'toggle-fullscreen': 'Intră în mod ecran complet', 'front': 'Adu-le pe toate în față' } ================================================ FILE: src/shared/locales/ru/about.js ================================================ export default { 'engine-version': 'Версия движка', 'license': 'Лицензия', 'about': 'Информация', 'release': 'Релиз', 'support': 'Поддержка' } ================================================ FILE: src/shared/locales/ru/app.js ================================================ export default { 'task-list': 'Задачи', 'add-task': 'Добавить задание', 'about': 'О Motrix', 'preferences': 'Настройки...', 'check-for-updates': 'Проверить обновление...', 'check-updates-now': 'Проверить сейчас', 'checking-for-updates': 'Проверка обновлений ...', 'check-for-updates-title': 'Проверить обновления', 'update-available-message': 'Новая версия Motrix доступна для скачивания, скачать сейчас?', 'update-not-available-message': 'Вы уже используете самую последнюю версию!', 'update-downloaded-message': 'Готово к установке...', 'update-error-message': 'Ошибка обновления', 'engine-damaged-message': 'Движок поврежден. Пожалуйста, переустановите его : (', 'engine-missing-message': 'Движок потерян. Пожалуйста, переустановите его : (', 'system-error-title': 'Системная ошибка', 'system-error-message': 'Ошибка запуска приложения: {{message}}', 'hide': 'Спрятать Motrix', 'hide-others': 'Спрятать все остальное', 'unhide': 'Отобразить все', 'show': 'Отобразить Motrix', 'quit': 'Закрыть Motrix', 'under-development-message': 'К сожалению, эта функция все еще в разработке...', 'yes': 'Да', 'no': 'Нет', 'save': 'Сохранить', 'reset': 'Отмена', 'cancel': 'Отмена', 'submit': 'Подтвердить', 'gt1d': '> 1 день', 'hour': 'ч', 'minute': 'м', 'second': 'с' } ================================================ FILE: src/shared/locales/ru/edit.js ================================================ export default { 'undo': 'Отмена', 'redo': 'Повторить', 'cut': 'Вырезать', 'copy': 'Копировать', 'paste': 'Вставить', 'delete': 'Удалить', 'select-all': 'Выбрать все' } ================================================ FILE: src/shared/locales/ru/help.js ================================================ export default { 'official-website': 'Сайт Motrix', 'manual': 'Инструкция', 'release-notes': 'Пометки к релизу...', 'report-problem': 'Сообщить о проблеме', 'toggle-dev-tools': 'Переключить инструменты разработчика' } ================================================ FILE: src/shared/locales/ru/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/ru/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Файл', 'task': 'Задания', 'edit': 'Редактировать', 'window': 'Окно', 'help': 'Помощь' } ================================================ FILE: src/shared/locales/ru/preferences.js ================================================ export default { 'basic': 'Основные', 'advanced': 'Расширенные', 'lab': 'Лаборатория', 'save': 'Сохранить и применить', 'save-success-message': 'Настройки сохранены успешно', 'save-fail-message': 'Ошибка при сохранении настроек', 'discard': 'Отмена', 'startup': 'Запускать', 'open-at-login': 'Запускать програму вместе со стартом операционной системы', 'keep-window-state': 'Во время закрытия приложения, сохранять размер и положение окна', 'auto-resume-all': 'Автоматически возобновлять все незавершенные задачи', 'default-dir': 'Папка по умолчанию', 'mas-default-dir-tips': 'Из-за ограничения в App Store, рекомендуется устанавливать путь по умолчанию как ~/Downloads', 'transfer-settings': 'коробка передач', 'transfer-speed-upload': 'Лимит отдачи', 'transfer-speed-download': 'Лимит загрузки', 'transfer-speed-unlimited': 'Безлимитно', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Сохранить магнитную ссылку как торрент-файл', 'bt-auto-download-content': 'Автоматически загружать магнит и торрент', 'bt-force-encryption': 'Принудительное шифрование BT', 'keep-seeding': 'Продолжайте посев, пока не остановите его вручную', 'seed-ratio': 'Соотношение семян', 'seed-time': 'Время посева', 'seed-time-unit': 'минут', 'task-manage': 'Менеджер задач', 'max-concurrent-downloads': 'Максимум активных задач', 'max-connection-per-server': 'Максимум соединений на сервер', 'new-task-show-downloading': 'Автоматически отображать задачу после ее добавления', 'no-confirm-before-delete-task': 'Перед удалением задачи подтверждение не требуется', 'continue': 'Продолжить', 'task-completed-notify': 'Сообщение после окончания загрузки', 'auto-purge-record': 'Автоматически чистить записи о загрузках после закрытия приложения', 'ui': 'UI', 'appearance': 'Внешний вид', 'theme-auto': 'Автоматически', 'theme-light': 'Светлый', 'theme-dark': 'Темный', 'auto-hide-window': 'Автоскрытие окон', 'run-mode': 'Запускать как...', 'run-mode-standard': 'Стандартное приложение', 'run-mode-tray': 'Приложение в трее', 'run-mode-hide-tray': 'Скрыть приложение в трее', 'tray-speedometer': 'Панель меню отображает скорость в реальном времени', 'show-progress-bar': 'Показать индикатор загрузки', 'language': 'Язык', 'change-language': 'Сменить язык', 'hide-app-menu': 'Скрыть меню приложения (только для Windows и Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Использовать Proxy', 'proxy-bypass-input-tips': 'Обойти настройки прокси для этих хостов и доменов, по одному в строке', 'proxy-scope-download': 'Скачать', 'proxy-scope-update-app': 'Обновить приложение', 'proxy-scope-update-trackers': 'Обновить трекеры', 'proxy-tips': 'Посмотреть руководство по прокси', 'bt-tracker': 'Tracker Сервер', 'bt-tracker-input-tips': 'Tracker сервера, один в строку', 'bt-tracker-tips': 'Рекомендовано: ', 'sync-tracker-tips': 'Синхронизация', 'auto-sync-tracker': 'Обновлять список трекеров каждый день автоматически', 'port': 'Порты прослушивания', 'bt-port': 'Порт прослушивания BT', 'dht-port': 'Порт прослушивания DHT', 'security': 'Безопастность', 'rpc': 'RPC', 'rpc-listen-port': 'Порт прослушивания RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Смотреть инструкцию RPC Secret', 'developer': 'Разработчик', 'user-agent': 'User-Agent', 'mock-user-agent': 'Макет User-Agent', 'aria2-conf-path': 'Встроенный путь к aria2.conf', 'app-log-path': 'Путь к журналу приложения', 'download-session-path': 'Загрузить путь сессии', 'factory-reset': 'Настройки по умолчанию', 'factory-reset-confirm': 'Вы уверены, что хотите вернуться к настройкам по умолчанию?', 'lab-warning': '⚠️ Включение функций лаборатории может привести к сбоям приложения и потери данных. Вы действуете на свой страх и риск!', 'download-protocol': 'Протоколы', 'protocols-default-client': 'Установить как клиента по умолчанию для следующих протоколов', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Расширения', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Предоставляются сообществом, ', 'baidu-exporter-help': 'Нажмите здесь для использования', 'auto-update': 'Автоматическое обновление', 'auto-check-update': 'Автоматически проверять обновления', 'last-check-update-time': 'Последняя проверка на обновления прошла в', 'not-saved': 'Настройки не сохранены', 'not-saved-confirm': 'Измененные настройки будут потеряны, вы обязательно уйдете?' } ================================================ FILE: src/shared/locales/ru/subnav.js ================================================ export default { 'task-list': 'Задачи', 'preferences': 'Настройки' } ================================================ FILE: src/shared/locales/ru/task.js ================================================ export default { 'active': 'Загрузки', 'waiting': 'Ожидание', 'stopped': 'Остановлено', 'new-task': 'Новое задание', 'new-bt-task': 'Новое BT задание', 'open-file': 'Открыть Torrent файл...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Один URL-задания в строку (поддержка magnet)', 'thunder-link-tips': 'Совет: Ссылки типа Thunder могут не загружаться после декодированния', 'new-task-uris-required': 'Введите хотя бы один действительный URL-адрес ресурса', 'new-task-torrent-required': 'Пожалуйста, выберите torrent файл', 'file-name': 'Имя файла', 'file-extension': 'Тип файла', 'file-size': 'Размер', 'file-completed-size': 'Завершенный', 'selected-files-sum': 'Выбрано: {{selectedFilesCount}} файлов, общий размер {{selectedFilesTotalSize}}', 'select-at-least-one': 'Пожалуйста, выберите хотя бы один файл', 'task-gid': 'GID', 'task-name': 'Имя загрузки', 'task-out': 'Переименовать', 'task-out-tips': 'Необязательный', 'task-split': 'Разбить', 'task-dir': 'Сохранить как', 'pause-task': 'Приостановить задание', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Авторизация', 'task-referer': 'Реферал', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Ошибка', 'task-piece': 'Кусок', 'task-piece-length': 'Размер куска', 'task-num-pieces': 'Шт', 'task-bittorrent-info': 'Информация о торрентах', 'task-info-hash': 'Хеш', 'task-bittorrent-creation-date': 'Дата создания', 'task-bittorrent-comment': 'Комментарий', 'task-progress-info': 'Прогресс', 'task-status': 'Статус', 'task-num-seeders': 'Сеялки', 'task-connections': 'Подключения', 'task-file-size': 'Размер', 'task-download-speed': 'Скорость загрузки', 'task-upload-speed': 'Скорость загрузки', 'task-download-length': 'Скачано', 'task-upload-length': 'Загружено', 'task-ratio': 'Соотношение', 'task-peer-host': 'Хозяин', 'task-peer-ip': 'IP', 'task-peer-client': 'Клиент', 'navigate-to-downloading': 'Перейти к загрузке', 'show-advanced-options': 'Расширенные опции', 'copyright-warning': 'Предупреждение об авторских правах', 'copyright-warning-message': 'Файл, который вы пытаетесь загрузить, имеет запись об авторских правах на видео или аудио контент, пожалуйста проверьте, имеете ли вы права на загрузку этого файла.', 'copyright-yes': 'Да, у меня есть права', 'copyright-no': 'Нет, у меня нет прав', 'copyright-error-message': 'Ошибка при добавлении задачи из-за проблем с авторскими правами', 'pause-task-success': 'Успешно остановлено задание "{{taskName}}"', 'pause-task-fail': 'Ошибка во время остановки задания "{{taskName}}"', 'resume-task': 'Возобновить задание', 'resume-task-success': 'Успешно возобновлено задание "{{taskName}}"', 'resume-task-fail': 'Ошибка во время возобновления задания "{{taskName}}"', 'delete-task': 'Удалить задание', 'delete-selected-tasks': 'Удалить выбранные задания', 'delete-task-confirm': 'Вы уверены, что хотите удалить задание "{{taskName}}"?', 'batch-delete-task-confirm': 'Вы уверены, что хотите удалить {{count}} задач загрузки в пакетном режиме?', 'delete-task-label': 'Удалить вместе с файлами', 'delete-task-success': 'Успешно удалено задание "{{taskName}}"', 'delete-task-fail': 'Ошибка во время удаления задания "{{taskName}}"', 'remove-task-file-fail': 'Ошибка во время удаления файла(ов) задания. Пожалуйста, удалите его (их) самостоятельно', 'remove-task-config-file-fail': 'Ошибка при удалении файла конфигурации задания. Пожалуйста, удалите его самостоятельно', 'move-task-up': 'Переместить задание вверх', 'move-task-down': 'Переместить задание вниз', 'pause-all-task': 'Приостановить все задания', 'pause-all-task-success': 'Успешно приостановлены все задания', 'pause-all-task-fail': 'Ошибка во время остановки всех заданий', 'resume-all-task': 'Возобновить все задания', 'resume-all-task-success': 'Успешно возобновлены все задания', 'resume-all-task-fail': 'Ошибка во время возобновления всех заданий', 'select-all-task': 'Выберите все задачи', 'clear-recent-tasks': 'Очистить последние задания', 'purge-record': 'Очистить записи о заданиях', 'purge-record-success': 'Успешно очищены записи о заданиях', 'purge-record-fail': 'Ошибка при очистке записей о заданиях', 'batch-delete-task-success': 'Успешно удалить задачи в пакетном режиме', 'batch-delete-task-fail': 'Не удалось удалить задачи в пакетном режиме', 'refresh-list': 'Обновить список заданий', 'no-task': 'Нет текущих заданий', 'copy-link': 'Копировать ссылку', 'copy-link-success': 'Успешно скопированна ссылка', 'remove-record': 'Удалить запись про задание', 'remove-record-confirm': 'Вы уверены, что хотите удалить запись про задание "{{taskName}}"?', 'remove-record-label': 'Удалить вместе с файлами', 'remove-record-success': 'Успешно удалена запись про задание "{{taskName}}"', 'remove-record-fail': 'Ошибка при удалении записи про задание "{{taskName}}"', 'show-in-folder': 'Отобразить файлы заданий в папке', 'file-not-exist': 'Запрошенный файл не существует или был удален', 'file-path-error': 'Ошибка в пути к файлу', 'opening-task-message': 'Открытие "{{taskName}}" ...', 'get-task-name': 'Получить имя задания...', 'remaining-prefix': 'Осталось', 'select-torrent': 'Перетяните torrent файл сюда, или нажмите выбрать', 'task-info-dialog-title': '{{title}} Детали', 'download-start-message': 'Началась загрузка {{taskName}}', 'download-pause-message': 'Приостановить загрузку {{taskName}}', 'download-stop-message': 'Остановить загрузку {{taskName}}', 'download-error-message': 'Ошибка во время загрузки {{taskName}}', 'download-complete-message': 'Завершена загрузка {{taskName}}', 'download-complete-notify': 'Загрузка завершена', 'bt-download-complete-message': 'Завершена загрузка {{taskName}}, раздача', 'bt-download-complete-notify': 'BT Загрузка завершена, раздача...', 'bt-download-complete-tips': 'Совет: Вы можете остановить задачу, чтобы остановить раздачу', 'bt-stopping-seeding-tip': 'Остановка посева, потребуется некоторое время, чтобы отключиться, пожалуйста, подождите...', 'download-fail-message': 'Не удалось загрузить {{taskName}}', 'download-fail-notify': 'Ошибка загрузки' } ================================================ FILE: src/shared/locales/ru/window.js ================================================ export default { 'reload': 'Перезагрузить', 'close': 'Закрыть', 'minimize': 'Свернуть', 'zoom': 'Увеличение', 'toggle-fullscreen': 'Перейти в полноэкранный режим', 'front': 'Поверх всех окон' } ================================================ FILE: src/shared/locales/th/about.js ================================================ export default { 'engine-version': 'เวอร์ชั่นเอ็นจิ้น', 'license': 'ใบอนุญาต', 'about': 'เกี่ยวกับ', 'release': 'ปล่อยเวอร์ชั่น', 'support': 'การสนับสนุน' } ================================================ FILE: src/shared/locales/th/app.js ================================================ export default { 'task-list': 'งาน', 'add-task': 'เพิ่มงาน', 'about': 'เกี่ยวกับ Motrix', 'preferences': 'ปรับแต่ง...', 'check-for-updates': 'ตรวจสอบการอัพเดต...', 'check-updates-now': 'ตรวสอบตอนนี้', 'checking-for-updates': 'กำลังตรวจสอบการอัพเดท ...', 'check-for-updates-title': 'ตรวจสอบการอัพเดท', 'update-available-message': 'มีเวอร์ชั่นใหม่ให้พร้อมใช้งาน อัพเดทตอนี้หรือไม่?', 'update-not-available-message': 'คุณใช้เวอร์ชั่นปัจจุบันแล้ว', 'update-downloaded-message': 'พร้อมติดตั้ง...', 'update-error-message': 'อัพเดทล้มเหลว', 'engine-damaged-message': 'เอ็นจิ้นเกิดความเสียหาย กรุณาติดตั้งใหม่อีกครั้ง : (', 'engine-missing-message': 'ไม่พบเอ็นจิ้น กรุณาติดตั้งใหม่อีกครั้ง : (', 'system-error-title': 'ระบบผิดพลาด', 'system-error-message': 'การเริ่มต้นแอปพลิเคชันล้มเหลว: {{message}}', 'hide': 'ซ่อน Motrix', 'hide-others': 'ซ่อนอย่างอืน', 'unhide': 'แสดงทั้งหมด', 'show': 'แสดง Motrix', 'quit': 'ออก Motrix', 'under-development-message': 'ขออภัย ฟีเจอร์นี้อยู่ระหว่างการพัฒนา...', 'yes': 'ใช่', 'no': 'ไม่', 'save': 'บันทึก', 'reset': 'ทิ้ง', 'cancel': 'ยกเลิก', 'submit': 'ส่ง', 'gt1d': '> 1 วัน', 'hour': 'ชม.', 'minute': 'น.', 'second': 'วิ' } ================================================ FILE: src/shared/locales/th/edit.js ================================================ export default { 'undo': 'ไม่ทำ', 'redo': 'ทำซ้ำ', 'cut': 'ตัด', 'copy': 'คัดลอก', 'paste': 'วาง', 'delete': 'ลบทิ้ง', 'select-all': 'เลือกทั้งหมด' } ================================================ FILE: src/shared/locales/th/help.js ================================================ export default { 'official-website': 'เว็บไซต์ Motrix', 'manual': 'คู่มือ', 'release-notes': 'บันทึกประจำรุ่น...', 'report-problem': 'รายงานปัญหา', 'toggle-dev-tools': 'สลับไปยังเครื่องมือสำหรับนักพัฒนา' } ================================================ FILE: src/shared/locales/th/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/th/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'ไฟล์', 'task': 'งาน', 'edit': 'แก้ไข', 'window': 'หน้าต่าง', 'help': 'ช่วยเหลือ' } ================================================ FILE: src/shared/locales/th/preferences.js ================================================ export default { 'basic': 'พื้นฐาน', 'advanced': 'ขั้นสูง', 'lab': 'ห้องทดลอง', 'save': 'บันทึกและนำไปใช้', 'save-success-message': 'บันทึกการตั้งค่าเรียบร้อยแล้ว', 'save-fail-message': 'บันทึกการตั้งค่าไม่สำเร็จ', 'discard': 'ทิ้ง', 'startup': 'เริ่มขึ้น', 'open-at-login': 'เปิดเมื่อเข้าสู่ระบบ', 'keep-window-state': 'รักษาขนาดและตำแหน่งของหน้าต่างเมื่อออก', 'auto-resume-all': 'ทำงานที่ยังไม่เสร็จทั้งหมดต่อโดยอัตโนมัติ', 'default-dir': 'เส้นทางเริ่มต้น', 'mas-default-dir-tips': 'เนื่องจากข้อจำกัดการอนุญาตแซนด์บ็อกซ์ของ App Store แนะนำให้ตั้งค่าไดเร็กทอรีดาวน์โหลดเริ่มต้นเป็น ~/Downloads', 'transfer-settings': 'การส่งผ่าน', 'transfer-speed-upload': 'จำกัดการอัพโหลด', 'transfer-speed-download': 'จำกัดการดาวน์โหลด', 'transfer-speed-unlimited': 'ไม่จำกัด', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'บันทึกลิงก์แม่เหล็กเป็นไฟล์ทอร์เรนต์', 'bt-auto-download-content': 'ดาวน์โหลดเนื้อหาแม่เหล็กและทอร์เรนต์โดยอัตโนมัติ', 'bt-force-encryption': 'บังคับการเข้ารหัส BT', 'keep-seeding': 'เก็บ Seed ไว้จนกว่าจะหยุดเอง', 'seed-ratio': 'เวลาแบ่งปัน BT', 'seed-time': 'อัตราส่วนแบ่ง BT', 'seed-time-unit': 'นาที', 'task-manage': 'การจัดการงาน', 'max-concurrent-downloads': 'งานที่ใช้งานสูงสุด', 'max-connection-per-server': 'การเชื่อมต่อสูงสุดต่อเซิร์ฟเวอร์', 'new-task-show-downloading': 'แสดงการดาวน์โหลดโดยอัตโนมัติหลังจากเพิ่มงาน', 'no-confirm-before-delete-task': 'ไม่จำเป็นต้องมีการยืนยันก่อนที่จะลบงาน', 'continue': 'ต่อ', 'task-completed-notify': 'แจ้งเตือนหลังดาวน์โหลดเสร็จสิ้น', 'auto-purge-record': 'ล้างบันทึกการดาวน์โหลดโดยอัตโนมัติเมื่อออกจากแอป', 'ui': 'UI', 'appearance': 'รูปร่าง', 'theme-auto': 'อัตโนมัติ', 'theme-light': 'สว่าง', 'theme-dark': 'มืด', 'auto-hide-window': 'ซ่อนหน้าต่างอัตโนมัติ', 'run-mode': 'เรียกใช้ด้วย', 'run-mode-standard': 'แอปพลิเคชั่นปกติ', 'run-mode-tray': 'แถบแจ้งเตือนแอปพลิเคชัน', 'run-mode-hide-tray': 'ซ่อนแอปพลิเคชันถาด', 'tray-speedometer': 'ถาดแถบเมนูแสดงความเร็วแบบเรียลไทม์', 'show-progress-bar': 'แสดงแถบความคืบหน้าการดาวน์โหลด', 'language': 'ภาษา', 'change-language': 'เปลี่ยนภาษา', 'hide-app-menu': 'ซ่อนเมนูแอป (Windows & Linux เท่านั้น)', 'proxy': 'พร็อกซี่', 'enable-proxy': 'เปิดใช้งานพร็อกซี่', 'proxy-bypass-input-tips': 'ข้ามการตั้งค่าพร็อกซีสำหรับโฮสต์และโดเมนเหล่านี้ หนึ่งรายการต่อบรรทัด', 'proxy-scope-download': 'ดาวน์โหลด', 'proxy-scope-update-app': 'อัปเดตแอปพลิเคชัน', 'proxy-scope-update-trackers': 'อัปเดตแทร็กเกอร์', 'proxy-tips': 'ดูคู่มือพร็อกซี', 'bt-tracker': 'เซิร์ฟเวอร์ติดตาม', 'bt-tracker-input-tips': 'เซิร์ฟเวอร์ตัวติดตาม หนึ่งตัวต่อบรรทัด', 'bt-tracker-tips': 'ขอแนะนำ: ', 'sync-tracker-tips': 'ซิงค์', 'auto-sync-tracker': 'อัพเดทรายการตัวติดตามทุกวันโดยอัตโนมัติ', 'port': 'Listen Ports', 'bt-port': 'BT Listen Port', 'dht-port': 'DHT Listen Port', 'security': 'ความปลอดภัย', 'rpc': 'RPC', 'rpc-listen-port': 'พอร์ตฟัง RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'ดูคู่มือลับ RPC', 'developer': 'นักพัฒนา', 'user-agent': 'User-Agent', 'mock-user-agent': 'จำลอง User-Agent', 'aria2-conf-path': 'เส้นทาง aria2.conf ที่ฝังอยู่', 'app-log-path': 'เส้นทางบันทึกแอป', 'download-session-path': 'เส้นทางดาวน์โหลดเซสชัน', 'session-reset': 'รีเซ็ตเซสชั่นการดาวน์โหลด', 'session-reset-confirm': 'คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตเซสชันการดาวน์โหลด', 'factory-reset': 'ตั้งค่าจากโรงงาน', 'factory-reset-confirm': 'คุณแน่ใจหรือไม่ว่าต้องการเปลี่ยนกลับเป็นการตั้งค่าจากโรงงาน', 'lab-warning': '⚠️ การเปิดใช้งานฟีเจอร์แล็บอาจส่งผลให้แอปขัดข้องหรือข้อมูลสูญหาย ตัดสินใจยอมรับความเสี่ยงเอง!', 'download-protocol': 'โปรโตคอล', 'protocols-default-client': 'ตั้งเป็นไคลเอนต์เริ่มต้นสำหรับโปรโตคอลต่อไปนี้', 'protocols-magnet': 'แม่เหล็ก [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'ส่วนขยาย', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'มอบให้โดยชุมชน, ', 'baidu-exporter-help': 'คลิกที่นี่เพื่อใช้งาน', 'auto-update': 'อัพเดทอัตโนมัติ', 'auto-check-update': 'ตรวจสอบการอัพเดทอัตโนมัติ', 'last-check-update-time': 'ตรวจสอบการอัพเดทครั้งล่าสุด' } ================================================ FILE: src/shared/locales/th/subnav.js ================================================ export default { 'task-list': 'งาน', 'preferences': 'ปรับแต่ง' } ================================================ FILE: src/shared/locales/th/task.js ================================================ export default { 'active': 'การดาวน์โหลด', 'waiting': 'กำลังรอ', 'stopped': 'หยุดแล้ว', 'new-task': 'งานใหม่', 'new-bt-task': 'งาน BT ใหม่', 'open-file': 'เปิดไฟล์ทอร์เรนต์...', 'uri-task': 'URL', 'torrent-task': 'ทอร์เรนต์', 'uri-task-tips': 'URL หนี่งงานต่อบรรทัด (รองรับแม่เหล็ก)', 'thunder-link-tips': 'เคล็ดลับ: ลิงก์ Thunder อาจไม่สามารถดาวน์โหลดได้หลังจากถอดรหัส', 'new-task-uris-required': 'โปรดป้อน URL ทรัพยากรที่ถูกต้องอย่างน้อยหนึ่งรายการ', 'new-task-torrent-required': 'โปรดเลือกไฟล์ทอร์เรนต์', 'file-name': 'ชื่อ', 'file-extension': 'นามสกุลไฟล์', 'file-size': 'ขนาด', 'file-completed-size': 'สำเร็จ', 'selected-files-sum': 'เลือกแล้ว: {{selectedFilesCount}} ไฟล์, ขนามรวม {{selectedFilesTotalSize}}', 'select-at-least-one': 'โปรดเลือกอย่างน้อยหนึ่งไฟล์', 'task-gid': 'GID', 'task-name': 'ชื่องาน', 'task-out': 'เปลี่ยนชื่อ', 'task-out-tips': 'ไม่จำเป็น', 'task-split': 'แบ่ง', 'task-dir': 'บันทึกไปที่', 'pause-task': 'พักงาน', 'task-ua': 'UA', 'task-user-agent': 'ตัวแทนผู้ใช้', 'task-authorization': 'การอนุญาต', 'task-referer': 'ผู้อ้างอิง', 'task-cookie': 'คุ้กกี้', 'task-proxy': 'พร็อกซี่', 'task-error-info': 'ผิดพลาด', 'task-piece': 'ชิ้นส่วน', 'task-piece-length': 'ขนาดชิ้น', 'task-num-pieces': 'ชิ้นส่วน', 'task-bittorrent-info': 'ข้อมูลทอร์เรนต์', 'task-info-hash': 'แฮช', 'task-bittorrent-creation-date': 'วันที่สร้าง', 'task-bittorrent-comment': 'ความคิดเห็น', 'task-progress-info': 'ความคืบหน้า', 'task-status': 'สถานะ', 'task-num-seeders': 'Seeders', 'task-connections': 'การเชื่อมต่อ', 'task-file-size': 'ขนาด', 'task-download-speed': 'ความเร็วดาวน์โหลด', 'task-upload-speed': 'ความเร็วอัพโหลด', 'task-download-length': 'ดาวน์โหลดแล้ว', 'task-upload-length': 'อัพโหลดแล้ว', 'task-ratio': 'อัตรา', 'task-peer-host': 'โฮสต์', 'task-peer-ip': 'IP', 'task-peer-client': 'ไคลเอนต์', 'navigate-to-downloading': 'นำทางไปยังการดาวน์โหลด', 'show-advanced-options': 'ตัวเลือกขั้นสูง', 'copyright-warning': 'คำเตือนเรื่องลิขสิทธิ์', 'copyright-warning-message': 'ไฟล์ที่คุณต้องการดาวน์โหลดอาจเป็นไฟล์เสียงหรือวิดีโอที่มีลิขสิทธิ์ โปรดตรวจสอบให้แน่ใจว่าคุณมีสิทธิ์ในการเข้าถึงไฟล์ดังกล่าว', 'copyright-yes': 'ใช่ ขออนุญาติแล้ว', 'copyright-no': 'ไม่ ฉันไม่ได้รับอนุญาต', 'copyright-error-message': 'ไม่สามารถเพิ่มงานเนื่องจากปัญหาลิขสิทธิ์', 'pause-task-success': 'หยุดงานชั่วคราวสำเร็จ "{{taskName}}"', 'pause-task-fail': 'ไม่สามารถหยุดงานชั่วคราว "{{taskName}}"', 'resume-task': 'ทำงานต่อ', 'resume-task-success': 'กลับมาทำงานต่อได้สำเร็จ "{{taskName}}"', 'resume-task-fail': 'ไม่สามารถทำงานต่อได้ "{{taskName}}"', 'delete-task': 'ลบงาน', 'delete-selected-tasks': 'ลบงานที่เลือก', 'delete-task-confirm': 'คุณแน่ใจหรือว่าต้องการลบงานดาวน์โหลด "{{taskName}}"?', 'batch-delete-task-confirm': 'คุณแน่ใจหรือว่าต้องการลบดาวน์โหลด {{count}} งานในชุด?', 'delete-task-label': 'ลบพร้อมไฟล์', 'delete-task-success': 'ลบงานเรียบร้อยแล้ว "{{taskName}}"', 'delete-task-fail': 'ไม่สามารถลบงาน "{{taskName}}"', 'remove-task-file-fail': 'ลบไฟล์งานไม่สำเร็จ โปรดลบด้วยตนเอง', 'remove-task-config-file-fail': 'ลบไฟล์กำหนดค่างานไม่สำเร็จ โปรดลบด้วยตนเอง', 'move-task-up': 'ย้ายงานขึ้น', 'move-task-down': 'ย้ายงานลง', 'pause-all-task': 'หยุดงานทั้งหมดชั่วคราว', 'pause-all-task-success': 'หยุดงานทั้งหมดชั่วคราวเรียบร้อยแล้ว', 'pause-all-task-fail': 'ไม่สามารถหยุดงานทั้งหมดชั่วคราว', 'resume-all-task': 'ทำงานทั้งหมดต่อ', 'resume-all-task-success': 'กลับมาทำงานทั้งหมดต่อได้สำเร็จ', 'resume-all-task-fail': 'ไม่สามารถทำงานทั้งหมดต่อได้', 'select-all-task': 'เลือกงานทั้งหมด', 'clear-recent-tasks': 'ล้างงานล่าสุด', 'purge-record': 'ล้างบันทึกงาน', 'purge-record-success': 'ลบบันทึกงานเรียบร้อยแล้ว', 'purge-record-fail': 'ไม่สามารถล้างบันทึกงาน', 'batch-delete-task-success': 'ลบงานในแบตช์สำเร็จ', 'batch-delete-task-fail': 'ไม่สามารถลบงานในชุดงาน', 'refresh-list': 'รีเฟรชรายการงาน', 'no-task': 'ไม่มีงานปัจจุบัน', 'copy-link': 'คัดลอกลิงค์', 'copy-link-success': 'คัดลอกลิงค์สำเร็จ', 'remove-record': 'ลบบันทึกงาน', 'remove-record-confirm': 'คุณแน่ใจหรือว่าต้องการลบบันทึกการดาวน์โหลดสำหรับ "{{taskName}}"?', 'remove-record-label': 'ลบด้วยไฟล์', 'remove-record-success': 'ลบบันทึกงานสำหรับ "{{taskName}}" เรียบร้อยแล้ว', 'remove-record-fail': 'ไม่สามารถลบบันทึกงานสำหรับ "{{taskName}}"', 'show-in-folder': 'แสดงงานในโฟลเดอร์', 'file-not-exist': 'ไฟล์เป้าหมายไม่มีอยู่หรือถูกลบไปแล้ว', 'file-path-error': 'ข้อผิดพลาดเส้นทางไฟล์', 'opening-task-message': 'กำลังเปิด "{{taskName}}" ...', 'get-task-name': 'รับชื่องาน...', 'remaining-prefix': 'ที่เหลืออยู่', 'select-torrent': 'ลากไฟล์ทอร์เรนต์มาที่นี่ หรือคลิกเพื่อเลือก', 'task-detail-title': 'รายละเอียดงาน', 'task-info-dialog-title': 'รายละเอียด {{title}}', 'download-start-message': 'เริ่มดาวน์โหลดแล้ว {{taskName}}', 'download-pause-message': 'หยุดการดาวน์โหลดชั่วคราว {{taskName}}', 'download-stop-message': 'หยุดดาวน์โหลด {{taskName}}', 'download-error-message': 'เกิดข้อผิดพลาดขณะดาวน์โหลด {{taskName}}', 'download-complete-message': 'ดาวน์โหลดเสร็จแล้ว {{taskName}}', 'download-complete-notify': 'ดาวน์โหลดเสร็จแล้ว', 'bt-download-complete-message': 'ดาวน์โหลดเสร็จแล้ว {{taskName}}, กำลัง seed', 'bt-download-complete-notify': 'BT ดาวน์โหลดเสร็จแล้ว, กำลัง seed...', 'bt-download-complete-tips': 'เคล็ดลับ: คุณสามารถหยุดงานเพื่อสิ้นสุดการ seed ได้', 'bt-stopping-seeding-tip': 'กำลังหยุดการ seed จะใช้เวลาสักพักในการตัดการเชื่อมต่อ โปรดรอสักครู่...', 'download-fail-message': 'ไม่สามารถดาวน์โหลด {{taskName}}', 'download-fail-notify': 'ดาวน์โหลดล้มเหลว' } ================================================ FILE: src/shared/locales/th/window.js ================================================ export default { 'reload': 'โหลดใหม่', 'close': 'ปิด', 'minimize': 'ย่อหน้าต่างเล็ก', 'zoom': 'ขยาย', 'toggle-fullscreen': 'เข้าสู่เต็มหน้าจอ', 'front': 'นำทั้งหมดมาไว้ข้างหน้า' } ================================================ FILE: src/shared/locales/tr/about.js ================================================ export default { 'engine-version': 'Engine Versiyonu', 'license': 'Lisans', 'about': 'Hakkında', 'release': 'Sürüm', 'support': 'Destek' } ================================================ FILE: src/shared/locales/tr/app.js ================================================ export default { 'task-list': 'Görevler', 'add-task': 'Görev Ekle', 'about': 'Motrix Hakkında', 'preferences': 'Ayarlar...', 'check-for-updates': 'Güncellemeleri kontrol et...', 'check-updates-now': 'Şimdi kontrol et', 'checking-for-updates': 'Güncellemeleri kontrol ediyor ...', 'check-for-updates-title': 'Güncellemeleri kontrol et', 'update-available-message': 'Motrix\'in yeni bir sürümü var, şimdi güncelle?', 'update-not-available-message': 'Gündemdesin!', 'update-downloaded-message': 'Yüklemeye hazır...', 'update-error-message': 'Güncelleme Hatası', 'engine-damaged-message': 'Motor hasarlı, lütfen tekrar takın : (', 'engine-missing-message': 'Motor eksik, lütfen tekrar takın : (', 'system-error-title': 'Sistem hatası', 'system-error-message': 'Uygulama başlatılamadı: {{message}}', 'hide': 'Motrix\'i gizle', 'hide-others': 'Diğerlerini gizle', 'unhide': 'Hepsini Göster', 'show': 'Motrix\'i Göster', 'quit': 'Motrix\'ten çık', 'under-development-message': 'Üzgünüz, bu özellik geliştirme aşamasında...', 'yes': 'Evet', 'no': 'Hayır', 'save': 'Kayıt etmek', 'reset': 'İptal Et', 'cancel': 'İptal', 'submit': 'Gönder', 'gt1d': '> 1 gün', 'hour': 'S', 'minute': 'd', 'second': 's' } ================================================ FILE: src/shared/locales/tr/edit.js ================================================ export default { 'undo': 'Geri al', 'redo': 'Yinele', 'cut': 'Kes', 'copy': 'Kopyala', 'paste': 'Yapıştır', 'delete': 'Sil', 'select-all': 'Hepsini seç' } ================================================ FILE: src/shared/locales/tr/help.js ================================================ export default { 'official-website': 'Motrix Web Sitesi', 'manual': 'Kılavuz', 'release-notes': 'Sürüm Notları...', 'report-problem': 'Sorun bildir', 'toggle-dev-tools': 'Geliştirici Aracına geçiş yap' } ================================================ FILE: src/shared/locales/tr/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/tr/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Dosya', 'task': 'Görev', 'edit': 'Düzenle', 'window': 'Pencere', 'help': 'Yardım' } ================================================ FILE: src/shared/locales/tr/preferences.js ================================================ export default { 'basic': 'Temel', 'advanced': 'Gelişmiş', 'lab': 'Deneysel', 'save': 'Kaydet & Uygula', 'save-success-message': 'Tercihleri başarıyla kaydedin', 'save-fail-message': 'Tercihleri kaydetme başarısız oldu', 'discard': 'İptal Et', 'startup': 'Başlangıçta', 'open-at-login': 'Giriş sırasında aç', 'keep-window-state': 'Pencerenin boyutunu ve konumunu geri yükleyin', 'auto-resume-all': 'Tüm bitmemiş görevleri otomatik olarak devam ettir', 'default-dir': 'Varsayılan Klasör', 'mas-default-dir-tips': 'App Store\'un sanal alan izinleri kısıtlamaları nedeniyle, varsayılan indirme dizininin İndirilenler dizinine ayarlanması önerilir.', 'transfer-settings': 'İletim', 'transfer-speed-upload': 'Yükleme limiti', 'transfer-speed-download': 'İndirme limiti', 'transfer-speed-unlimited': 'Sınırsız', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Mıknatıs bağlantısını torrent dosyası olarak kaydedin', 'bt-auto-download-content': 'Magnet ve torrent içeriğini otomatik olarak indirin', 'bt-force-encryption': 'BT Zorunlu Şifreleme', 'keep-seeding': 'Manuel olarak durdurana kadar tohumlamaya devam edin', 'seed-ratio': 'Tohum Oranı', 'seed-time': 'Tohum Zamanı', 'seed-time-unit': 'dakika', 'task-manage': 'Görev Yöneticisi', 'max-concurrent-downloads': 'Maksimum aktif görev', 'max-connection-per-server': 'Sunucu başına maksimum bağlantı', 'new-task-show-downloading': 'Görev ekledikten sonra indirmeyi otomatik göster', 'no-confirm-before-delete-task': 'Görevi silmeden önce onay gerekmez', 'continue': 'Devamlı', 'task-completed-notify': 'İndirme bittikten sonra bildirim göster', 'auto-purge-record': 'Auto purge download record when app exit', 'ui': 'Kullanıcı Arayüzü', 'appearance': 'Görünüş', 'theme-auto': 'Otomatik', 'theme-light': 'Açık', 'auto-hide-window': 'Pencereleri otomatik gizle', 'run-mode': 'Olarak çalıştırmak', 'run-mode-standard': 'Standart Uygulama', 'run-mode-tray': 'Tepsi Uygulaması', 'theme-dark': 'Koyu', 'run-mode-hide-tray': 'Tepsi uygulamasını gizle', 'tray-speedometer': 'Menü çubuğu tepsisi gerçek zamanlı hızı gösterir', 'show-progress-bar': 'İndirme ilerleme çubuğunu göster', 'language': 'Dil', 'change-language': 'Dil değiştir', 'hide-app-menu': 'Uygulama menüsünü göster (Windows & Linux için)', 'proxy': 'Proxy', 'enable-proxy': 'Proxy etkinleştir', 'proxy-bypass-input-tips': 'Her bir satırda bir tane olacak şekilde bu Ana Bilgisayarlar ve Alanlar için proxy ayarlarını atlayın', 'proxy-scope-download': 'İndirme', 'proxy-scope-update-app': 'Uygulamayı Güncelle', 'proxy-scope-update-trackers': 'İzleyicileri Güncelle', 'proxy-tips': 'Proxy Kılavuzunu Görüntüle', 'bt-tracker': 'İzleyici Sunucular', 'bt-tracker-input-tips': 'İzleyici sunucusu, her satıra bir tane', 'bt-tracker-tips': 'Tavsiye et:', 'sync-tracker-tips': 'Senkronizasyon', 'auto-sync-tracker': 'İzleyici listesini her gün otomatik olarak güncelleyin', 'port': 'Bağlantı noktalarını dinleyin', 'bt-port': 'BT dinleme bağlantı noktası', 'dht-port': 'DHT dinleme bağlantı noktası', 'security': 'Güvenlik', 'rpc': 'RPC', 'rpc-listen-port': 'RPC Dinleme Portu', 'rpc-secret': 'RPC sırrı', 'rpc-secret-tips': 'RPC gizli kılavuzunu görüntüle', 'developer': 'Geliştirici', 'user-agent': 'User-Agent', 'mock-user-agent': 'Sahte Kullanıcı Kimliği (User-Agent)', 'aria2-conf-path': 'Dahili aria2.conf yolu', 'app-log-path': 'Uygulama log yolu', 'download-session-path': 'Oturum yolunu indir', 'factory-reset': 'Fabrika ayarlarına dön', 'factory-reset-confirm': 'Fabrika ayarlarına geri dönmek istediğinize emin misiniz?', 'lab-warning': '⚠️ Deneysel özellikleri etkinleştirmek uygulamanın çökmesine veya veri kaybına neden olabilir, kendiniz karar verin!', 'download-protocol': 'Protokol', 'protocols-default-client': 'Aşağıdaki protokoller için varsayılan istemci olarak ayarla', 'protocols-magnet': 'Mıknatıs [ magnet:// ]', 'protocols-thunder': 'gök gürültüsü [ thunder:// ]', 'browser-extensions': 'Eklentiler', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Topluluk tarafından sağlanan, ', 'baidu-exporter-help': 'Kullanım detayları için buraya tıklayın', 'auto-update': 'Otomatik güncelleme', 'auto-check-update': 'Otomatik Kontrol Güncellemesi', 'last-check-update-time': 'Son Kontrol Güncelleme Saati', 'not-saved': 'Tercihler kaydedilmedi', 'not-saved-confirm': 'Değiştirilen tercihler kaybolacak, ayrılacağınızdan emin misiniz?' } ================================================ FILE: src/shared/locales/tr/subnav.js ================================================ export default { 'task-list': 'Görevler', 'preferences': 'Ayarlar' } ================================================ FILE: src/shared/locales/tr/task.js ================================================ export default { 'active': 'İndiriliyor', 'waiting': 'Bekleniyor', 'stopped': 'Durdu', 'new-task': 'Yeni Görev', 'new-bt-task': 'Yeni BT Görevi', 'open-file': 'Torrent Dosyasını Aç...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Her bir satır için bir görev (magnet destekli)', 'thunder-link-tips': 'İpucu: Thunder bağlantıları kod çözme işleminden sonra indirilemeyebilir', 'new-task-uris-required': 'Lütfen en az bir geçerli kaynak URL adresi girin', 'new-task-torrent-required': 'Lütfen bir torrent dosyası seçin', 'file-name': 'Dosya Adı', 'file-extension': 'uzantı', 'file-size': 'Boyut', 'file-completed-size': 'İndirildi', 'selected-files-sum': 'Seçildi: {{selectedFilesCount}} dosya sayısı, total {{selectedFilesTotalSize}}', 'select-at-least-one': 'Lütfen en az bir dosya seçin', 'task-gid': 'GID', 'task-name': 'Görev Adı', 'task-out': 'Dosya Adı', 'task-out-tips': 'Opsiyonel', 'task-split': 'Parça', 'task-dir': 'Yol', 'pause-task': 'Görevi Durdur', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Yetkilendirme', 'task-referer': 'Yönlendiren', 'task-cookie': 'Çerez', 'task-proxy': 'Proxy', 'task-error-info': 'Hata', 'task-piece': 'Parça', 'task-piece-length': 'Parça Boyutu', 'task-num-pieces': 'Adet', 'task-bittorrent-info': 'Torrent Bilgisi', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Oluşturulma tarihi', 'task-bittorrent-comment': 'Yorum Yap', 'task-progress-info': 'İlerleme', 'task-status': 'Durum', 'task-num-seeders': 'Ekme makineleri', 'task-connections': 'Bağlantılar', 'task-file-size': 'Boyut', 'task-download-speed': 'İndirme hızı', 'task-upload-speed': 'Yükleme hızı', 'task-download-length': 'İndirildi', 'task-upload-length': 'Yüklendi', 'task-ratio': 'Oran', 'task-peer-host': 'Ev sahibi', 'task-peer-ip': 'IP', 'task-peer-client': 'Müşteri', 'navigate-to-downloading': 'İndirilenlere git', 'show-advanced-options': 'Gelişmiş ayarlar', 'copyright-warning': 'Telif Uyarısı', 'copyright-warning-message': 'İndirmek istediğiniz dosya telif hakkıyla korunan ses veya videoya ait olabilir, lütfen telif hakkı lisansına sahip olduğunuzdan emin olun.', 'copyright-yes': 'Evet, sahibim', 'copyright-no': 'Hayır', 'copyright-error-message': 'Telif hakkı sorunları nedeniyle görev ekleme işlemi başarısız oldu', 'pause-task-success': '"{{taskName}}" görevi durduruldu', 'pause-task-fail': '"{{taskName}}" görevi durdurulamadı', 'resume-task': 'Görevi sürdür', 'resume-task-success': '"{{taskName}}" devam ettirildi', 'resume-task-fail': '"{{taskName}}" devam ettirilemedi', 'delete-task': 'Görevi sil', 'delete-selected-tasks': 'Seçilen görevleri sil', 'delete-task-confirm': '"{{taskName}}" adlı görevi silmek istediğinizden emin misiniz?', 'batch-delete-task-confirm': '{{count}} indirme görevini toplu olarak kaldırmak istediğinizden emin misiniz?', 'delete-task-label': 'Dosyalar ile birlikte sil', 'delete-task-success': '"{{taskName}}" silindi', 'delete-task-fail': '"{{taskName}}" silinemedi', 'remove-task-file-fail': 'Görev dosyaları silinemedi, lütfen manuel olarak silin', 'remove-task-config-file-fail': 'Görev yapılandırma dosyası silinemedi, lütfen manuel olarak silin', 'move-task-up': 'Görevi yukarı taşı', 'move-task-down': 'Görevi aşağı taşı', 'pause-all-task': 'Bütün görevleri durdur', 'pause-all-task-success': 'Bütün görevleri durdurma başarılı', 'pause-all-task-fail': 'Bütün görevleri durdurma başarısız', 'resume-all-task': 'Bütün görevleri sürdür', 'resume-all-task-success': 'Bütün görevleri sürdürme başarılı', 'resume-all-task-fail': 'Bütün görevleri sürdürme başarısız', 'select-all-task': 'Tüm görevi seçin', 'clear-recent-tasks': 'Son görevleri temizle', 'purge-record': 'Görev Kaydını Temizle', 'purge-record-success': 'Görev Kaydını Temizleme başarılı', 'purge-record-fail': 'Görev Kaydını Temizleme başarısız', 'batch-delete-task-success': 'Toplu işteki görevleri başarıyla silin', 'batch-delete-task-fail': 'Toplu işteki görevler silinemedi', 'refresh-list': 'Görev listesini yenile', 'no-task': 'Görev yok', 'copy-link': 'Bağlantıyı kopyala', 'copy-link-success': 'Bağlantı Kopyalama başarılı', 'remove-record': 'Görev kaydını kaldır', 'remove-record-confirm': '"{{taskName}}" görevinin kaydın kaldırmak istediğinizden emin misiniz?', 'remove-record-label': 'Dosyalar ile birlikte kaldır', 'remove-record-success': '"{{taskName}}" kayıtları kaldırıldı', 'remove-record-fail': '"{{taskName}}" kayıtları kaldırılamadı', 'show-in-folder': 'Görevi klasörde göster', 'file-not-exist': 'Dosya yok ya da silinmiş', 'file-path-error': 'Dosya yolu hatası', 'opening-task-message': '"{{taskName}}" açılıyor ...', 'get-task-name': 'Görev ismi getir...', 'remaining-prefix': 'Kalan Süre', 'select-torrent': 'Torrent dosyasını sürükleyin ya da seçmek için tıklayın', 'task-info-dialog-title': '{{title}} hakkında bilgiler', 'download-start-message': '{{taskName}} görevi başlatıldı', 'download-pause-message': '{{taskName}} görevi duraklatıldı', 'download-stop-message': '{{taskName}} görevi durduruldu', 'download-error-message': '{{taskName}} görevinde hata oluştu', 'download-complete-message': '{{taskName}} görevi tamamlandı', 'download-complete-notify': 'İndirme bitti', 'bt-download-complete-message': '{{taskName}} indirme tamamlandı, tohumlama...', 'bt-download-complete-notify': 'BT Indirme tamamlandı, tohumlama...', 'bt-download-complete-tips': 'Ipuçları: Eğer tohumlama sona erdirmek için görev durdurabilirsiniz', 'bt-stopping-seeding-tip': 'Ekim işlemini durdurmak, bağlantıyı kesmek biraz zaman alacak, lütfen bekleyin...', 'download-fail-message': '{{taskName}} görevi indirilemedi', 'download-fail-notify': 'İndirme başarısız' } ================================================ FILE: src/shared/locales/tr/window.js ================================================ export default { 'reload': 'Yenile', 'close': 'Kapat', 'minimize': 'Küçült', 'zoom': 'Yakınlaştır', 'toggle-fullscreen': 'Tam ekran yap', 'front': 'Tümünü Öne Getir' } ================================================ FILE: src/shared/locales/uk/about.js ================================================ export default { 'engine-version': 'Версія двигуна', 'license': 'Ліцензія', 'about': 'Інформація', 'release': 'Реліз', 'support': 'Підтримка' } ================================================ FILE: src/shared/locales/uk/app.js ================================================ export default { 'task-list': 'Завдання', 'add-task': 'Додати завдання', 'about': 'Про Motrix', 'preferences': 'Налаштування...', 'check-for-updates': 'Перевірити оновлення...', 'check-updates-now': 'Перевірити зараз', 'checking-for-updates': 'Перевірка оновлень ...', 'check-for-updates-title': 'Перевірити оновлення', 'update-available-message': 'Нова версія Motrix доступна для завантаження, завантажити зараз?', 'update-not-available-message': 'У вас вже є остання версія!', 'update-downloaded-message': 'Готово для встановлення...', 'update-error-message': 'Помилка оновлення', 'engine-damaged-message': 'Двигун пошкоджено, будь ласка переінсталюйте : (', 'engine-missing-message': 'Двигун загублено, будь ласка переінсталюйте : (', 'system-error-title': 'Системна помилка', 'system-error-message': 'Помилка запуску додатка: {{message}}', 'hide': 'Сховати Motrix', 'hide-others': 'Сховати інше', 'unhide': 'Відобразити все', 'show': 'Відобразити Motrix', 'quit': 'Закрити Motrix', 'under-development-message': 'На жаль, ця функція розробляється...', 'yes': 'Так', 'no': 'Ні', 'save': 'Зберегти', 'reset': 'Відмінити', 'cancel': 'Відмінити', 'submit': 'Підтвердити', 'gt1d': '> 1 день', 'hour': 'г', 'minute': 'х', 'second': 'с' } ================================================ FILE: src/shared/locales/uk/edit.js ================================================ export default { 'undo': 'Відміна', 'redo': 'Повторити', 'cut': 'Вирізати', 'copy': 'Копіювати', 'paste': 'Вставити', 'delete': 'Видалити', 'select-all': 'Обрати все' } ================================================ FILE: src/shared/locales/uk/help.js ================================================ export default { 'official-website': 'Сайт Motrix', 'manual': 'Інструкція', 'release-notes': 'Примітки до релизу...', 'report-problem': 'Повідомити про проблему', 'toggle-dev-tools': 'Переключити інстрменти розробника' } ================================================ FILE: src/shared/locales/uk/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/uk/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Файл', 'task': 'Завдання', 'edit': 'Редагування', 'window': 'Вікно', 'help': 'Допомога' } ================================================ FILE: src/shared/locales/uk/preferences.js ================================================ export default { 'basic': 'Основні', 'advanced': 'Розширені', 'lab': 'Лабораторія', 'save': 'Зберігти та Застосувати', 'save-success-message': 'Налаштування збережено успішно', 'save-fail-message': 'Помилка при зберіганні налаштувань', 'discard': 'Відмінити', 'startup': 'Стартап', 'open-at-login': 'Запускати програму разом з запуском операційної системи', 'keep-window-state': 'Під час закриття додатка, зберігати розмір і положення вікна', 'auto-resume-all': 'Автоматично поновлювати всі невиконанні завдання', 'default-dir': 'Шлях за замовчуванням', 'mas-default-dir-tips': 'Через обмеження в App Store, рекомендовано встановити шлях за замовчення ~/Downloads', 'transfer-settings': 'Передача', 'transfer-speed-upload': 'Ліміт вивантаження', 'transfer-speed-download': 'Ліміт завантаження', 'transfer-speed-unlimited': 'Безлімітно', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Зберегти магнітне посилання як торрент-файл', 'bt-auto-download-content': 'Автоматично завантажуйте вміст магніту та торрент', 'bt-force-encryption': 'Обов\'язкова криптографія BT', 'keep-seeding': 'Продовжуйте висівати, поки не зупините його вручну', 'seed-ratio': 'Співвідношення насіння', 'seed-time': 'Час насіння', 'seed-time-unit': 'хвилин', 'task-manage': 'Менеджер завдань', 'max-concurrent-downloads': 'Максимум активних завдань', 'max-connection-per-server': 'Максимум з\'єднання на сервер', 'new-task-show-downloading': 'Автоматично відображати завантаження після додавання завдання', 'no-confirm-before-delete-task': 'Перед видаленням завдання не потрібно підтверджувати', 'continue': 'Продовжити', 'task-completed-notify': 'Повідомлення після завершення завантаження', 'auto-purge-record': 'Автоматично чистити записи про завантаження перед закриттям додатка', 'ui': 'UI', 'appearance': 'Зовнішній вигляд', 'theme-auto': 'Автоматично', 'theme-light': 'Світлий', 'theme-dark': 'Темний', 'auto-hide-window': 'Автозахист вікон', 'run-mode': 'Виконати як', 'run-mode-standard': 'Стандартне застосування', 'run-mode-tray': 'Додаток у лотку', 'run-mode-hide-tray': 'Приховати програму лотка', 'tray-speedometer': 'Лоток панелі меню показує швидкість у режимі реального часу', 'show-progress-bar': 'Показати панель завантаження', 'language': 'Мова', 'change-language': 'Змінити мову', 'hide-app-menu': 'Сховати меню додатка (Тільки для Windows та Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Застосувати Proxy', 'proxy-bypass-input-tips': 'Обхід налаштувань проксі для цих хостів та доменів, по одному на рядок', 'proxy-scope-download': 'Завантажити', 'proxy-scope-update-app': 'Оновити додаток', 'proxy-scope-update-trackers': 'Оновити трекери', 'proxy-tips': 'Перегляньте посібник з проксі', 'bt-tracker': 'Tracker Сервер', 'bt-tracker-input-tips': 'Tracker сервера, один в рядок', 'bt-tracker-tips': 'Рекомендовано: ', 'sync-tracker-tips': 'Сінхронізуватись', 'auto-sync-tracker': 'Щодня оновлюйте список трекерів автоматично', 'port': 'Слухайте порти', 'bt-port': 'Порт прослуховування BT', 'dht-port': 'Порт прослуховування DHT', 'security': 'Безпека', 'rpc': 'RPC', 'rpc-listen-port': 'Порт прослуховування RPC', 'rpc-secret': 'RPC Secret', 'rpc-secret-tips': 'Дивитись інструкцію RPC Secret', 'developer': 'Розробник', 'user-agent': 'User-Agent', 'mock-user-agent': 'Макет User-Agent', 'aria2-conf-path': 'Вбудований шлях до aria2.conf', 'app-log-path': 'Шлях до журналу додатка', 'download-session-path': 'Завантажити шлях сесії', 'factory-reset': 'Налаштування за замовчуванням', 'factory-reset-confirm': 'Ви впевненні, що бажаєте повернутись до налаштувань за замовчуванням?', 'lab-warning': '⚠️ Увімкнення функцій лабораторії може призвести до збою програми або втрати даних, вирішити на власний ризик!', 'download-protocol': 'Протоколи', 'protocols-default-client': 'Встановіть як клієнта за замовчуванням для таких протоколів', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Розширення', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Надається спільнотою, ', 'baidu-exporter-help': 'Натисніть тут для використання', 'auto-update': 'Автоматичне оновлення', 'auto-check-update': 'Автоматично перевіряти оновлення', 'last-check-update-time': 'В останнє оновлення перевірялось', 'not-saved': 'Налаштування не збережено', 'not-saved-confirm': 'Змінені параметри буде втрачено. Ви впевнені, що залишите?' } ================================================ FILE: src/shared/locales/uk/subnav.js ================================================ export default { 'task-list': 'Завдання', 'preferences': 'Налаштування' } ================================================ FILE: src/shared/locales/uk/task.js ================================================ export default { 'active': 'Завантаження', 'waiting': 'Очікування', 'stopped': 'Зупинено', 'new-task': 'Нове завдання', 'new-bt-task': 'Нове BT завдання', 'open-file': 'Відкрити Torrent файл...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Одне URL-задання на рядок (підтримуе magnet)', 'thunder-link-tips': 'Порада: Посилання типу Thunder може не завантажуватися після декодування', 'new-task-uris-required': 'Введіть принаймні один дійсний URL-адресу ресурсу', 'new-task-torrent-required': 'Будь ласка оберіть torrent файл', 'file-name': 'Ім\'я файлу', 'file-extension': 'Тип файлу', 'file-size': 'Розмір', 'file-completed-size': 'Завершений', 'selected-files-sum': 'Обрано: {{selectedFilesCount}} файлів, загальний розмір {{selectedFilesTotalSize}}', 'select-at-least-one': 'Виберіть принаймні один файл', 'task-gid': 'GID', 'task-name': 'Ім\'я завдання', 'task-out': 'Перейменувати', 'task-out-tips': 'Необов\'язковий', 'task-split': 'Розбити', 'task-dir': 'Зберігти в', 'pause-task': 'Призупинити завдання', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Авторизація', 'task-referer': 'Реферал', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Помилка', 'task-piece': 'Шматок', 'task-piece-length': 'Розмір шматка', 'task-num-pieces': 'Шматочки', 'task-bittorrent-info': 'Інформація про торрент', 'task-info-hash': 'Хеш', 'task-bittorrent-creation-date': 'Дата створення', 'task-bittorrent-comment': 'Прокоментуйте', 'task-progress-info': 'Прогрес', 'task-status': 'Статус', 'task-num-seeders': 'Сівалки', 'task-connections': 'Зв\'язки', 'task-file-size': 'Розмір', 'task-download-speed': 'Швидкість завантаження', 'task-upload-speed': 'Швидкість завантаження', 'task-download-length': 'Завантажено', 'task-upload-length': 'Завантажено', 'task-ratio': 'Співвідношення', 'task-peer-host': 'Ведучий', 'task-peer-ip': 'IP', 'task-peer-client': 'Клієнт', 'navigate-to-downloading': 'Перейти до завантаження', 'show-advanced-options': 'Розширенні опції', 'copyright-warning': 'Попередження про авторські права', 'copyright-warning-message': 'Файл який ви намагаєтесь завантажити можливо має авторські права на аудіо або відео, будь ласка перевірте чи ви маєте повноваження на доступ до цього файлу.', 'copyright-yes': 'Так, я маю повноваження', 'copyright-no': 'Ні, в мене нема повноваженнь', 'copyright-error-message': 'Помилка при додаванні завдання, через проблему за авторськими правами', 'pause-task-success': 'Успішно призупинине завдання "{{taskName}}"', 'pause-task-fail': 'Помилка призупинення завдання "{{taskName}}"', 'resume-task': 'Поновити Завдання', 'resume-task-success': 'Успішно поновленне завдання "{{taskName}}"', 'resume-task-fail': 'Помилка при поновленні завдання "{{taskName}}"', 'delete-task': 'Видалити завдання', 'delete-selected-tasks': 'Видалити обранне завдання', 'delete-task-confirm': 'Ви впевненні що бажаєте видалити завдання "{{taskName}}"?', 'batch-delete-task-confirm': 'Ви впевнені, що хочете видалити {{count}} завдань із завантаження в пакеті?', 'delete-task-label': 'Видалити разом з файлами', 'delete-task-success': 'Успішно видалине завдання "{{taskName}}"', 'delete-task-fail': 'Помилка при видаленні завдання "{{taskName}}"', 'remove-task-file-fail': 'Поимилка при видаленні файла(файлів) завдання, будь ласка видаліть його(їх) самостійно', 'remove-task-config-file-fail': 'Помилка пи видаленні файла конфігурації завдання, будьласка видаліть його самостійно', 'move-task-up': 'Пермістити завдання в гору', 'move-task-down': 'Перемістити завдання в низ', 'pause-all-task': 'Призупинити всі завдання', 'pause-all-task-success': 'Успішно призупиненні всі завдання', 'pause-all-task-fail': 'Помилка при призупиненні всіх завдань', 'resume-all-task': 'Відновити всі завдання', 'resume-all-task-success': 'Успішно поновленні всі завдання', 'resume-all-task-fail': 'Помилка при поновленні всіх завдань', 'select-all-task': 'Виберіть усе завдання', 'clear-recent-tasks': 'Очистити останні завдання', 'purge-record': 'Очистити записи про завдання', 'purge-record-success': 'Успішно очишенні записи про завдання', 'purge-record-fail': 'Помилка при очишенні записів про завдання', 'batch-delete-task-success': 'Видалити завдання в пакеті', 'batch-delete-task-fail': 'Не вдалося видалити завдання з партії', 'refresh-list': 'Оновити список завдань', 'no-task': 'Нема поточних завдань', 'copy-link': 'Копіювати посилання', 'copy-link-success': 'Успішно скопійоване посилання', 'remove-record': 'Видалити запис про завдання', 'remove-record-confirm': 'Ви впевненні, що бажаєте видалити запис про завантаження "{{taskName}}"?', 'remove-record-label': 'Видалити разом з файлами', 'remove-record-success': 'Успішно видалено запис про завдання "{{taskName}}"', 'remove-record-fail': 'Помилка при видаленні запису про завдання "{{taskName}}"', 'show-in-folder': 'Відобразити файли завдання у папці', 'file-not-exist': 'Розшукуємий файл не існує або був видаленний', 'file-path-error': 'Помилка шляху файла', 'opening-task-message': 'Відкриття "{{taskName}}" ...', 'get-task-name': 'Отримання Ім\'я завдання...', 'remaining-prefix': 'Залишилося', 'select-torrent': 'Перетягніть torrent файл сюди, або натисніть обрати', 'task-info-dialog-title': '{{title}} Деталі', 'download-start-message': 'Почалось завантаження {{taskName}}', 'download-pause-message': 'Призупинине завантаження {{taskName}}', 'download-stop-message': 'Зупинине завантаження {{taskName}}', 'download-error-message': 'Помилка під час завантаження {{taskName}}', 'download-complete-message': 'Виконане завантаження {{taskName}}', 'download-complete-notify': 'Завантаження виконане', 'bt-download-complete-message': 'Виконане завантаження {{taskName}}, раздача', 'bt-download-complete-notify': 'BT Виконане завантаження, роздача...', 'bt-download-complete-tips': 'Порада: Ви можите зупинити завдання щоб зупинити роздачу', 'bt-stopping-seeding-tip': 'Припиняючи посів, потрібно буде трохи часу відключити, зачекайте, будь ласка ...', 'download-fail-message': 'Не вдалося завантажити {{taskName}}', 'download-fail-notify': 'Помилка завантаження' } ================================================ FILE: src/shared/locales/uk/window.js ================================================ export default { 'reload': 'Перезавнтажити', 'close': 'Закрити', 'minimize': 'Згорнути', 'zoom': 'Збільшення', 'toggle-fullscreen': 'Перейти до повноекранного режиму', 'front': 'Поверх всіх вікон' } ================================================ FILE: src/shared/locales/vi/about.js ================================================ export default { 'engine-version': 'Phiên bản Ứng dụng', 'license': 'Giấy phép', 'about': 'Về Motrix', 'release': 'Phát hành', 'support': 'Hỗ trợ' } ================================================ FILE: src/shared/locales/vi/app.js ================================================ export default { 'task-list': 'Danh sách Tác vụ', 'add-task': 'Thêm tác vụ', 'about': 'Về Motrix', 'preferences': 'Cài đặt...', 'check-for-updates': 'Kiểm tra cập nhật...', 'check-updates-now': 'Kiểm tra ngay', 'checking-for-updates': 'Đang kiểm tra cập nhật...', 'check-for-updates-title': 'Kiểm tra Cập nhật', 'update-available-message': 'Có một phiên bản mới của Motrix, cập nhật ngay?', 'update-not-available-message': 'Bạn đang dùng bản mới nhất!', 'update-downloaded-message': 'Đã sẵn sàng để cài đặt...', 'update-error-message': 'Cập nhật lỗi', 'engine-damaged-message': 'Ứng dụng bị lỗi, vui lòng cài đặt lại : (', 'engine-missing-message': 'Ứng dụng bị thiếu tập tin, vui lòng cài đặt lại : (', 'system-error-title': 'Lỗi hệ thống', 'system-error-message': 'Khởi động ứng dụng thất bại: {{message}}', 'hide': 'Ẩn Motrix', 'hide-others': 'Ẩn tất cả', 'unhide': 'Hiển thị tất cả', 'show': 'Hiển thị Motrix', 'quit': 'Thoát Motrix', 'under-development-message': 'Xin lỗi, tính năng này đang được phát triển...', 'yes': 'Có', 'no': 'Không', 'save': 'Lưu', 'reset': 'Loại bỏ', 'cancel': 'Huỷ', 'submit': 'Tải về', 'gt1d': '> 1 ngày', 'hour': ' giờ', 'minute': ' phút', 'second': ' giây' } ================================================ FILE: src/shared/locales/vi/edit.js ================================================ export default { 'undo': 'Hoàn tác', 'redo': 'Redo', 'cut': 'Cắt', 'copy': 'Sao chép', 'paste': 'Dán', 'delete': 'Xóa', 'select-all': 'Chọn tất cả' } ================================================ FILE: src/shared/locales/vi/help.js ================================================ export default { 'official-website': 'Trang web của Motrix', 'manual': 'Hướng dẫn sử dụng', 'release-notes': 'Ghi chú Phát hành...', 'report-problem': 'Báo cáo Vấn đề', 'toggle-dev-tools': 'Mở công cụ Dành cho Nhà phát triển' } ================================================ FILE: src/shared/locales/vi/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/vi/menu.js ================================================ export default { 'app': 'Motrix', 'file': 'Tập tin', 'task': 'Tác vụ', 'edit': 'Chỉnh sửa', 'window': 'Cửa sổ', 'help': 'Trợ giúp' } ================================================ FILE: src/shared/locales/vi/preferences.js ================================================ export default { 'basic': 'Cơ bản', 'advanced': 'Nâng cao', 'lab': 'Phòng thí nghiệm', 'save': 'Lưu & Áp dụng', 'save-success-message': 'Lưu cài đặt thành công', 'save-fail-message': 'Lưu cài đặt thất bại', 'discard': 'Loại bỏ', 'startup': 'Khởi động', 'open-at-login': 'Mở khi đăng nhập', 'keep-window-state': 'Giữ kích thước và vị trí của cửa sổ khi thoát', 'auto-resume-all': 'Tự động tiếp tục tất cả các tác vụ chưa hoàn thành', 'default-dir': 'Đường dẫn mặc định', 'mas-default-dir-tips': 'Do các hạn chế cấp phép sandbox của App Store, thư mục tải xuống mặc định được khuyến nghị đặt tại ~/Downloads', 'transfer-settings': 'Tốc độ truyền', 'transfer-speed-upload': 'Giới hạn Tải lên', 'transfer-speed-download': 'Giới hạn Tải về', 'transfer-speed-unlimited': 'Không giới hạn', 'bt-settings': 'BitTorrent', 'bt-save-metadata': 'Lưu liên kết magnet dưới dạng tệp torrent', 'bt-auto-download-content': 'Tự động tải xuống nam châm và nội dung torrent', 'bt-force-encryption': 'Bắt BT mã hóa đầy đủ', 'keep-seeding': 'Tiếp tục seed cho đến khi dừng lại theo cách thủ công', 'seed-ratio': 'Tỷ lệ seed', 'seed-time': 'Thời gian seed', 'seed-time-unit': 'phút', 'task-manage': 'Quản lý Tác vụ', 'max-concurrent-downloads': 'Số tác vụ đang hoạt động tối đa', 'max-connection-per-server': 'Số kết nối tối đa trên mỗi máy chủ', 'new-task-show-downloading': 'Tự động hiển thị tải xuống sau khi thêm tác vụ', 'no-confirm-before-delete-task': 'Không cần xác nhận trước khi xóa tác vụ', 'continue': 'Tiếp tục', 'task-completed-notify': 'Thông báo sau khi tải xuống hoàn tất', 'auto-purge-record': 'Tự động xoá hồ sơ tải xuống khi thoát khỏi ứng dụng', 'ui': 'UI', 'appearance': 'Giao diện', 'theme-auto': 'Tự động', 'theme-light': 'Nền sáng', 'theme-dark': 'Nền tối', 'auto-hide-window': 'Tự động ẩn cửa sổ', 'run-mode': 'Chạy như', 'run-mode-standard': 'Ứng dụng Tiêu chuẩn', 'run-mode-tray': 'Ứng dụng khay thông báo', 'run-mode-hide-tray': 'Ẩn ứng dụng khay hệ thống', 'tray-speedometer': 'Thanh menu hiển thị tốc độ thời gian thực', 'show-progress-bar': 'Hiển thị thanh tiến trình tải xuống', 'language': 'Ngôn ngữ', 'change-language': 'Thay đổi Ngôn ngữ', 'hide-app-menu': 'Ẩn thanh Menu (Chỉ Windows & Linux)', 'proxy': 'Proxy', 'enable-proxy': 'Bật Proxy', 'proxy-bypass-input-tips': 'Bỏ qua cài đặt proxy cho các Máy chủ và tên miền này, mỗi cái một dòng', 'proxy-scope-download': 'Tải về', 'proxy-scope-update-app': 'Cập nhật ứng dụng', 'proxy-scope-update-trackers': 'Cập nhật theo dõi', 'proxy-tips': 'Xem Proxy Thủ Công', 'bt-tracker': 'Máy Chủ Tracker', 'bt-tracker-input-tips': 'Máy chủ theo dõi, mỗi thông tin trên một dòng', 'bt-tracker-tips': 'Khuyên dùng: ', 'sync-tracker-tips': 'Đồng bộ', 'auto-sync-tracker': 'Tự động cập nhật danh sách tracker mỗi ngày', 'port': 'Cổng giao tiếp cuối', 'bt-port': 'Cổng giao tiếp BT cuối', 'dht-port': 'Cổng giao tiếp DHT cuối', 'security': 'Bảo mật', 'rpc': 'RPC', 'rpc-listen-port': 'Cổng Nghe RPC', 'rpc-secret': 'RPC bí mật', 'rpc-secret-tips': 'Xem RPC bí mật thủ công', 'developer': 'Lập trình viên', 'user-agent': 'User-Agent', 'mock-user-agent': 'Mock User-Agent', 'aria2-conf-path': 'Đường dẫn aria2.conf tích hợp sẵn', 'app-log-path': 'Đường dẫn nhật ký ứng dụng', 'download-session-path': 'Đường dẫn phiên tải về', 'session-reset': 'Đặt lại phiên tải xuống', 'factory-reset': 'Khôi phục cài đặt gốc', 'factory-reset-confirm': 'Bạn có chắc chắn muốn khôi phục cài đặt gốc?', 'lab-warning': '⚠️ Kích hoạt các tính năng trong phòng thí nghiệm có thể dẫn đến sự cố ứng dụng hoặc mất dữ liệu, hãy cân nhắc cho quyết định của mình!', 'download-protocol': 'Các giao thức', 'protocols-default-client': 'Đặt làm máy khách mặc định cho các giao thức sau', 'protocols-magnet': 'Magnet [ magnet:// ]', 'protocols-thunder': 'Thunder [ thunder:// ]', 'browser-extensions': 'Tiện ích mở rộng', 'baidu-exporter': 'BaiduExporter', 'browser-extensions-tips': 'Được cung cấp bởi Cộng đồng, ', 'baidu-exporter-help': 'Nhấn vào đây để sử dụng', 'auto-update': 'Tự động cập nhật', 'auto-check-update': 'Tự động kiểm tra cập nhật', 'last-check-update-time': 'Kiểm tra cập nhật lần cuối', 'not-saved': 'Tùy chọn chưa được lưu', 'not-saved-confirm': 'Các tùy chọn đã sửa đổi sẽ bị mất, bạn có chắc chắn thoát không?' } ================================================ FILE: src/shared/locales/vi/subnav.js ================================================ export default { 'task-list': 'Tác vụ', 'preferences': 'Cài đặt' } ================================================ FILE: src/shared/locales/vi/task.js ================================================ export default { 'active': 'Đang tải về', 'waiting': 'Đang chờ', 'stopped': 'Ngừng', 'new-task': 'Tác vụ mới', 'new-bt-task': 'Tác vụ BT mới', 'open-file': 'Mở tệp Torrent...', 'uri-task': 'URL', 'torrent-task': 'Torrent', 'uri-task-tips': 'Mỗi URL tác vụ một dòng (hỗ trợ magnet)', 'thunder-link-tips': 'Mẹo: Liên kết Thunder có thể không tải được sau khi giải mã', 'new-task-uris-required': 'Vui lòng nhập ít nhất một url tài nguyên hợp lệ', 'new-task-torrent-required': 'Vui lòng chọn tệp Torrent', 'file-name': 'Tên', 'file-extension': 'Loại', 'file-size': 'Kích thước', 'file-completed-size': 'Đã hoàn thành', 'selected-files-sum': 'Đã chọn: {{selectedFilesCount}} tập tin, tổng kích thước {{selectedFilesTotalSize}}', 'select-at-least-one': 'Vui lòng chọn ít nhất một tệp', 'task-gid': 'GID', 'task-name': 'Tên tác vụ', 'task-out': 'Đổi tên', 'task-out-tips': 'Không bắt buộc', 'task-split': 'Chia nhỏ', 'task-dir': 'Lưu đến', 'pause-task': 'Tạm dừng Tác vụ', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Authorization', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': 'Lỗi', 'task-piece': 'Mảnh', 'task-piece-length': 'Kích thước mảnh', 'task-num-pieces': 'Các mảnh', 'task-bittorrent-info': 'Thông tin Torrent', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': 'Ngày tạo', 'task-bittorrent-comment': 'Bình luận', 'task-progress-info': 'Tiến trình', 'task-status': 'Trạng thái', 'task-num-seeders': 'Seeders', 'task-connections': 'Kết nối', 'task-file-size': 'Kích thước', 'task-download-speed': 'Tốc độ tải về', 'task-upload-speed': 'Tốc độ tải lên', 'task-download-length': 'Đã tải xuống', 'task-upload-length': 'Đã tải lên', 'task-ratio': 'Tỉ lệ', 'task-peer-host': 'Tổ chức', 'task-peer-ip': 'IP', 'task-peer-client': 'Clients', 'navigate-to-downloading': 'Điều hướng tải xuống', 'show-advanced-options': 'Lựa chọn Nâng cao', 'copyright-warning': 'Cảnh báo Bản quyền', 'copyright-warning-message': 'Tập tin bạn muốn tải xuống có thể là âm thanh hoặc video có bản quyền, vui lòng đảm bảo rằng bạn có quyền truy cập vào nó.', 'copyright-yes': 'Đúng, tôi được cấp phép', 'copyright-no': 'Không, tôi không được cấp phép', 'copyright-error-message': 'Không thể thêm Tác vụ vì vấn đề bản quyền', 'pause-task-success': 'Dừng tác vụ thành công "{{taskName}}"', 'pause-task-fail': 'Dừng tác vụ thất bại "{{taskName}}"', 'resume-task': 'Tiếp tục Tác vụ', 'resume-task-success': 'Tác vụ được tải lại thành công "{{taskName}}"', 'resume-task-fail': 'Tác vụ không thể tải lại "{{taskName}}"', 'delete-task': 'Xóa tác vụ', 'delete-selected-tasks': 'Xóa những tác vụ được chọn', 'delete-task-confirm': 'Bạn có chắc chắn muốn xóa tác vụ tải xuống "{{taskName}}"?', 'batch-delete-task-confirm': 'Bạn có chắc chắn muốn xóa {{Count}} tác vụ tải xuống không?', 'delete-task-label': 'Xóa kèm tập tin', 'delete-task-success': 'Xóa tác vụ thành công "{{taskName}}"', 'delete-task-fail': 'Xóa tác vụ thất bại "{{taskName}}"', 'remove-task-file-fail': 'Không thể xóa (các) tập tin trong tác vụ, vui lòng xóa chúng theo cách thủ công', 'remove-task-config-file-fail': 'Không thể xóa tập tin cấu hình tác vụ, vui lòng xóa chúng theo cách thủ công', 'move-task-up': 'Đưa tác vụ lên', 'move-task-down': 'Đưa tác vụ xuống', 'pause-all-task': 'Dừng Tất cả Tác vụ', 'pause-all-task-success': 'Dừng tất cả tác vụ thành công', 'pause-all-task-fail': 'Dừng tất cả tác vụ thất bại', 'resume-all-task': 'Tải lại tất cả tác vụ', 'resume-all-task-success': 'Tải lại tất cả tác vụ thành công', 'resume-all-task-fail': 'Tải lại tất cả tác vụ thất bại', 'select-all-task': 'Chọn tất cả tác vụ', 'clear-recent-tasks': 'Xóa các tác vụ gần đây', 'purge-record': 'Làm mới bản ghi tác vụ', 'purge-record-success': 'Làm mới bản ghi tác vụ thành công', 'purge-record-fail': 'Làm mới tản ghi tác vụ thất bại', 'batch-delete-task-success': 'Xóa các tác vụ hàng loạt thành công', 'batch-delete-task-fail': 'Xóa các tác vụ hàng loạt thất bại', 'refresh-list': 'Làm mới danh sách tác vụ', 'no-task': 'Hiện tại không có tác vụ', 'copy-link': 'Sao chép đường dẫn', 'copy-link-success': 'Sao chép đường dẫn thành công', 'remove-record': 'Xóa bản ghi tác vụ', 'remove-record-confirm': 'Bạn có chắc chắn muốn xóa bản ghi tải xuống cho "{{taskName}}" không?', 'remove-record-label': 'Xóa kèm tập tin', 'remove-record-success': 'Xoá thành công bản ghi tác vụ cho "{{taskName}}"', 'remove-record-fail': 'Xoá thất bại bản ghi tác vụ cho "{{taskName}}"', 'show-in-folder': 'Hiển thị tác vụ trong thư mục', 'file-not-exist': 'Tập tin mục tiêu không tồn tại hoặc đã bị xóa', 'file-path-error': 'Lỗi đường dẫn tệp', 'opening-task-message': 'Đang mở "{{taskName}}" ...', 'get-task-name': 'Lấy tên tác vụ....', 'remaining-prefix': 'Còn lại', 'select-torrent': 'Kéo thả tệp torrent vào đây hoặc nhấp để chọn', 'task-detail-title': 'Thông tin tác vụ', 'task-info-dialog-title': '{{title}} Chi tiết', 'download-start-message': 'Bắt đầu tải xuống {{taskName}}', 'download-pause-message': 'Tạm dừng tải xuống {{taskName}}', 'download-stop-message': 'Đã dừng tải xuống {{taskName}}', 'download-error-message': 'Xảy ra lỗi khi tải xuống {{taskName}}', 'download-complete-message': 'Đã hoàn tất tải xuống {{taskName}}', 'download-complete-notify': 'Tải xuống hoàn tất', 'bt-download-complete-message': 'Đã hoàn tất tải xuống {{taskName}}, đang seed', 'bt-download-complete-notify': 'BT đã hoàn tất tải xuống, đang seed...', 'bt-download-complete-tips': 'Mẹo: Bạn có thể dừng một tác vụ để kết thúc việc seed', 'bt-stopping-seeding-tip': 'Ngừng seed, sẽ mất một thời gian để ngắt kết nối, vui lòng đợi...', 'download-fail-message': 'Không thể tải xuống {{taskName}}', 'download-fail-notify': 'Tải xuống thất bại' } ================================================ FILE: src/shared/locales/vi/window.js ================================================ export default { 'reload': 'Tải lại', 'close': 'Đóng', 'minimize': 'Thu nhỏ', 'zoom': 'Thu phóng', 'toggle-fullscreen': 'Mở Toàn Màn Hình', 'front': 'Đưa tất cả lên phía trước' } ================================================ FILE: src/shared/locales/zh-CN/about.js ================================================ export default { 'engine-version': '引擎版本', 'license': '开源许可', 'about': '关于我们', 'release': '更新日志', 'support': '帮助支持' } ================================================ FILE: src/shared/locales/zh-CN/app.js ================================================ export default { 'task-list': '任务列表', 'add-task': '新建任务', 'about': '关于 Motrix', 'preferences': '偏好设置...', 'check-for-updates': '检查更新...', 'check-updates-now': '立即检查', 'checking-for-updates': '正在检查更新...', 'check-for-updates-title': '检查更新', 'update-available-message': '发现新版本,是否现在更新?', 'update-not-available-message': '已是最新版', 'update-downloaded-message': '更新下载完成,应用程序将退出并开始更新...', 'update-error-message': '检查更新失败', 'engine-damaged-message': '引擎损坏,请重新安装 : (', 'engine-missing-message': '引擎缺失,请重新安装 : (', 'system-error-title': '系统错误', 'system-error-message': '应用启动失败: {{message}}', 'hide': '隐藏 Motrix', 'hide-others': '隐藏其他', 'unhide': '显示全部', 'show': '显示 Motrix', 'quit': '退出 Motrix', 'under-development-message': '该功能开发中...', 'yes': '是', 'no': '否', 'save': '保存', 'reset': '放弃', 'cancel': '取 消', 'submit': '提 交', 'gt1d': '超过一天', 'hour': '时', 'minute': '分', 'second': '秒' } ================================================ FILE: src/shared/locales/zh-CN/edit.js ================================================ export default { 'undo': '撤销', 'redo': '重做', 'cut': '剪切', 'copy': '复制', 'paste': '黏贴', 'delete': '删除', 'select-all': '全选' } ================================================ FILE: src/shared/locales/zh-CN/help.js ================================================ export default { 'official-website': 'Motrix 官网', 'manual': '使用手册', 'release-notes': '发行说明...', 'report-problem': '报告问题', 'toggle-dev-tools': '开发者工具' } ================================================ FILE: src/shared/locales/zh-CN/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/zh-CN/menu.js ================================================ export default { 'app': 'Motrix', 'file': '文件', 'task': '任务', 'edit': '编辑', 'window': '窗口', 'help': '帮助' } ================================================ FILE: src/shared/locales/zh-CN/preferences.js ================================================ export default { 'basic': '基础设置', 'advanced': '进阶设置', 'lab': '实验室', 'save': '保存并应用', 'save-success-message': '偏好设置保存成功', 'save-fail-message': '偏好设置保存失败', 'discard': '放弃', 'startup': '启动', 'open-at-login': '开机自动启动', 'keep-window-state': '恢复上次退出时窗口的大小和位置', 'auto-resume-all': '自动开始未完成的任务', 'default-dir': '默认下载路径', 'mas-default-dir-tips': '因 App Store 的沙箱权限限制,默认下载路径建议设置为您的「下载」目录', 'transfer-settings': '传输设置', 'transfer-speed-upload': '上传限速', 'transfer-speed-download': '下载限速', 'transfer-speed-unlimited': '不限速', 'bt-settings': 'BT 设置', 'bt-save-metadata': '保存磁力链接元数据为种子文件', 'bt-auto-download-content': '自动开始下载磁力链接、种子的文件', 'bt-force-encryption': 'BT强制加密', 'keep-seeding': '持续做种,直到手动停止', 'seed-ratio': '做种分享率', 'seed-time': '做种时间', 'seed-time-unit': '分钟', 'task-manage': '任务管理', 'max-concurrent-downloads': '同时下载的最大任务数', 'max-connection-per-server': '每个服务器最大连接数', 'new-task-show-downloading': '新建任务后自动跳转到下载页面', 'no-confirm-before-delete-task': '删除任务前无需确认', 'continue': '断点续传', 'task-completed-notify': '下载完成后通知', 'auto-purge-record': '当应用退出时自动清除下载记录', 'ui': '界面', 'appearance': '外观', 'theme-auto': '自动', 'theme-light': '浅色', 'theme-dark': '深色', 'auto-hide-window': '自动隐藏窗口', 'run-mode': '运行为', 'run-mode-standard': '标准应用', 'run-mode-tray': '托盘应用', 'run-mode-hide-tray': '隐藏托盘应用', 'tray-speedometer': '托盘显示实时速度', 'show-progress-bar': '显示下载进度条', 'language': '语言', 'change-language': '切换语言', 'hide-app-menu': '隐藏菜单栏(仅支持 Windows 和 Linux)', 'proxy': '代理', 'enable-proxy': '使用代理服务器', 'proxy-bypass-input-tips': '忽略这些主机与域的代理设置,一行一个', 'proxy-scope-download': '下载', 'proxy-scope-update-app': '更新应用程序', 'proxy-scope-update-trackers': '更新 Tracker 列表', 'proxy-tips': '查看代理配置说明', 'bt-tracker': 'Tracker 服务器', 'bt-tracker-input-tips': 'Tracker 服务器,一行一个', 'bt-tracker-tips': '推荐使用:', 'sync-tracker-tips': '从服务器同步', 'auto-sync-tracker': '每天自动更新 Tracker 服务器列表', 'port': '监听端口', 'bt-port': 'BT 监听端口', 'dht-port': 'DHT 监听端口', 'security': '安全性', 'rpc': 'RPC', 'rpc-listen-port': 'RPC 监听端口', 'rpc-secret': 'RPC 授权密钥', 'rpc-secret-tips': '查看说明文档', 'developer': '开发者', 'user-agent': 'User-Agent', 'mock-user-agent': '模拟用户代理(UA)', 'aria2-conf-path': '内置的 aria2.conf 路径', 'app-log-path': '应用日志路径', 'download-session-path': '下载会话路径', 'session-reset': '重置下载会话记录', 'session-reset-confirm': '你确定要重置下载会话记录吗?', 'factory-reset': '恢复初始设置', 'factory-reset-confirm': '你确定要恢复为初始设置吗?', 'lab-warning': '⚠️启用实验特性可能造成应用崩溃或数据丢失,请自行决定!', 'download-protocol': '下载协议', 'protocols-default-client': '设置为以下协议的默认客户端', 'protocols-magnet': '磁力链接 [ magnet:// ]', 'protocols-thunder': '迅雷链接 [ thunder:// ]', 'browser-extensions': '浏览器扩展', 'baidu-exporter': '百度网盘助手', 'browser-extensions-tips': '社区提供的浏览器扩展「不保证可用性」,', 'baidu-exporter-help': '点此查看使用说明', 'auto-update': '自动更新', 'auto-check-update': '自动检查更新', 'last-check-update-time': '上次检查更新时间', 'follow-metalink': '自动开始下载磁力链接、种子内的文件', 'follow-torrent': '种子下载完后自动下载种子内容', 'not-saved': '设置未保存', 'not-saved-confirm': '已修改的设置将会丢失,确定要离开吗?' } ================================================ FILE: src/shared/locales/zh-CN/subnav.js ================================================ export default { 'task-list': '任务列表', 'preferences': '偏好设置' } ================================================ FILE: src/shared/locales/zh-CN/task.js ================================================ export default { 'active': '下载中', 'waiting': '等待中', 'stopped': '已停止', 'new-task': '新建任务', 'new-bt-task': '新建 BT 任务', 'open-file': '打开种子文件...', 'uri-task': '链接任务', 'torrent-task': '种子任务', 'uri-task-tips': '添加多个下载链接时,请确保每行只有一个链接(支持磁力链)', 'thunder-link-tips': '友情提示:迅雷链接解码之后的资源不一定存在', 'new-task-uris-required': '请至少输入一个有效的下载地址', 'new-task-torrent-required': '请先选择种子文件', 'file-name': '文件名', 'file-extension': '扩展名', 'file-size': '大小', 'file-completed-size': '已完成', 'selected-files-sum': '已选:{{selectedFilesCount}}个文件,共 {{selectedFilesTotalSize}}', 'select-at-least-one': '请选择至少一个文件', 'task-gid': 'GID', 'task-name': '任务名称', 'task-out': '重命名', 'task-out-tips': '选填', 'task-split': '分片数', 'task-dir': '存储路径', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Authorization', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': '代理', 'task-error-info': '错误信息', 'task-piece': '分片', 'task-piece-length': '分片大小', 'task-num-pieces': '分片数量', 'task-bittorrent-info': '种子信息', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': '发布时间', 'task-bittorrent-comment': '备注', 'task-progress-info': '任务进度', 'task-status': '任务状态', 'task-num-seeders': '种子数', 'task-connections': '连接数', 'task-file-size': '文件大小', 'task-download-speed': '下载速度', 'task-upload-speed': '上传速度', 'task-download-length': '已下载', 'task-upload-length': '已上传', 'task-ratio': '分享率', 'task-peer-host': '服务器', 'task-peer-ip': 'IP', 'task-peer-client': '客户端', 'navigate-to-downloading': '跳转到下载页面', 'show-advanced-options': '高级选项', 'copyright-warning': '版权提醒', 'copyright-warning-message': '您要下载的文件可能是有版权的音视频,请确保您有相应的版权方授权。', 'copyright-yes': '是,我有版权方授权', 'copyright-no': '否', 'copyright-error-message': '因版权问题,添加任务失败', 'pause-task': '暂停任务', 'pause-task-success': '暂停任务 "{{taskName}}" 成功', 'pause-task-fail': '暂停任务 "{{taskName}}" 失败', 'resume-task': '恢复任务', 'resume-task-success': '恢复任务 "{{taskName}}" 成功', 'resume-task-fail': '恢复任务 "{{taskName}}" 失败', 'delete-task': '移除任务', 'delete-selected-tasks': '移除选中的任务', 'delete-task-confirm': '你确定要移除 "{{taskName}}" 下载任务吗?', 'batch-delete-task-confirm': '你确定要批量移除{{count}}个下载任务吗?', 'delete-task-label': '同时删除文件', 'delete-task-success': '移除任务 "{{taskName}}" 成功', 'delete-task-fail': '移除任务 "{{taskName}}" 失败', 'remove-task-file-fail': '删除任务文件失败,请手动删除', 'remove-task-config-file-fail': '删除任务配置文件失败,请手动删除', 'move-task-up': '上移任务', 'move-task-down': '下移任务', 'pause-all-task': '暂停所有任务', 'pause-all-task-success': '暂停所有任务成功', 'pause-all-task-fail': '暂停所有任务失败', 'resume-all-task': '恢复所有任务', 'resume-all-task-success': '恢复所有任务成功', 'resume-all-task-fail': '恢复所有任务失败', 'select-all-task': '选中所有任务', 'clear-recent-tasks': '清除最近的下载记录', 'purge-record': '清除下载记录', 'purge-record-success': '清除下载记录成功', 'purge-record-fail': '清除下载记录失败', 'batch-delete-task-success': '批量移除任务成功', 'batch-delete-task-fail': '批量移除任务失败', 'refresh-list': '刷新任务列表', 'no-task': '当前没有下载任务', 'copy-link': '拷贝链接', 'copy-link-success': '拷贝链接成功', 'remove-record': '移除下载记录', 'remove-record-confirm': '你确定要移除 "{{taskName}}" 下载记录吗?', 'remove-record-label': '同时删除文件', 'remove-record-success': '移除 "{{taskName}}" 下载记录成功', 'remove-record-fail': '移除 "{{taskName}}" 下载记录失败', 'show-in-folder': '在文件夹中显示', 'file-not-exist': '目标文件不存在或已删除', 'file-path-error': '文件路径异常', 'opening-task-message': '正在打开 "{{taskName}}" ...', 'get-task-name': '获取任务名中...', 'remaining-prefix': '剩余', 'select-torrent': '将种子拖到此处,或点击选择', 'task-detail-title': '任务详情', 'task-info-dialog-title': '{{title}} 详情', 'download-start-message': '开始下载 {{taskName}}', 'download-pause-message': '暂停下载 {{taskName}}', 'download-stop-message': '{{taskName}} 下载中止', 'download-error-message': '{{taskName}} 下载发生错误', 'download-complete-message': '{{taskName}} 下载完成', 'download-complete-notify': '下载完成', 'bt-download-complete-message': '{{taskName}} 下载完成,正在做种...', 'bt-download-complete-notify': 'BT 任务下载完成,正在做种...', 'bt-download-complete-tips': '提示:你可以停止任务结束做种', 'bt-stopping-seeding-tip': '正在停止做种,断开连接需要些时间,请耐心等待...', 'download-fail-message': '{{taskName}} 下载失败', 'download-fail-notify': '下载失败' } ================================================ FILE: src/shared/locales/zh-CN/window.js ================================================ export default { 'reload': '重新加载', 'close': '关闭', 'minimize': '最小化', 'zoom': '放大', 'toggle-fullscreen': '进入全屏幕', 'front': '前置全部窗口' } ================================================ FILE: src/shared/locales/zh-TW/about.js ================================================ export default { 'engine-version': '引擎版本', 'license': '授權條款', 'about': '關於', 'release': '版本資訊', 'support': '支援' } ================================================ FILE: src/shared/locales/zh-TW/app.js ================================================ export default { 'task-list': '任務列表', 'add-task': '新增任務', 'about': '關於 Motrix', 'preferences': '偏好設定...', 'check-for-updates': '檢查更新...', 'check-updates-now': '立即檢查', 'checking-for-updates': '正在檢查更新...', 'check-for-updates-title': '檢查更新', 'update-available-message': '發現新版本,是否現在更新?', 'update-not-available-message': '已是最新版', 'update-downloaded-message': '更新下載完成,應用程式將退出並開始更新...', 'update-error-message': '檢查更新失敗', 'engine-damaged-message': '引擎損壞,請重新安裝 : (', 'engine-missing-message': '引擎缺失,請重新安裝 : (', 'system-error-title': '系統錯誤', 'system-error-message': '應用程式啟動失敗: {{message}}', 'hide': '隱藏 Motrix', 'hide-others': '隱藏其它', 'unhide': '顯示全部', 'show': '顯示 Motrix', 'quit': '結束 Motrix', 'under-development-message': '該功能開發中...', 'yes': '是', 'no': '否', 'save': '儲存', 'reset': '捨棄', 'cancel': '取消', 'submit': '送出', 'gt1d': '超過一天', 'hour': '時', 'minute': '分', 'second': '秒' } ================================================ FILE: src/shared/locales/zh-TW/edit.js ================================================ export default { 'undo': '還原', 'redo': '取消還原', 'cut': '剪下', 'copy': '複製', 'paste': '貼上', 'delete': '刪除', 'select-all': '全選' } ================================================ FILE: src/shared/locales/zh-TW/help.js ================================================ export default { 'official-website': 'Motrix 官網', 'manual': '使用說明', 'release-notes': '版本資訊...', 'report-problem': '回報問題', 'toggle-dev-tools': '開發者工具' } ================================================ FILE: src/shared/locales/zh-TW/index.js ================================================ import about from './about' import app from './app' import edit from './edit' import help from './help' import menu from './menu' import preferences from './preferences' import subnav from './subnav' import task from './task' import window from './window' export default { about, app, edit, help, menu, preferences, subnav, task, window } ================================================ FILE: src/shared/locales/zh-TW/menu.js ================================================ export default { 'app': 'Motrix', 'file': '檔案', 'task': '任務', 'edit': '編輯', 'window': '視窗', 'help': '說明' } ================================================ FILE: src/shared/locales/zh-TW/preferences.js ================================================ export default { 'basic': '基本設定', 'advanced': '進階設定', 'lab': '實驗性功能', 'save': '儲存並套用', 'save-success-message': '偏好設定儲存成功', 'save-fail-message': '偏好設定儲存失敗', 'discard': '捨棄', 'startup': '啟動', 'open-at-login': '開機自動啟動', 'keep-window-state': '恢復上次退出時視窗的大小和位置', 'auto-resume-all': '自動繼續未完成的任務', 'default-dir': '預設下載路徑', 'mas-default-dir-tips': '因 App Store 的沙盒權限限制,建議將預設下載路徑設定為您的「下載」資料夾', 'transfer-settings': '傳輸設定', 'transfer-speed-upload': '上傳限制', 'transfer-speed-download': '下載限制', 'transfer-speed-unlimited': '無限制', 'bt-settings': 'BT 設定', 'bt-save-metadata': '將磁力連結中繼資料儲存為種子檔案', 'bt-auto-download-content': '自動開始下載磁力連結、種子的檔案', 'bt-force-encryption': '強制 BT 加密', 'keep-seeding': '持續做種,直到手動停止', 'seed-ratio': '做種分享率', 'seed-time': '做種時間', 'seed-time-unit': '分鐘', 'task-manage': '任務管理', 'max-concurrent-downloads': '最多可同時下載的任務數', 'max-connection-per-server': '每台伺服器的最大連線數', 'new-task-show-downloading': '新增任務後自動顯示下載頁面', 'no-confirm-before-delete-task': '刪除任務之前無需確認', 'continue': '斷點續傳', 'task-completed-notify': '下載完成後通知', 'auto-purge-record': '當結束程式時自動清除下載紀錄', 'ui': '使用者介面', 'appearance': '外觀', 'theme-auto': '自動', 'theme-light': '淺色', 'theme-dark': '深色', 'auto-hide-window': '自動隱藏視窗', 'run-mode': '運作模式', //macOS only feature 'run-mode-standard': '標準應用程式', 'run-mode-tray': '托盤應用程式', 'run-mode-hide-tray': '隱藏托盤應用程式', 'tray-speedometer': '托盤顯示即時速度', 'show-progress-bar': '顯示下載進度條', 'language': '語言', 'change-language': '更改語言', 'hide-app-menu': '隱藏選單列(僅支援 Windows 和 Linux)', 'proxy': 'Proxy', 'enable-proxy': '使用 Proxy', 'proxy-bypass-input-tips': '忽略這些主機與網域的 Proxy 設定,一列一個', 'proxy-scope-download': '下載', 'proxy-scope-update-app': '更新應用程式', 'proxy-scope-update-trackers': '更新 Tracker 列表', 'proxy-tips': '查看 Proxy 配置手冊', 'bt-tracker': 'Tracker 伺服器', 'bt-tracker-input-tips': 'Tracker 伺服器,一列一個', 'bt-tracker-tips': '推薦使用:', 'sync-tracker-tips': '從源伺服器同步', 'auto-sync-tracker': '每天自動更新 Tracker 伺服器列表', 'port': '監聽連接埠', 'bt-port': 'BT 監聽連接埠', 'dht-port': 'DHT 監聽連接埠', 'security': '安全性', 'rpc': 'RPC', 'rpc-listen-port': 'RPC 監聽埠', 'rpc-secret': 'RPC 授權密鑰', 'rpc-secret-tips': '查看說明手冊', 'developer': '開發者', 'user-agent': 'User-Agent', 'mock-user-agent': '偽裝 User Agent', 'aria2-conf-path': '內建的 aria2.conf 路徑', 'app-log-path': '應用程式記錄檔位置', 'download-session-path': '下載工作階段路徑', 'session-reset': '重設下載工作階段', 'session-reset-confirm': '您確定要重設下載工作階段嗎?', 'factory-reset': '還原出廠預設值', 'factory-reset-confirm': '您確定要還原為出廠預設值嗎?', 'lab-warning': '⚠️開啟實驗性功能可能會造成程式當機或資料遺失,請自行斟酌!', 'download-protocol': '下載協定', 'protocols-default-client': '設定為以下通訊協定的預設客戶端', 'protocols-magnet': '磁力連結 [ magnet:// ]', 'protocols-thunder': '迅雷連結 [ thunder:// ]', 'browser-extensions': '瀏覽器擴充功能', 'baidu-exporter': '百度網盤助手', 'browser-extensions-tips': '社群提供的瀏覽器擴充功能「不保證可用性」,', 'baidu-exporter-help': '點此檢視使用說明', 'auto-update': '自動更新', 'auto-check-update': '自動檢查更新', 'last-check-update-time': '上次檢查更新時間', 'not-saved': '設定未儲存', 'not-saved-confirm': '已修改的設定將會丟失,確定要離開嗎?' } ================================================ FILE: src/shared/locales/zh-TW/subnav.js ================================================ export default { 'task-list': '任務清單', 'preferences': '偏好設定' } ================================================ FILE: src/shared/locales/zh-TW/task.js ================================================ export default { 'active': '下載中', 'waiting': '等待中', 'stopped': '已停止', 'new-task': '新增任務', 'new-bt-task': '新增 BT 任務', 'open-file': '開啟種子檔案...', 'uri-task': '連結任務', 'torrent-task': '種子任務', 'uri-task-tips': '新增多個下載連結時,請確保每行只有一個連結(支援磁力連結)', 'thunder-link-tips': '提醒:迅雷連結解碼之後的資源不一定存在', 'new-task-uris-required': '請至少輸入一個有效的資源網址', 'new-task-torrent-required': '請先選擇種子檔案', 'file-name': '檔案名稱', 'file-extension': '副檔名', 'file-size': '大小', 'file-completed-size': '已下載', 'selected-files-sum': '已選取:{{selectedFilesCount}}個檔案,總計 {{selectedFilesTotalSize}}', 'select-at-least-one': '請選擇至少一個檔案', 'task-gid': 'GID', 'task-name': '任務名稱', 'task-out': '重新命名', 'task-out-tips': '選填', 'task-split': '分片數', 'task-dir': '儲存資料夾', 'task-ua': 'UA', 'task-user-agent': 'User-Agent', 'task-authorization': 'Authorization', 'task-referer': 'Referer', 'task-cookie': 'Cookie', 'task-proxy': 'Proxy', 'task-error-info': '錯誤訊息', 'task-piece': '分片', 'task-piece-length': '分片大小', 'task-num-pieces': '分片數量', 'task-bittorrent-info': '種子資訊', 'task-info-hash': 'Hash', 'task-bittorrent-creation-date': '發佈時間', 'task-bittorrent-comment': '備註', 'task-progress-info': '任務進度', 'task-status': '任務狀態', 'task-num-seeders': '種子數', 'task-connections': '連接數', 'task-file-size': '檔案大小', 'task-download-speed': '下載速度', 'task-upload-speed': '上傳速度', 'task-download-length': '已下載', 'task-upload-length': '已上傳', 'task-ratio': '分享率', 'task-peer-host': '伺服器', 'task-peer-ip': 'IP', 'task-peer-client': '客戶端', 'navigate-to-downloading': '前往下載頁面', 'show-advanced-options': '進階選項', 'copyright-warning': '版權警告', 'copyright-warning-message': '您要下載的檔案可能是有版權的音訊視訊,請確保您有相應的版權方授權。', 'copyright-yes': '是,我有版權方授權', 'copyright-no': '否', 'copyright-error-message': '因版權問題,新增任務失敗', 'pause-task': '暫停任務', 'pause-task-success': '暫停任務 "{{taskName}}" 成功', 'pause-task-fail': '暫停任務 "{{taskName}}" 失敗', 'resume-task': '繼續任務', 'resume-task-success': '繼續任務 "{{taskName}}" 成功', 'resume-task-fail': '繼續任務 "{{taskName}}" 失敗', 'delete-task': '移除任務', 'delete-selected-tasks': '移除選取的任務', 'delete-task-confirm': '你確定要移除 "{{taskName}}" 下載任務嗎?', 'batch-delete-task-confirm': '你確定要批量移除{{count}}個下載任務嗎?', 'delete-task-label': '同時刪除檔案', 'delete-task-success': '移除任務 "{{taskName}}" 成功', 'delete-task-fail': '移除任務 "{{taskName}}" 失敗', 'remove-task-file-fail': '刪除任務檔案失敗,請手動刪除', 'remove-task-config-file-fail': '刪除任務設定檔失敗,請手動刪除', 'move-task-up': '上移任務', 'move-task-down': '下移任務', 'pause-all-task': '暫停所有任務', 'pause-all-task-success': '暫停所有任務成功', 'pause-all-task-fail': '暫停所有任務失敗', 'resume-all-task': '繼續所有任務', 'resume-all-task-success': '繼續所有任務成功', 'resume-all-task-fail': '繼續所有任務失敗', 'select-all-task': '選擇所有任務', 'clear-recent-tasks': '清除最近的下載紀錄', 'purge-record': '清除下載紀錄', 'purge-record-success': '清除下載紀錄成功', 'purge-record-fail': '清除下載紀錄失敗', 'batch-delete-task-success': '批次刪除任務成功', 'batch-delete-task-fail': '批次刪除任務失敗', 'refresh-list': '重新整理任務清單', 'no-task': '目前没有下載任務', 'copy-link': '複製連結', 'copy-link-success': '複製連結成功', 'remove-record': '移除下載紀錄', 'remove-record-confirm': '確定要移除 "{{taskName}}" 下載紀錄嗎?', 'remove-record-label': '同時刪除檔案', 'remove-record-success': '移除 "{{taskName}}" 下載紀錄成功', 'remove-record-fail': '移除 "{{taskName}}" 下載紀錄失敗', 'show-in-folder': '在資料夾中顯示', 'file-not-exist': '目標檔案不存在或已刪除', 'file-path-error': '檔案路徑錯誤', 'opening-task-message': '正在打開 "{{taskName}}" ...', 'get-task-name': '取得任務名稱中...', 'remaining-prefix': '剩下', 'select-torrent': '將種子拖曳至此,或點選來選取', 'task-detail-title': '任務詳細資訊', 'task-info-dialog-title': '{{title}} 詳細資訊', 'download-start-message': '開始下載 {{taskName}}', 'download-pause-message': '暫停下載 {{taskName}}', 'download-stop-message': '{{taskName}} 下載中止', 'download-error-message': '{{taskName}} 下載錯誤', 'download-complete-message': '{{taskName}} 下載完成', 'download-complete-notify': '下載完成', 'bt-download-complete-message': '{{taskName}} 下載完成,正在做種...', 'bt-download-complete-notify': 'BT 任務下載完成,正在做種...', 'bt-download-complete-tips': '提示:你可以停止任務結束做種', 'bt-stopping-seeding-tip': '停止做種中,需要些時間才能斷開連接,請稍候...', 'download-fail-message': '{{taskName}} 下載失敗', 'download-fail-notify': '下載失敗' } ================================================ FILE: src/shared/locales/zh-TW/window.js ================================================ export default { 'reload': '重新載入', 'close': '關閉', 'minimize': '最小化', 'zoom': '放大', 'toggle-fullscreen': '切換全螢幕', 'front': '將全部視窗移至最前方' } ================================================ FILE: src/shared/ua.js ================================================ export const ARIA2_UA = 'aria2/1.36.0' export const TRANSMISSION_UA = 'Transmission/3.00' export const CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' export const DU_UA = 'netdisk;6.0.0.12;PC;PC-Windows;10.0.16299;WindowsBaiduYunGuanJia' export default { aria2: ARIA2_UA, transmission: TRANSMISSION_UA, chrome: CHROME_UA, du: DU_UA } ================================================ FILE: src/shared/utils/curl.js ================================================ import * as curlParser from '@bany/curl-to-json' export const buildUrisFromCurl = (uris = []) => { return uris.map((uri) => { if (uri.startsWith('curl')) { const parsedUri = curlParser(uri) uri = parsedUri.url if (parsedUri.params && Object.keys(parsedUri.params).length > 0) { const paramsStr = Object.keys(parsedUri.params) .map((k) => `${k}=${parsedUri.params[k]}`) .join('&') uri = `${uri}?${paramsStr}` } return uri } else { return uri } }) } export const buildHeadersFromCurl = (uris = []) => { return uris.map((uri) => { if (uri.startsWith('curl')) { const parsed = curlParser(uri) const header = parsed.header ?? {} if (parsed.cookie) { header.cookie = parsed.cookie } if (parsed['user-agent']) { header['user-agent'] = parsed['user-agent'] } if (parsed.referer) { header.referer = parsed.referer } return header } else { return undefined } }) } export const buildDefaultOptionsFromCurl = (form, headers = []) => { const firstNonNullHeader = headers.find((elem) => elem) if (firstNonNullHeader) { form.cookie = !form.cookie && firstNonNullHeader.cookie ? firstNonNullHeader.cookie : form.cookie form.referer = !form.referer && firstNonNullHeader.referer ? firstNonNullHeader.referer : form.referer form.userAgent = !form.userAgent && firstNonNullHeader['user-agent'] ? firstNonNullHeader['user-agent'] : form.userAgent form.authorization = !form.authorization && firstNonNullHeader.authorization ? firstNonNullHeader.authorization : form.authorization } return form } ================================================ FILE: src/shared/utils/index.js ================================================ import { camelCase, compact, difference, isArray, isEmpty, isFunction, isNaN, isPlainObject, kebabCase, omitBy, parseInt, pick } from 'lodash' import bitTorrentPeerId from 'bittorrent-peerid' import { userKeys, systemKeys, needRestartKeys } from '@shared/configKeys' import { APP_THEME, ENGINE_RPC_HOST, GRAPHIC, NONE_SELECTED_FILES, SELECTED_ALL_FILES, RESOURCE_TAGS, IMAGE_SUFFIXES, AUDIO_SUFFIXES, VIDEO_SUFFIXES, SUB_SUFFIXES, UNKNOWN_PEERID, SUPPORT_RTL_LOCALES, UNKNOWN_PEERID_NAME, DOCUMENT_SUFFIXES } from '@shared/constants' export const bytesToSize = (bytes, precision = 1) => { const b = parseInt(bytes, 10) const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] if (b === 0) { return '0 KB' } const i = parseInt(Math.floor(Math.log(b) / Math.log(1024)), 10) if (i === 0) { return `${b} ${sizes[i]}` } return `${(b / (1024 ** i)).toFixed(precision)} ${sizes[i]}` } export const extractSpeedUnit = (speed = '') => { if (parseInt(speed) === 0) { return 'K' } const regex = /^(\d+\.?\d*)([KMG])$/ const match = regex.exec(speed) if (!match) { return 'K' } return match[2] } export const bitfieldToPercent = (text) => { const len = text.length - 1 let p let one = 0 for (let i = 0; i < len; i++) { p = parseInt(text[i], 16) for (let j = 0; j < 4; j++) { one += (p & 1) p >>= 1 } } return Math.floor(one / (4 * len) * 100).toString() } export const bitfieldToGraphic = (text) => { const len = text.length let result = '' for (let i = 0; i < len; i++) { result += GRAPHIC[Math.floor(parseInt(text[i], 16) / 4)] + ' ' } return result } export const peerIdParser = (str) => { if (!str || str === UNKNOWN_PEERID) { return UNKNOWN_PEERID_NAME } let parsed = {} let decodedStr try { // decodeURI or decodeURIComponent cannot parse '%2DUT360W%2D%92%B6%EBh%1F%A1%DBfo%F6%D5I' decodedStr = unescape(str) const buffer = Buffer.from(decodedStr, 'binary') parsed = bitTorrentPeerId(buffer) } catch (e) { console.log('peerIdParser.fail', e, str, decodedStr) return UNKNOWN_PEERID_NAME } const result = parsed.version ? `${parsed.client} v${parsed.version}` : parsed.client return result } export const calcProgress = (totalLength, completedLength, decimal = 2) => { const total = parseInt(totalLength, 10) const completed = parseInt(completedLength, 10) if (total === 0 || completed === 0) { return 0 } const percentage = completed / total * 100 const result = parseFloat(percentage.toFixed(decimal)) return result } export const calcRatio = (totalLength, uploadLength) => { const total = parseInt(totalLength, 10) const upload = parseInt(uploadLength, 10) if (total === 0 || upload === 0) { return 0 } const percentage = upload / total const result = parseFloat(percentage.toFixed(4)) return result } export const timeRemaining = (totalLength, completedLength, downloadSpeed) => { const remainingLength = totalLength - completedLength return Math.ceil(remainingLength / downloadSpeed) } /** * timeFormat * @param {int} seconds * @param {string} prefix * @param {string} suffix * @param {object} i18n * i18n: { * gt1d: 'More than one day', * hour: 'h', * minute: 'm', * second: 's' * } */ export const timeFormat = (seconds, { prefix = '', suffix = '', i18n }) => { let result = '' let hours = '' let minutes = '' let secs = seconds || 0 const i = { gt1d: '> 1 day', hour: 'h', minute: 'm', second: 's', ...i18n } if (secs <= 0) { return '' } if (secs > 86400) { return `${prefix} ${i.gt1d} ${suffix}` } if (secs > 3600) { hours = `${Math.floor(secs / 3600)}${i.hour} ` secs %= 3600 } if (secs > 60) { minutes = `${Math.floor(secs / 60)}${i.minute} ` secs %= 60 } secs += i.second result = hours + minutes + secs return result ? `${prefix} ${result} ${suffix}` : result } export const localeDateTimeFormat = (timestamp, locale) => { if (!timestamp) { return '' } if (`${timestamp}`.length === 10) { timestamp *= 1000 } const date = new Date(timestamp) return date.toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }) } export const ellipsis = (str = '', maxLen = 64) => { const len = str.length let result = str if (len < maxLen) { return result } if (maxLen > 0) { result = `${result.substring(0, maxLen)}...` } return result } export const getFileSelection = (files = []) => { console.log('getFileSelection===>', files) const selectedFiles = files.filter((file) => file.selected) if (files.length === 0 || selectedFiles.length === 0) { return NONE_SELECTED_FILES } if (files.length === selectedFiles.length) { return SELECTED_ALL_FILES } const indexArr = [] files.forEach((_, index) => { indexArr.push(index) }) const result = indexArr.join(',') return result } export const getTaskName = (task, options = {}) => { const o = { defaultName: '', maxLen: 64, // -1: No limit length ...options } const { defaultName, maxLen } = o let result = defaultName if (!task) { return result } const { files, bittorrent } = task const total = files.length if (bittorrent && bittorrent.info && bittorrent.info.name) { result = ellipsis(bittorrent.info.name, maxLen) } else if (total === 1) { result = getFileNameFromFile(files[0]) result = ellipsis(result, maxLen) } return result } export const getFileNameFromFile = (file) => { if (!file) { return '' } let { path } = file if (!path && file.uris && file.uris.length > 0) { path = decodeURI(file.uris[0].uri) } const index = path.lastIndexOf('/') if (index <= 0 || index === path.length) { return path } return path.substring(index + 1) } export const isMagnetTask = (task) => { const { bittorrent } = task return bittorrent && !bittorrent.info } export const checkTaskIsSeeder = (task) => { const { bittorrent, seeder } = task return !!bittorrent && seeder === 'true' } export const getTaskUri = (task, withTracker = false) => { const { files } = task let result = '' if (checkTaskIsBT(task)) { result = buildMagnetLink(task, withTracker) return result } if (files && files.length === 1) { const { uris } = files[0] result = uris[0].uri } return result } export const buildMagnetLink = (task, withTracker = false, btTracker = []) => { const { bittorrent, infoHash } = task const { info } = bittorrent const params = [ `magnet:?xt=urn:btih:${infoHash}` ] if (info && info.name) { params.push(`dn=${encodeURI(info.name)}`) } if (withTracker) { const trackers = difference(bittorrent.announceList, btTracker) trackers.forEach((tracker) => { params.push(`tr=${encodeURI(tracker)}`) }) } const result = params.join('&') return result } export const checkTaskTitleIsEmpty = (task) => { const { files, bittorrent } = task const [file] = files const { path } = file let result = path if (bittorrent && bittorrent.info && bittorrent.info.name) { result = bittorrent.info.name } return result === '' } export const checkTaskIsBT = (task = {}) => { const { bittorrent } = task return !!bittorrent } export const isTorrent = (file) => { const { name, type } = file return name.endsWith('.torrent') || type === 'application/x-bittorrent' } export const getAsBase64 = (file, callback) => { const reader = new FileReader() reader.addEventListener('load', () => { // https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL const result = reader.result.split('base64,')[1] callback(result) }) reader.readAsDataURL(file) } export const mergeTaskResult = (response = []) => { let result = [] for (const res of response) { result = result.concat(...res) } return result } export const changeKeysCase = (obj, caseConverter) => { const result = {} if (isEmpty(obj) || !isFunction(caseConverter)) { return result } for (const [k, value] of Object.entries(obj)) { const key = caseConverter(k) result[key] = value } return result } export const changeKeysToCamelCase = (obj = {}) => { return changeKeysCase(obj, camelCase) } export const changeKeysToKebabCase = (obj = {}) => { return changeKeysCase(obj, kebabCase) } export const validateNumber = (n) => { return !isNaN(parseFloat(n)) && isFinite(n) && Number(n) === n } export const fixValue = (obj = {}) => { const result = {} for (const [k, v] of Object.entries(obj)) { if (v === 'true') { result[k] = true } else if (v === 'false') { result[k] = false } else if (validateNumber(v)) { result[k] = Number(v) } else { result[k] = v } } return result } export const separateConfig = (options) => { // user const user = {} // system const system = {} // others const others = {} for (const [k, v] of Object.entries(options)) { if (userKeys.indexOf(k) !== -1) { user[k] = v } else if (systemKeys.indexOf(k) !== -1) { system[k] = v } else { others[k] = v } } return { user, system, others } } export const compactUndefined = (arr = []) => { return arr.filter((item) => { return item !== undefined }) } export const splitTextRows = (text = '') => { text = `${text}` let result = text .replace(/(?:\\\r\\\n|\\\r|\\\n)/g, ' ') .replace(/(?:\r\n|\r|\n)/g, '\n') .split('\n') || [] result = result.map((row) => row.trim()) return result } export const convertCommaToLine = (text = '') => { text = `${text}` let arr = text.split(',') arr = arr.map((row) => row.trim()) const result = arr.join('\n').trim() return result } export const convertLineToComma = (text = '') => { const result = text.trim().replace(/(?:\r\n|\r|\n)/g, ',') return result } export const filterVideoFiles = (files = []) => { const suffix = [...VIDEO_SUFFIXES, ...SUB_SUFFIXES] return files.filter((item) => { return suffix.includes(item.extension) }) } export const filterAudioFiles = (files = []) => { return files.filter((item) => { return AUDIO_SUFFIXES.includes(item.extension) }) } export const filterImageFiles = (files = []) => { return files.filter((item) => { return IMAGE_SUFFIXES.includes(item.extension) }) } export const filterDocumentFiles = (files = []) => { return files.filter((item) => { return DOCUMENT_SUFFIXES.includes(item.extension) }) } export const isAudioOrVideo = (uri = '') => { const suffixs = [...AUDIO_SUFFIXES, ...VIDEO_SUFFIXES] const result = suffixs.some((suffix) => { return uri.includes(suffix) }) return result } export const needCheckCopyright = (links = '') => { const uris = splitTaskLinks(links) const avs = uris.filter(uri => { return isAudioOrVideo(uri) }) const result = avs.length > 0 return result } export const decodeThunderLink = (url = '') => { if (!url.startsWith('thunder://')) { return url } let result = url.trim() result = result.split('thunder://')[1] result = Buffer.from(result, 'base64').toString('utf8') result = result.substring(2, result.length - 2) return result } export const splitTaskLinks = (links = '') => { const temp = compact(splitTextRows(links)) const result = temp.map((item) => { return decodeThunderLink(item) }) return result } export const detectResource = (content) => { return RESOURCE_TAGS.some((type) => { return content.includes(type) }) } export const buildFileList = (rawFile) => { rawFile.uid = Date.now() const file = { status: 'ready', name: rawFile.name, size: rawFile.size, percentage: 0, uid: rawFile.uid, raw: rawFile } const fileList = [file] return fileList } export const isRTL = (locale = 'en-US') => { return SUPPORT_RTL_LOCALES.includes(locale) } export const getLangDirection = (locale = 'en-US') => { return isRTL(locale) ? 'rtl' : 'ltr' } export const listTorrentFiles = (files) => { const result = files.map((file, index) => { const extension = getFileExtension(file.path) const item = { // aria2 select-file start index at 1 // possible Values: 1-1048576 idx: index + 1, extension: `.${extension}`, ...file } return item }) return result } export const getFileName = (fullPath) => { // eslint-disable-next-line return fullPath.replace(/^.*[\\\/]/, '') } export const getFileExtension = (filename) => { return filename.slice((filename.lastIndexOf('.') - 1 >>> 0) + 2) } export const removeExtensionDot = (extension = '') => { return extension.replace('.', '') } export const diffConfig = (current = {}, next = {}) => { const curr = pick(current, Object.keys(next)) const result = omitBy(next, (val, key) => { if (isArray(val) || isPlainObject(val)) { return JSON.stringify(curr[key]) === JSON.stringify(val) } return curr[key] === val }) return result } export const calcFormLabelWidth = (locale) => { return locale.startsWith('de') ? '28%' : '25%' } export const parseHeader = (header = '') => { header = header.trim() let result = {} if (!header) { return result } const headers = splitTextRows(header) headers.forEach((line) => { const index = line.indexOf(':') const name = line.substring(0, index) const value = line.substring(index + 1).trim() result[name] = value }) result = changeKeysToCamelCase(result) return result } export const formatOptionsForEngine = (options = {}) => { const result = {} Object.keys(options).forEach((key) => { const kebabCaseKey = kebabCase(key) if (Array.isArray(options[key])) { result[kebabCaseKey] = options[key].join('\n') } else { result[kebabCaseKey] = `${options[key]}` } }) return result } export const buildRpcUrl = (options = {}) => { const { port, secret } = options let result = `${ENGINE_RPC_HOST}:${port}/jsonrpc` if (secret) { result = `token:${secret}@${result}` } result = `http://${result}` return result } export const checkIsNeedRestart = (changed = {}) => { let result = false if (isEmpty(changed)) { return result } const kebabCaseChanged = changeKeysToKebabCase(changed) needRestartKeys.some((key) => { if (Object.keys(kebabCaseChanged).includes(key)) { result = true return true } return false }) return result } export const checkIsNeedRun = (enable, lastTime, interval) => { if (!enable) { return false } return (Date.now() - lastTime > interval) } export const generateRandomInt = (min = 0, max = 10000) => { let result = min const range = max - min result += Math.floor(Math.random() * Math.floor(range)) return result } export const intersection = (array1 = [], array2 = []) => { if (array1.length === 0 || array2.length === 0) { return [] } return array1.filter(value => array2.includes(value)) } export const cloneArray = (arr = [], reversed = false) => { if (!Array.isArray(arr)) { return arr } const result = [...arr] return reversed ? result.reverse() : result } export const pushItemToFixedLengthArray = (arr = [], maxLength, item) => { const result = arr.length >= maxLength ? [...arr.slice(1, maxLength - 1), item] : [...arr, item] return result } export const removeArrayItem = (arr = [], item) => { const idx = arr.indexOf(item) if (idx === -1) { return [...arr] } const result = [ ...arr.slice(0, idx), ...arr.slice(idx + 1) ] return result } export const getInverseTheme = (theme) => { return (theme === APP_THEME.LIGHT) ? APP_THEME.DARK : APP_THEME.LIGHT } export const changedConfig = { basic: {}, advanced: {} } export const backupConfig = { theme: undefined, locale: undefined } ================================================ FILE: src/shared/utils/rename.js ================================================ const RULE_REGEX = /\(([^)]*)\)/ const PLUS = '+' const MINUS = '-' const OPERATORS = [PLUS, MINUS] export const getRuleString = (out) => { const rule = out.match(RULE_REGEX) const result = rule && rule[1] return result } export const buildRule = (rule) => { let ruleArr let operator = PLUS let init = 1 let step = 1 let len = 1 OPERATORS.some(OPT => { if (rule.includes(OPT)) { ruleArr = rule.split(OPT) operator = OPT return true } return false }) if (ruleArr) { len = ruleArr[0].length init = parseInt(ruleArr[0], 10) step = ruleArr[1] || 1 if (operator === MINUS) { step = -step } } return { init, step, len } } export const buildOuts = (uris = [], out = '') => { const result = [] const count = uris.length if (count === 0 || !out) { return result } if (count === 1) { return [out] } const ruleStr = getRuleString(out) if (!ruleStr) { return result } const rule = buildRule(ruleStr) let idx let temp for (let i = 0; i < count; i++) { idx = `${rule.init + rule.step * i}`.padStart(rule.len, '0') temp = out.replace(RULE_REGEX, idx) result.push(temp) } return result } ================================================ FILE: src/shared/utils/tracker.js ================================================ import { isEmpty } from 'lodash' import axios from 'axios' import { MAX_BT_TRACKER_LENGTH, ONE_SECOND, PROXY_SCOPES } from '@shared/constants' export const convertToAxiosProxy = (proxyServer = '') => { if (!proxyServer) { return } const url = new URL(proxyServer) const { username, password, protocol = 'http:', hostname, port } = url let result = { protocol: protocol.replace(':', ''), host: hostname, port } const auth = username || password ? { username, password } : undefined if (auth) { result = { ...result, auth } } return result } export const fetchBtTrackerFromSource = async (source, proxyConfig = {}) => { if (isEmpty(source)) { return [] } const now = Date.now() const { enable, server, scope = [] } = proxyConfig const proxy = enable && server && scope.includes(PROXY_SCOPES.UPDATE_TRACKERS) ? convertToAxiosProxy(server) : undefined // Axios's config.proxy is Node.js only const promises = source.map(async (url) => { return axios.get(`${url}?t=${now}`, { timeout: 30 * ONE_SECOND, proxy }).then((value) => value.data) }) const results = await Promise.allSettled(promises) const values = results.map((item) => item.value) const result = [...new Set(values)] return result } export const convertTrackerDataToLine = (arr = []) => { const result = arr.join('\r\n').replace(/^\s*[\r\n]/gm, '').trim() return result } export const convertTrackerDataToComma = (arr = []) => { const result = convertTrackerDataToLine(arr).replace(/(?:\r\n|\r|\n)/g, ',').trim() return result } export const reduceTrackerString = (str = '') => { if (str.length <= MAX_BT_TRACKER_LENGTH) { return str } const subStr = str.substring(0, MAX_BT_TRACKER_LENGTH) const index = subStr.lastIndexOf(',') if (index === -1) { return subStr } const result = subStr.substring(0, index) return result } ================================================ FILE: src/shared/utils/tray.js ================================================ import { APP_THEME, TRAY_CANVAS_CONFIG } from '@shared/constants' // Temp Fix: Cannot find module 'lodash' // import { bytesToSize } from '@shared/utils' const bytesToSize = (bytes) => { const b = parseInt(bytes, 10) const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] if (b === 0) { return '0 KB' } const i = parseInt(Math.floor(Math.log(b) / Math.log(1024)), 10) if (i === 0) { return `${b} ${sizes[i]}` } return `${(b / (1024 ** i)).toFixed(1)} ${sizes[i]}` } const lightTextColor = '#000' const darkTextColor = '#fff' const baseWidth = TRAY_CANVAS_CONFIG.WIDTH const baseHeight = TRAY_CANVAS_CONFIG.HEIGHT const baseIconWidth = TRAY_CANVAS_CONFIG.ICON_WIDTH const baseIconHeight = TRAY_CANVAS_CONFIG.ICON_HEIGHT const baseTextWidth = TRAY_CANVAS_CONFIG.TEXT_WIDTH const baseFontSize = TRAY_CANVAS_CONFIG.TEXT_FONT_SIZE const fontFamily = 'Arial' export const draw = async ({ canvas, theme, icon, uploadSpeed, downloadSpeed, scale, resultType }) => { if (!canvas) { throw new Error('canvas is required') } const width = baseWidth * scale const height = baseHeight * scale const textColor = (theme === APP_THEME.LIGHT) ? lightTextColor : darkTextColor const fontSize = (baseFontSize * scale) + 1 const textFont = `${fontSize}px "${fontFamily}"` const iconWidth = baseIconWidth * scale const iconHeight = baseIconHeight * scale const textWidth = baseTextWidth * scale if (canvas.width !== width) { canvas.width = width } if (canvas.height !== height) { canvas.height = height } const ctx = canvas.getContext('2d') ctx.clearRect(0, 0, canvas.width, canvas.height) if (icon) { ctx.drawImage(icon, 0, 0, iconWidth, iconHeight) } ctx.font = textFont ctx.textBaseline = 'top' ctx.textAlign = 'right' ctx.fillStyle = textColor const uploadText = `${bytesToSize(uploadSpeed)}/s` const uploadTextY = 0 ctx.fillText(uploadText, width, uploadTextY, textWidth) const downloadText = `${bytesToSize(downloadSpeed)}/s` const downloadTextY = baseFontSize * scale + 0.5 ctx.fillText(downloadText, width, downloadTextY, textWidth) const result = transferCanvasTo(canvas, resultType) return result } export const transferCanvasTo = (canvas, type) => { switch (type) { case 'DATA_URL': return canvas.toDataURL() case 'BLOB': return canvas.convertToBlob() case 'BITMAP': return canvas.transferToImageBitmap() default: return canvas.convertToBlob() } } ================================================ FILE: static/.gitkeep ================================================