hfs 0.42.3 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +56 -17
  2. package/admin/assets/index-35f6e3dc.css +1 -0
  3. package/admin/assets/index-ef68a7ab.js +510 -0
  4. package/{frontend/assets/sha512-f6798733.js → admin/assets/sha512-d091720e.js} +1 -1
  5. package/admin/index.html +2 -2
  6. package/frontend/assets/index-a3b0d6ac.js +94 -0
  7. package/frontend/assets/index-fe0f3d77.css +1 -0
  8. package/{admin/assets/sha512-69b26793.js → frontend/assets/sha512-96fd938f.js} +1 -1
  9. package/frontend/fontello.css +9 -3
  10. package/frontend/fontello.woff2 +0 -0
  11. package/frontend/index.html +3 -2
  12. package/package.json +2 -2
  13. package/plugins/antibrute/plugin.js +1 -1
  14. package/plugins/download-counter/plugin.js +14 -5
  15. package/plugins/download-counter/public/main.js +12 -2
  16. package/plugins/vhosting/plugin.js +17 -11
  17. package/src/adminApis.js +4 -4
  18. package/src/api.auth.js +4 -1
  19. package/src/api.file_list.js +22 -10
  20. package/src/api.lang.js +9 -11
  21. package/src/api.vfs.js +43 -29
  22. package/src/apiMiddleware.js +3 -2
  23. package/src/block.js +6 -20
  24. package/src/config.js +6 -2
  25. package/src/const.js +5 -4
  26. package/src/customHtml.js +2 -2
  27. package/src/debounceAsync.js +3 -3
  28. package/src/frontEndApis.js +7 -32
  29. package/src/github.js +26 -8
  30. package/src/index.js +2 -1
  31. package/src/lang.js +67 -0
  32. package/src/langs/embedded.js +13 -0
  33. package/src/langs/hfs-lang-it.json +100 -0
  34. package/src/langs/hfs-lang-ko.json +103 -0
  35. package/src/langs/hfs-lang-ms.json +70 -0
  36. package/src/langs/hfs-lang-ru.json +106 -0
  37. package/src/langs/hfs-lang-sr.json +108 -0
  38. package/src/langs/hfs-lang-zh-tw.json +106 -0
  39. package/src/langs/hfs-lang-zh.json +98 -0
  40. package/src/listen.js +8 -3
  41. package/src/log.js +6 -2
  42. package/src/middlewares.js +24 -18
  43. package/src/misc.js +17 -9
  44. package/src/perm.js +1 -1
  45. package/src/plugins.js +6 -6
  46. package/src/serveFile.js +2 -2
  47. package/src/serveGuiFiles.js +23 -10
  48. package/src/update.js +1 -2
  49. package/src/upload.js +6 -6
  50. package/src/util-http.js +5 -3
  51. package/src/vfs.js +114 -74
  52. package/src/zip.js +4 -4
  53. package/admin/assets/index-08017e15.js +0 -511
  54. package/admin/assets/index-94bbe0be.css +0 -1
  55. package/frontend/assets/index-5f125477.js +0 -94
  56. package/frontend/assets/index-a09cacfd.css +0 -1
package/src/const.js CHANGED
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.APP_PATH = exports.IS_BINARY = exports.IS_WINDOWS = exports.HTTP_SERVER_ERROR = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.SESSION_DURATION = exports.DAY = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
30
+ exports.APP_PATH = exports.IS_BINARY = exports.IS_WINDOWS = exports.HTTP_SERVER_ERROR = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.HFS_REPO = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.SESSION_DURATION = exports.DAY = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
31
31
  const minimist_1 = __importDefault(require("minimist"));
32
32
  const fs = __importStar(require("fs"));
33
33
  const os_1 = require("os");
@@ -38,13 +38,14 @@ exports.DEV = process.env.DEV || exports.argv.dev ? 'DEV' : '';
38
38
  exports.ORIGINAL_CWD = process.cwd();
39
39
  exports.HFS_STARTED = new Date();
40
40
  const PKG_PATH = (0, path_1.join)(__dirname, '..', 'package.json');
41
- exports.BUILD_TIMESTAMP = "2023-03-29T21:48:40.615Z";
41
+ exports.BUILD_TIMESTAMP = "2023-04-23T13:31:04.738Z";
42
42
  const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
43
43
  exports.VERSION = pkg.version;
44
44
  exports.DAY = 86400000;
