-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.js
144 lines (128 loc) · 3.9 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const fs = require('fs');
const path = require('path');
const PO = require('pofile');
/**
* Recursively list all files in a directory.
* @param dir {string} The directory to list files from.
* @param fileList {string[]} The list of files to append to.
* @returns {string[]} The list of files.
*/
function listFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
files.forEach(file => {
let filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
listFiles(filePath, fileList);
} else {
filePath = filePath
.replace(__dirname, '')
.replaceAll('\\', '/');
fileList.push(filePath);
}
});
return fileList;
}
exports.listFiles = listFiles;
/**
* Translates a string.
* @param str The string to translate.
* @param poFile The PO file to use.
* @param args The arguments to replace.
* @returns {*|string} The translated string.
*/
function i18n(str, poFile, args) {
const item = poFile.items.find(i => i.msgid === str);
if (!item) {
console.warn(`${poFile.headers.Language}: No translation found for "${str}"`);
}
str = (item && item.msgstr[0]) || str;
for (let i = 0; i < args.length; i++) {
str = str.replace(`{${i}}`, args[i]);
}
return str;
}
exports.i18n = i18n;
/** @type {Map<string, PO>} */
const loadedPOFiles = new Map();
// returning reference
exports.getLoadedPOFiles = () => ({ loadedPOFiles });
/**
* Gets the language data for the request. If the language code is not found, it will fall back to English.
* @param req The request object.
* @returns {Promise<{poFile: PO, langCode: string}>}
*/
async function getLanguageData(req) {
let langCode = req.cookies['pxls-accept-language-override'] || 'en';
const poDirFiles = fs.readdirSync(path.join(__dirname, 'po'));
if (!poDirFiles.includes(`Localization${langCode === 'en' ? '' : '_' + langCode}.po`)) {
langCode = 'en';
}
const poFile = await getPO(langCode);
return { poFile, langCode };
}
exports.getLanguageData = getLanguageData;
/**
* Gets the PO file for the language code.
* @param langCode The language code.
* @returns {Promise<PO>} A promise that resolves to the PO file.
*/
async function getPO(langCode) {
if (loadedPOFiles.has(langCode)) {
return loadedPOFiles.get(langCode);
}
const poFile = await loadPO(path.join(__dirname, 'po', `Localization${langCode === 'en' ? '' : '_' + langCode}.po`));
loadedPOFiles.set(langCode, poFile);
return poFile;
}
/**
* Loads a PO file.
* @param path The path to the PO file.
* @returns {Promise<PO>} A promise that resolves to the PO file.
*/
function loadPO(path) {
return new Promise((resolve, reject) => {
PO.load(path, (error, poFile) => {
if (error) {
reject(error);
} else {
resolve(poFile);
}
});
});
}
exports.loadPO = loadPO;
/**
* Proxies a fetch request with request headers.
* @param req The request object.
* @param url The URL to proxy to.
* @param options The fetch options.
* @returns {Promise<Response>} A promise that resolves to the response.
*/
function proxyFetch(req, url, options = {}) {
return new Promise((resolve, reject) => fetch(url, {
...options,
headers: req.headers
})
.then(res => res.ok ? resolve(res) : reject(res))
.catch(reject));
}
exports.proxyFetch = proxyFetch;
exports.handlebarsHelpers = (poFile) => ({
i18n: (str, ...args) => i18n(str, poFile, args),
isoTime: (time) => new Date(time).toISOString(),
localeDate: (time) => new Date(time * 1000).toLocaleString(),
default: (value, defaultValue) => value || defaultValue,
len: (value) => value.length,
eq: (v1, v2) => v1 === v2,
ne: (v1, v2) => v1 !== v2,
lt: (v1, v2) => v1 < v2,
gt: (v1, v2) => v1 > v2,
lte: (v1, v2) => v1 <= v2,
gte: (v1, v2) => v1 >= v2,
and() {
return Array.prototype.every.call(arguments, Boolean);
},
or() {
return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
}
});