mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-16 01:56:55 +00:00
4.15.4
* Added: Setting to hide the mass gift sub banner at the top of chat. * Changed: Messages for redeeming Channel Points now have custom rendering with less padding and background colors to properly highlight them. * Fixed: Moderation profiles not always applying when navigating between channels. * Fixed: Settings to disable auto-play not working. * Fixed: Remove debug logging when importing a profile.
This commit is contained in:
parent
0fdb988da7
commit
8ac1b2ce91
18 changed files with 481 additions and 44 deletions
153
bin/static_i18n.js
Normal file
153
bin/static_i18n.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
const transformSync = require('@babel/core').transformSync;
|
||||
const type = require('@babel/types');
|
||||
const traverse = require('@babel/traverse').default;
|
||||
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
|
||||
function matchesPattern(member, match, allowPartial = false) {
|
||||
if ( ! type.isMemberExpression(member) )
|
||||
return false;
|
||||
|
||||
const parts = Array.isArray(match) ? match : match.split('.');
|
||||
const nodes = [];
|
||||
|
||||
let node;
|
||||
for(node = member; type.isMemberExpression(node); node = node.object)
|
||||
nodes.push(node.property);
|
||||
|
||||
nodes.push(node);
|
||||
|
||||
if ( nodes.length < parts.length )
|
||||
return false;
|
||||
if ( ! allowPartial && nodes.length > parts.length )
|
||||
return false;
|
||||
|
||||
for(let i = 0, j = nodes.length - 1; i < parts.length; i++, j--) {
|
||||
const node = nodes[j];
|
||||
let value;
|
||||
if ( type.isIdentifier(node) )
|
||||
value = node.name;
|
||||
else if ( type.isStringLiteral(node) )
|
||||
value = node.value;
|
||||
else if ( type.isThisExpression(node) )
|
||||
value = 'this';
|
||||
else
|
||||
return false;
|
||||
|
||||
if ( parts[i] !== value )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const babelOptions = {
|
||||
ast: true,
|
||||
parserOpts: JSON.parse(fs.readFileSync('.babelrc', 'utf8'))
|
||||
};
|
||||
|
||||
babelOptions.parserOpts.plugins = [
|
||||
'jsx',
|
||||
'dynamicImport',
|
||||
'optionalChaining',
|
||||
'objectRestSpread'
|
||||
]
|
||||
|
||||
function getString(node) {
|
||||
if ( type.isStringLiteral(node) )
|
||||
return node.value;
|
||||
|
||||
if ( type.isTemplateLiteral(node) && (! node.expressions || ! node.expressions.length) && node.quasis && node.quasis.length === 1 )
|
||||
return node.quasis[0].value.cooked;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractFromCode(code) {
|
||||
const { ast } = transformSync(code, babelOptions);
|
||||
const matches = [];
|
||||
|
||||
traverse(ast, {
|
||||
CallExpression(path) {
|
||||
const callee = path.get('callee');
|
||||
if ( ! callee )
|
||||
return;
|
||||
|
||||
if ( !( matchesPattern(callee.node, 'this.i18n.t') ||
|
||||
matchesPattern(callee.node, 'i18n.t') ||
|
||||
matchesPattern(callee.node, 't.i18n.t') ||
|
||||
matchesPattern(callee.node, 'this.i18n.tList') ||
|
||||
matchesPattern(callee.node, 'i18n.tList') ||
|
||||
matchesPattern(callee.node, 't.i18n.tList') ||
|
||||
matchesPattern(callee.node, 'this.t') ||
|
||||
matchesPattern(callee.node, 'this.tList') ))
|
||||
return;
|
||||
|
||||
const key = getString(path.get('arguments.0').node);
|
||||
if ( ! key )
|
||||
return;
|
||||
|
||||
matches.push({
|
||||
key,
|
||||
loc: path.node.loc.start,
|
||||
phrase: getString(path.get('arguments.1').node)
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
function extractFromFiles(files) {
|
||||
const results = [];
|
||||
|
||||
if ( ! Array.isArray(files) )
|
||||
files = [files];
|
||||
|
||||
const scannable = new Set;
|
||||
for(const thing of files) {
|
||||
for(const file of glob.sync(thing, {}))
|
||||
scannable.add(file);
|
||||
}
|
||||
|
||||
for(const file of scannable) {
|
||||
const code = fs.readFileSync(file, 'utf8');
|
||||
const matches = extractFromCode(code);
|
||||
for(const match of matches) {
|
||||
match.source = `${file}:${match.loc.line}:${match.loc.column}`;
|
||||
delete match.loc;
|
||||
results.push(match);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
const bits = extractFromFiles([
|
||||
'src/**/*.js',
|
||||
'src/**/*.jsx'
|
||||
]);
|
||||
|
||||
const seen = new Set;
|
||||
const out = [];
|
||||
|
||||
for(const entry of bits) {
|
||||
if ( seen.has(entry.key) )
|
||||
continue;
|
||||
|
||||
seen.add(entry.key);
|
||||
if ( entry.key && entry.phrase )
|
||||
out.push({
|
||||
key: entry.key,
|
||||
phrase: entry.phrase,
|
||||
calls: [
|
||||
`/${entry.source}`
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync('extracted.json', JSON.stringify(out, null, '\t'));
|
||||
|
||||
console.log(`Extracted ${out.length} strings.`);
|
Loading…
Add table
Add a link
Reference in a new issue