45
- exports.SESSION_DURATION = exports.DAY;
46
- exports.API_VERSION = 7; // fileMenu event
45
+ exports.SESSION_DURATION = Number(process.env.SESSION_DURATION) * 1000 || exports.DAY;
46
+ exports.API_VERSION = 8; // entry.uri + script.plugin
47
47
  exports.COMPATIBLE_API_VERSION = 1; // while changes in the api are not breaking, this number stays the same, otherwise is made equal to API_VERSION
48
+ exports.HFS_REPO = 'rejetto/hfs';
48
49
  exports.SPECIAL_URI = '/~/';
49
50
  exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
50
51
  exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
package/src/customHtml.js CHANGED
@@ -12,7 +12,7 @@ const watchLoad_1 = require("./watchLoad");
12
12
  const valtio_1 = require("valtio");
13
13
  const promises_1 = require("fs/promises");
14
14
  exports.customHtmlSections = ['top', 'bottom', 'beforeHeader', 'afterHeader',
15
- 'afterMenuBar', 'afterEntryName', 'afterList'];
15
+ 'afterMenuBar', 'afterEntryName', 'afterList', 'beforeLogin'];
16
16
  exports.customHtmlState = (0, valtio_1.proxy)({
17
17
  sections: new Map()
18
18
  });
@@ -21,7 +21,7 @@ if (!(0, fs_1.existsSync)(FILE))
21
21
  events_1.default.once('config ready', () => {
22
22
  const legacy = (0, misc_1.prefix)('[beforeHeader]\n', frontEndApis_1.customHeader.get());
23
23
  (0, fs_1.writeFileSync)(FILE, legacy);
24
- frontEndApis_1.customHeader.set(undefined); // get rid of it
24
+ frontEndApis_1.customHeader.set(''); // get rid of it
25
25
  });
26
26
  (0, watchLoad_1.watchLoad)(FILE, data => {
27
27
  var _a;
@@ -18,7 +18,7 @@ function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infini
18
18
  });
19
19
  async function debouncer(...args) {
20
20
  if (runningCallback)
21
- return await runningCallback;
21
+ return runningCallback;
22
22
  whoIsWaiting = args;
23
23
  waitingSince || (waitingSince = Date.now());
24
24
  const waitingCap = maxWait - (Date.now() - (waitingSince || started));
@@ -29,7 +29,7 @@ function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infini
29
29
  return void (waitingSince = 0);
30
30
  if (whoIsWaiting !== args) // another fresher call is waiting
31
31
  return runningDebouncer;
32
- return await exec();
32
+ return exec();
33
33
  }
34
34
  async function exec() {
35
35
  if (!whoIsWaiting)
@@ -38,7 +38,7 @@ function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infini
38
38
  started = Date.now();
39
39
  try {
40
40
  runningCallback = callback.apply(null, whoIsWaiting);
41
- return await runningCallback;
41
+ return await runningCallback; // await necessary to go-finally at the right time and even on exceptions
42
42
  }
43
43
  finally {
44
44
  whoIsWaiting = undefined;
@@ -38,8 +38,7 @@ const const_1 = require("./const");
38
38
  const vfs_1 = require("./vfs");
39
39
  const promises_1 = require("fs/promises");
40
40
  const path_1 = require("path");
41
- const misc_1 = require("./misc");
42
- exports.customHeader = (0, config_1.defineConfig)('custom_header');
41
+ exports.customHeader = (0, config_1.defineConfig)('custom_header', '');
43
42
  exports.frontEndApis = {
44
43
  file_list: api_file_list_1.file_list,
45
44
  ...api_auth,
@@ -53,11 +52,11 @@ exports.frontEndApis = {
53
52
  }
54
53
  });
55
54
  },
56
- async create_folder({ path, name }, ctx) {
57
- apiAssertTypes({ string: { path, name } });
55
+ async create_folder({ uri, name }, ctx) {
56
+ apiAssertTypes({ string: { uri, name } });
58
57
  if (!(0, util_files_1.isValidFileName)(name) || (0, util_files_1.dirTraversal)(name))
59
58
  return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
60
- const parentNode = await (0, vfs_1.urlToNode)(path, ctx);
59
+ const parentNode = await (0, vfs_1.urlToNode)(uri, ctx);
61
60
  if (!parentNode)
62
61
  return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'parent not found');
63
62
  if (!(0, vfs_1.hasPermission)(parentNode, 'can_upload', ctx))
@@ -70,9 +69,9 @@ exports.frontEndApis = {
70
69
  return new apiMiddleware_1.ApiError(e.code === 'EEXIST' ? const_1.HTTP_CONFLICT : const_1.HTTP_BAD_REQUEST, e);
71
70
  }
72
71
  },
73
- async del({ path }, ctx) {
74
- apiAssertTypes({ string: { path } });
75
- const node = await (0, vfs_1.urlToNode)(path, ctx);
72
+ async del({ uri }, ctx) {
73
+ apiAssertTypes({ string: { uri } });
74
+ const node = await (0, vfs_1.urlToNode)(uri, ctx);
76
75
  if (!node)
77
76
  throw new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
78
77
  if (!node.source)
@@ -87,30 +86,6 @@ exports.frontEndApis = {
87
86
  throw new apiMiddleware_1.ApiError(e.code || const_1.HTTP_SERVER_ERROR, e);
88
87
  }
89
88
  },
90
- async load_lang({ lang, embedded }) {
91
- const ret = {};
92
- const langs = (0, misc_1.wantArray)(lang).map(x => x.toLowerCase());
93
- let i = 0;
94
- while (i < langs.length) {
95
- let x = langs[i];
96
- if (x === embedded)
97
- break;
98
- try {
99
- ret[x] = JSON.parse(await (0, promises_1.readFile)(`hfs-lang-${x}.json`, 'utf8'));
100
- }
101
- catch (_a) {
102
- do {
103
- x = x.substring(0, x.lastIndexOf('-'));
104
- } while (x && langs.includes(x));
105
- if (x) {
106
- langs[i] = x; // overwrite and retry
107
- continue;
108
- }
109
- }
110
- i++;
111
- }
112
- return ret;
113
- }
114
89
  };
115
90
  function notifyClient(ctx, name, data) {
116
91
  const { notificationChannel } = ctx.query;
package/src/github.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.searchPlugins = exports.getFolder2repo = exports.readOnlinePlugin = exports.getRepoInfo = exports.downloadPlugin = void 0;
7
+ exports.getProjectInfo = exports.searchPlugins = exports.getFolder2repo = exports.readOnlinePlugin = exports.readGithubFile = exports.getRepoInfo = exports.downloadPlugin = void 0;
8
8
  const events_1 = __importDefault(require("./events"));
9
9
  const misc_1 = require("./misc");
10
10
  const plugins_1 = require("./plugins");
@@ -48,12 +48,14 @@ function getRepoInfo(id) {
48
48
  return apiGithub('repos/' + id);
49
49
  }
50
50
  exports.getRepoInfo = getRepoInfo;
51
+ function readGithubFile(uri) {
52
+ return (0, misc_1.httpsString)('https://raw.githubusercontent.com/' + uri)
53
+ .then(res => res.body);
54
+ }
55
+ exports.readGithubFile = readGithubFile;
51
56
  async function readOnlinePlugin(repoInfo, branch = '') {
52
- const url = `https://raw.githubusercontent.com/${repoInfo.full_name}/${branch || repoInfo.default_branch}/${DIST_ROOT}plugin.js`;
53
- const res = await (0, misc_1.httpsString)(url);
54
- if (!res.ok)
55
- throw res.statusCode;
56
- const pl = (0, plugins_1.parsePluginSource)(repoInfo.full_name, res.body); // use 'repo' as 'id' client-side
57
+ const res = await readGithubFile(`${repoInfo.full_name}/${branch || repoInfo.default_branch}/${DIST_ROOT}plugin.js`);
58
+ const pl = (0, plugins_1.parsePluginSource)(repoInfo.full_name, res); // use 'repo' as 'id' client-side
57
59
  pl.branch = branch || undefined;
58
60
  return pl;
59
61
  }
@@ -76,10 +78,13 @@ async function apiGithub(uri) {
76
78
  return JSON.parse(res.body);
77
79
  }
78
80
  async function* searchPlugins(text = '') {
79
- var _a;
81
+ var _a, _b;
82
+ const projectInfo = await getProjectInfo();
80
83
  const res = await apiGithub('search/repositories?q=topic:hfs-plugin+' + encodeURI(text));
81
84
  for (const it of res.items) {
82
85
  const repo = it.full_name;
86
+ if ((_a = projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.plugins_blacklist) === null || _a === void 0 ? void 0 : _a.includes(repo))
87
+ continue;
83
88
  let pl = await readOnlinePlugin(it);
84
89
  if (!pl.apiRequired)
85
90
  continue; // mandatory field
@@ -100,9 +105,22 @@ async function* searchPlugins(text = '') {
100
105
  continue;
101
106
  Object.assign(pl, {
102
107
  downloading: downloading[repo],
103
- license: (_a = it.license) === null || _a === void 0 ? void 0 : _a.spdx_id,
108
+ license: (_b = it.license) === null || _b === void 0 ? void 0 : _b.spdx_id,
104
109
  }, lodash_1.default.pick(it, ['pushed_at', 'stargazers_count']));
105
110
  yield pl;
106
111
  }
107
112
  }
108
113
  exports.searchPlugins = searchPlugins;
114
+ // centralized hosted information, to be used as little as possible
115
+ let cache;
116
+ function getProjectInfo() {
117
+ return cache || (cache = readGithubFile(const_1.HFS_REPO + '/main/central.json').then(x => {
118
+ if (!x)
119
+ throw x; // go catch
120
+ setTimeout(() => cache = null, const_1.DAY); // invalidate cache
121
+ return JSON.parse(x);
122
+ }).catch(() => {
123
+ setTimeout(() => cache = null, 10000);
124
+ }));
125
+ }
126
+ exports.getProjectInfo = getProjectInfo;
package/src/index.js CHANGED
@@ -23,11 +23,12 @@ const config_1 = require("./config");
23
23
  const assert_1 = require("assert");
24
24
  const lodash_1 = __importDefault(require("lodash"));
25
25
  const misc_1 = require("./misc");
26
+ const koa_session_1 = __importDefault(require("koa-session"));
26
27
  (0, assert_1.ok)(lodash_1.default.intersection(Object.keys(frontEndApis_1.frontEndApis), Object.keys(adminApis_1.adminApis)).length === 0); // they share same endpoints
27
28
  const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(',')) || [(0, misc_1.randomId)(30)];
28
29
  exports.app = new koa_1.default({ keys });
29
30
  exports.app.use(middlewares_1.someSecurity)
30
- .use((0, middlewares_1.sessions)(exports.app))
31
+ .use((0, koa_session_1.default)({ key: 'hfs_$id', signed: true, rolling: true, maxAge: const_1.SESSION_DURATION }, exports.app))
31
32
  .use(middlewares_1.prepareState)
32
33
  .use(middlewares_1.headRequests)
33
34
  .use((0, log_1.log)())
package/src/lang.js ADDED
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getLangData = exports.file2code = exports.code2file = void 0;
7
+ const misc_1 = require("./misc");
8
+ const promises_1 = require("fs/promises");
9
+ const config_1 = require("./config");
10
+ const watchLoad_1 = require("./watchLoad");
11
+ const embedded_1 = __importDefault(require("./langs/embedded"));
12
+ const PREFIX = 'hfs-lang-';
13
+ const SUFFIX = '.json';
14
+ const EMBEDDED_LANGUAGE = 'en';
15
+ function code2file(code) {
16
+ return PREFIX + code.toLowerCase() + SUFFIX;
17
+ }
18
+ exports.code2file = code2file;
19
+ function file2code(fn) {
20
+ return fn.slice(PREFIX.length, -SUFFIX.length);
21
+ }
22
+ exports.file2code = file2code;
23
+ async function getLangData(ctx) {
24
+ const param = String(ctx.query.lang || '');
25
+ if (!param && forceLangData)
26
+ return forceLangData;
27
+ const ret = {};
28
+ const csv = param || ctx.get('Accept-Language') || '';
29
+ const langs = (0, misc_1.wantArray)(csv.split(',').map(x => x.toLowerCase()));
30
+ let i = 0;
31
+ while (i < langs.length) {
32
+ let k = langs[i] || ''; // shut up ts
33
+ if (!k || k === EMBEDDED_LANGUAGE)
34
+ break;
35
+ try {
36
+ ret[k] = JSON.parse(await (0, promises_1.readFile)(`hfs-lang-${k}.json`, 'utf8'));
37
+ }
38
+ catch (_a) {
39
+ if ((0, misc_1.hasProp)(embedded_1.default, k))
40
+ ret[k] = embedded_1.default[k];
41
+ else {
42
+ do {
43
+ k = k.substring(0, k.lastIndexOf('-'));
44
+ } while (k && langs.includes(k));
45
+ if (k) {
46
+ langs[i] = k; // overwrite and retry
47
+ continue;
48
+ }
49
+ }
50
+ }
51
+ i++;
52
+ }
53
+ return ret;
54
+ }
55
+ exports.getLangData = getLangData;
56
+ let forceLangData;
57
+ let undo;
58
+ (0, config_1.defineConfig)('force_lang', '', v => {
59
+ undo === null || undo === void 0 ? void 0 : undo();
60
+ if (!v)
61
+ return forceLangData = undefined;
62
+ forceLangData = {}; // necessary to make the embedded language work
63
+ const res = (0, watchLoad_1.watchLoad)(code2file(v), data => {
64
+ forceLangData = { [v]: JSON.parse(data) };
65
+ });
66
+ undo = res.unwatch;
67
+ });
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const hfs_lang_it_json_1 = __importDefault(require("./hfs-lang-it.json"));
7
+ const hfs_lang_zh_json_1 = __importDefault(require("./hfs-lang-zh.json"));
8
+ const hfs_lang_ru_json_1 = __importDefault(require("./hfs-lang-ru.json"));
9
+ const hfs_lang_sr_json_1 = __importDefault(require("./hfs-lang-sr.json"));
10
+ const hfs_lang_ko_json_1 = __importDefault(require("./hfs-lang-ko.json"));
11
+ const hfs_lang_ms_json_1 = __importDefault(require("./hfs-lang-ms.json"));
12
+ const hfs_lang_zh_tw_json_1 = __importDefault(require("./hfs-lang-zh-tw.json"));
13
+ exports.default = { it: hfs_lang_it_json_1.default, zh: hfs_lang_zh_json_1.default, ru: hfs_lang_ru_json_1.default, sr: hfs_lang_sr_json_1.default, ko: hfs_lang_ko_json_1.default, ms: hfs_lang_ms_json_1.default, 'zh-tw': hfs_lang_zh_tw_json_1.default };
@@ -0,0 +1,100 @@
1
+ {
2
+ "author": "Massimo Melina",
3
+ "version": 1.5,
4
+ "hfs_version": "0.43.0",
5
+ "translate": {
6
+ "Select": "Seleziona",
7
+ "n_files": "{n} file",
8
+ "n_folders": "{n,plural, one{# cartella} other{# cartelle}}",
9
+ "filter_count": "{n,plural, one{# filtrato} other{# filtrati}}",
10
+ "select_count": "{n,plural, one{# selezionato} other{# selezionati}}",
11
+ "filter_placeholder": "Digita qui per filtrare la lista dei file",
12
+ "Select some files": "Solo alcuni file",
13
+ "zip_checkboxes": "Spunta le caselle per selezionare i file desiderati, e poi clicca di nuovo su Zip",
14
+ "zip_tooltip_selected": "Scarica gli elementi selezionati come singolo file zip",
15
+ "zip_tooltip_whole": "Scarica intera lista (non considera il filtro) come singolo file zip. Se selezioni alcuni elementi, solo quelli saranno inclusi.",
16
+ "zip_confirm_search": "Scarica TUTTI i risultati di questa ricerca come file zip?",
17
+ "zip_confirm_folder": "Scarica l'INTERA artella come file zip?",
18
+ "select_tooltip": "Selezionare serve con la funziona Zip e per cancellare (dove disponibile), ma puoi anche filtrare la lista",
19
+ "delete_hint": "Per poter cancellare bisogna prima cliccare Seleziona",
20
+ "delete_confirm": "Cancellare {n,plural, one{l'elemento} =8 =11 {gli # elementi} other{i # elementi}}?",
21
+ "delete_completed": "Cancellazione: {n} riusciti",
22
+ "delete_failed": ", {n,plural, one{# fallito} other{# falliti}}",
23
+ "delete_select": "Seleziona qualcosa per poter cancellare",
24
+ "Delete": "Cancella",
25
+ "Options": "Opzioni",
26
+ "Search": "Cerca",
27
+ "search_msg": "Cerca in questa cartella e cartelle sottostanti",
28
+ "Searching": "Cercando",
29
+ "Searched": "Risultati ricerca",
30
+ "Clear search": "Annulla ricerca",
31
+ "Interrupted": "interrotto",
32
+ "stopped_before": "Ricerca interrotta prima di trovare risultati",
33
+ "empty_list": "Non c'è niente qui",
34
+ "filter_none": "Nessun elemento corrisponde al filtro impostato",
35
+ "Login": "Entra",
36
+ "Username": "Nome utente",
37
+ "Continue": "Avanti",
38
+ "login_untrusted": "Login interrotto: server non affidabile",
39
+ "login_bad_credentials": "Nome utente o password non validi",
40
+ "login_bad_cookies": "Login interrotto: non funzionano i cookies",
41
+ "User panel": "Pannello utente",
42
+ "Change password": "Cambia password",
43
+ "enter_pass": "Inserisci nuova password",
44
+ "enter_pass2": "Ripeti la stessa password",
45
+ "pass2_mismatch": "Hai scritto 2 volte la password ma non sono uguali.",
46
+ "password_changed": "La password è stata cambiata",
47
+ "Logout": "Esci",
48
+ "connection error": "errore di connessione",
49
+ "Full timestamp:": "Data e ora:",
50
+ "Search was interrupted": "Ricerca interrotta",
51
+ "Stop list": "Interrompi",
52
+ "upload_starting": "Ora dovrebbe iniziare il download",
53
+ "wrong_account": "L'account {u} non ha accesso a questa cartella, prova con un altro",
54
+ "no_upload_here": "Non hai il permesso di uplodare in questa cartella",
55
+ "Create folder": "Crea cartella",
56
+ "Pick files": "Scegli file",
57
+ "Pick folder": "Scegli una cartella",
58
+ "send_files": "Invia {n} file, {size}",
59
+ "Clear": "Azzera",
60
+ "failed_upload": "Upload fallito per {name}",
61
+ "confirm_resume": "Vuoi riprendere questo upload?",
62
+ "file too large": "file troppo grande",
63
+ "Enter folder name": "Inserisci nome cartella",
64
+ "Successfully created": "Creazione riuscita",
65
+ "enter_folder": "Accedi alla nuova cartella",
66
+ "folder_exists": "Questo nome esiste già",
67
+ "Sort by": "Ordinamento",
68
+ "name": "nome",
69
+ "extension": "estensione",
70
+ "size": "dimensioni",
71
+ "time": "data/ora",
72
+ "Invert order": "Inverti ordine",
73
+ "Folders first": "Prima le cartelle",
74
+ "Numeric names": "Rispetta numeri all'inizio",
75
+ "theme:": "tema:",
76
+ "auto": "automatico",
77
+ "light": "chiaro",
78
+ "dark": "scuro",
79
+ "parent folder": "cartella superiore",
80
+ "Confirm": "Conferma",
81
+ "Don't": "Annulla",
82
+ "Warning": "Attenzione",
83
+ "Error": "Errore",
84
+ "Unauthorized": "Non autorizzato",
85
+ "Forbidden": "Vietato",
86
+ "Not found": "Non trovato",
87
+ "Server error": "Errore del server",
88
+ "upload_concluded": "Upload concluso",
89
+ "upload_finished": "{n,plural, one{# riuscito} other{# riusciti}} ({size})",
90
+ "upload_errors": "{n,plural, one{# fallito} other{# falliti}}",
91
+ "upload_file_rejected": "Alcuni file non sono stati accettati",
92
+ "download counter": "download conteggiati",
93
+ "File menu": "Menu file",
94
+ "Folder menu": "Menu cartella",
95
+ "Name": "Nome",
96
+ "file_open": "Apri",
97
+ "Download": "Download",
98
+ "Missing permission": "Permesso mancante"
99
+ }
100
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "author": "배서연",
3
+ "version": 1.0,
4
+ "hfs_version": "0.42.3",
5
+ "translate": {
6
+ "Select": "선택",
7
+ "n_files": "{n,plural,one{1개의 파일} =2 =3{파일 #개} other{#개 파일}}",
8
+ "n_folders": "{n,plural,one{1 폴더} =2 =3 =4 {#개의 폴더} other{#개 폴더}",
9
+ "filter_count": "{n}개 통과",
10
+ "select_count": "{n}선정",
11
+ "filter_placeholder": "아래 목록을 필터링하려면 여기에 입력하세요.",
12
+ "Select some files": "파일선택",
13
+ "zip_checkboxes": "파일을 선택한 후 압축하기를 클릭해 주세요.",
14
+ "zip_tooltip_selected": "선택한 요소를 하나의 ZIP 파일로 다운로드",
15
+ "zip_tooltip_whole": "전체 목록(필터링되지 않은)을 하나의 ZIP 파일로 다운로드합니다. 항목을 선택한 경우 선택한 항목만 다운로드됩니다.",
16
+ "zip_confirm_search": "이 검색 결과를 모두 ZIP 아카이브로 다운로드하시겠습니까?",
17
+ "zip_confirm_folder": "전체 폴더를 ZIP 아카이브로 다운로드하시겠습니까?",
18
+ "select_tooltip": "압축하기 및 삭제하기(사용 가능한 경우)에 선택이 적용됩니다. 목록을 필터링할 수도 있습니다.",
19
+ "delete_hint": "삭제하려면 먼저 항목을 선택하세요.",
20
+ "delete_confirm": "{n,plural, one{# item} other{# items}}개 항목을 삭제하시겠습니까?",
21
+ "delete_completed": "{n}개의 항목이 삭제되었습니다.",
22
+ "delete_failed": ", {n,plural, one{# failed} other{# failed}}",
23
+ "delete_select": "삭제할 항목을 선택하세요.",
24
+ "Delete": "삭제",
25
+ "Options": "옵션",
26
+ "Search": "검색",
27
+ "Zip": "Zip으로 압축",
28
+ "search_msg": "검색",
29
+ "Searching": "검색 중",
30
+ "Searched": "검색 완료",
31
+ "Clear search": "검색결과 지우기",
32
+ "Interrupted": "중단됨",
33
+ "stopped_before": "이미 중단된 작업입니다.",
34
+ "empty_list": "목록이 비어 있습니다.",
35
+ "filter_none": "필터 없음",
36
+ "Admin-panel": "관리자 패널",
37
+ "Login": "로그인",
38
+ "Username": "사용자명",
39
+ "Password": "비밀번호",
40
+ "login_untrusted": "로그인 중단: 서버를 신뢰할 수 없음",
41
+ "login_bad_credentials": "잘못된 자격 증명",
42
+ "login_bad_cookies": "쿠키 오류 - 로그인 실패",
43
+ "User panel": "사용자 패널",
44
+ "Change password": "비밀번호 변경",
45
+ "enter_pass": "새 비밀번호 입력",
46
+ "enter_pass2": "새 비밀번호 재입력",
47
+ "pass2_mismatch": "새 비밀번호가 일치하지 않습니다.",
48
+ "password_changed": "비밀번호가 변경되었습니다.",
49
+ "Logout": "로그아웃",
50
+ "connection error": "연결 오류",
51
+ "Full timestamp:": "전체 타임스탬프:",
52
+ "Search was interrupted": "검색이 중단되었습니다.",
53
+ "Stop list": "중지 목록",
54
+ "upload_starting": "요청이 처리 중입니다.",
55
+ "wrong_account": "계정 {u}은(는) 접근 권한이 없습니다. 다른 계정을 시도해보십시오.",
56
+ "Create folder": "폴더 생성",
57
+ "Pick files": "파일 선택",
58
+ "Pick folder": "폴더 선택",
59
+ "send_files": "{n,plural,one{# file} other{# files}}, {size} 보내기",
60
+ "Clear": "지우기",
61
+ "failed_upload": "{name} 업로드에 실패했습니다.",
62
+ "confirm_resume": "업로드를 재시도 하시겠습니까?",
63
+ "Enter folder name": "폴더 이름 입력",
64
+ "Successfully created": "성공적으로 생성되었습니다.",
65
+ "enter_folder": "폴더 입력",
66
+ "folder_exists": "동일한 이름의 폴더가 이미 존재합니다.",
67
+ "Sort by": "정렬 기준",
68
+ "name": "이름",
69
+ "extension": "확장자",
70
+ "size": "크기",
71
+ "time": "시간",
72
+ "Invert order": "역순 정렬",
73
+ "Folders first": "폴더 우선 정렬",
74
+ "Numeric names": "숫자 우선 정렬",
75
+ "theme": "테마",
76
+ "auto": "자동",
77
+ "light": "밝은 테마",
78
+ "dark": "어두운 테마",
79
+ "parent folder": "상위 폴더",
80
+ "home": "홈",
81
+ "Continue": "계속하기",
82
+ "Confirm": "확인",
83
+ "Don't": "취소",
84
+ "Warning": "경고",
85
+ "Error": "오류",
86
+ "Info": "정보",
87
+ "Unauthorized": "허용되지 않음",
88
+ "Forbidden": "접근금지",
89
+ "Not found": "찾을 수 없음",
90
+ "Server error": "서버 오류",
91
+ "Upload": "업로드",
92
+ "upload_concluded": "업로드가 완료되었습니다.",
93
+ "upload_finished": "{n}개 파일 완료 ({size})",
94
+ "upload_errors": "{n}개 파일 실패",
95
+ "upload_file_rejected": "일부 파일은 업로드되지 않았습니다.",
96
+ "download counter": "다운로드 횟수",
97
+ "File menu": "파일 메뉴",
98
+ "Name": "이름",
99
+ "file_open": "열기",
100
+ "Download": "다운로드",
101
+ "Missing permission": "권한이 존재하지 않습니다."
102
+ }
103
+ }
@@ -0,0 +1,70 @@
1
+ {
2
+ "author": "ChatGPT",
3
+ "version": 1.0,
4
+ "hfs_version": "0.43.0",
5
+ "translate": {
6
+ "Select": "Pilih",
7
+ "n_files": "{n,plural, one{# fail} other{# fail}}",
8
+ "n_folders": "{n,plural, one{# folder} other{# folder}}",
9
+ "filter_count": "{n,plural, one{# disenaraikan} other{# disenaraikan}}",
10
+ "select_count": "{n,plural, one{# dipilih} other{# dipilih}}",
11
+ "filter_placeholder": "Taip di sini untuk menyaring senarai di bawah",
12
+ "Select some files": "Pilih beberapa fail",
13
+ "zip_checkboxes": "Guna kotak semak untuk memilih fail, kemudian anda boleh gunakan Zip lagi",
14
+ "zip_tooltip_selected": "Muat turun elemen yang dipilih sebagai fail zip tunggal",
15
+ "zip_tooltip_whole": "Muat turun senarai keseluruhan (tidak disaring) sebagai fail zip tunggal. Jika anda memilih beberapa elemen, hanya yang dipilih akan dimuat turun.",
16
+ "zip_confirm_search": "Muat turun SEMUA hasil carian ini sebagai arkib ZIP?",
17
+ "zip_confirm_folder": "Muat turun FOLDER KESULURUHAN sebagai arkib ZIP?",
18
+ "select_tooltip": "Pilihan digunakan untuk \"Zip\" dan \"Padam\" (apabila tersedia), tetapi anda juga boleh menyaring senarai",
19
+ "delete_hint": "Untuk memadam, klik Pilih dahulu",
20
+ "delete_confirm": "Padam {n,plural, one{# item} other{# items}}?",
21
+ "delete_completed": "Penghapusan: {n} selesai",
22
+ "delete_failed": ", {n,plural, one{# gagal} other{# gagal}}",
23
+ "delete_select": "Pilih sesuatu untuk dipadam",
24
+ "Delete": "Padam",
25
+ "Options": "Pilihan",
26
+ "Search": "Carian",
27
+ "Zip": "Zip",
28
+ "search_msg": "Cari folder ini dan sub-folder",
29
+ "Searching": "Mencari",
30
+ "Searched": "Dicari",
31
+ "Clear search": "Kosongkan carian",
32
+ "Interrupted": "Dihentikan",
33
+ "stopped_before": "berhenti_sebelum",
34
+ "empty_list": "senarai_kosong",
35
+ "filter_none": "filter_tidak_ada",
36
+ "Admin-panel": "Panel pentadbir",
37
+ "Login": "Log masuk",
38
+ "Username": "Nama pengguna",
39
+ "Password": "Kata laluan",
40
+ "login_untrusted": "Log masuk dibatalkan: identiti pelayan tidak boleh dipercayai",
41
+ "login_bad_credentials": "Kelayakan tidak sah",
42
+ "login_bad_cookies": "Kuki tidak berfungsi - log masuk gagal",
43
+ "User panel": "Panel pengguna",
44
+ "Change password": "Tukar kata laluan",
45
+ "enter_pass": "Masukkan kata laluan baru",
46
+ "enter_pass2": "Masukkan semula kata laluan baru yang sama",
47
+ "pass2_mismatch": "Kata laluan kedua yang anda masukkan tidak sepadan dengan yang pertama.",
48
+ "Confirm": "Sahkan",
49
+ "Don't": "Jangan",
50
+ "Warning": "Amaran",
51
+ "Error": "Ralat",
52
+ "Info": "Maklumat",
53
+ "Unauthorized": "Tidak dibenarkan",
54
+ "Forbidden": "Dilarang",
55
+ "Not found": "Tidak ditemui",
56
+ "Server error": "Ralat pelayan",
57
+ "Upload": "Muat naik",
58
+ "upload_concluded": "Muat naik selesai:",
59
+ "upload_finished": "{n} selesai ({size})",
60
+ "upload_errors": "{n} gagal",
61
+ "upload_file_rejected": "Beberapa fail tidak diterima",
62
+ "download counter": "pengira muat turun",
63
+ "File menu": "Menu fail",
64
+ "Folder menu": "Menu folder",
65
+ "Name": "Nama",
66
+ "file_open": "Buka",
67
+ "Download": "Muat turun",
68
+ "Missing permission": "Keizinan hilang"
69
+ }
70
+ }