Implementation of the translation system, inc generators.
This commit is contained in:
parent
2293302fbd
commit
27a92af825
24 changed files with 11690 additions and 371 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,7 +9,7 @@ generated/
|
||||||
node_modules/
|
node_modules/
|
||||||
auth/certs/
|
auth/certs/
|
||||||
auth/js/auth.js.map
|
auth/js/auth.js.map
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as ts from "typescript";
|
import * as ts from "typescript";
|
||||||
import * as generator from "./generator";
|
import * as generator from "./ts_generator";
|
||||||
|
|
||||||
import {readFileSync} from "fs";
|
import {readFileSync} from "fs";
|
||||||
import * as glob from "glob";
|
import * as glob from "glob";
|
||||||
|
@ -48,7 +48,6 @@ function compile(fileNames: string[], options: ts.CompilerOptions): void {
|
||||||
process.exit(exitCode);
|
process.exit(exitCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Arguments: " + process.argv.slice(2));
|
|
||||||
const config = ts.parseCommandLine(process.argv.slice(2), file => readFileSync(file).toString());
|
const config = ts.parseCommandLine(process.argv.slice(2), file => readFileSync(file).toString());
|
||||||
console.dir(config);
|
console.dir(config);
|
||||||
if(config.errors && config.errors.length > 0) {
|
if(config.errors && config.errors.length > 0) {
|
||||||
|
|
|
@ -1,280 +1,3 @@
|
||||||
import * as ts from "typescript";
|
|
||||||
import * as sha256 from "sha256";
|
|
||||||
import {SyntaxKind} from "typescript";
|
|
||||||
|
|
||||||
|
|
||||||
export function generate(file: ts.SourceFile, config: Configuration) : TranslationEntry[] {
|
|
||||||
let result: TranslationEntry[] = [];
|
|
||||||
|
|
||||||
file.forEachChild(n => _generate(config, n, result));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function report(node: ts.Node, message: string) {
|
|
||||||
const sf = node.getSourceFile();
|
|
||||||
let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
|
||||||
console.log(`${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1}): ${message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _generate(config: Configuration, node: ts.Node, result: TranslationEntry[]) {
|
|
||||||
//console.log("Node: %s", SyntaxKind[node.kind]);
|
|
||||||
|
|
||||||
call_analize:
|
|
||||||
if(ts.isCallExpression(node)) {
|
|
||||||
const call = <ts.CallExpression>node;
|
|
||||||
const call_name = call.expression["escapedText"] as string;
|
|
||||||
if(call_name != "tr") break call_analize;
|
|
||||||
|
|
||||||
console.dir(call_name);
|
|
||||||
|
|
||||||
console.log("Parameters: %o", call.arguments.length);
|
|
||||||
if(call.arguments.length > 1) {
|
|
||||||
report(call, "Invalid argument count");
|
|
||||||
node.forEachChild(n => _generate(config, n, result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const object = <ts.StringLiteral>call.arguments[0];
|
|
||||||
if(object.kind != SyntaxKind.StringLiteral) {
|
|
||||||
report(call, "Invalid argument: " + SyntaxKind[object.kind]);
|
|
||||||
node.forEachChild(n => _generate(config, n, result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Message: %o", object.text);
|
|
||||||
|
|
||||||
//FIXME
|
|
||||||
if(config.replace_cache) {
|
|
||||||
console.log("Update!");
|
|
||||||
ts.updateCall(call, call.expression, call.typeArguments, [ts.createLiteral("PENIS!")]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
|
||||||
result.push({
|
|
||||||
filename: node.getSourceFile().fileName,
|
|
||||||
line: line,
|
|
||||||
character: character,
|
|
||||||
message: object.text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
node.forEachChild(n => _generate(config, n, result));
|
|
||||||
}
|
|
||||||
function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] {
|
|
||||||
const nodes: ts.Node[] = [], blocked_nodes: ts.Statement[] = [];
|
|
||||||
|
|
||||||
const node_path = (node: ts.Node) => {
|
|
||||||
const sf = node.getSourceFile();
|
|
||||||
let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
|
||||||
return `${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const create_error = (variable_name: ts.Expression, variable_path: ts.Expression, other_path: ts.Expression) => {
|
|
||||||
return [
|
|
||||||
ts.createLiteral("Translation with generated name \""),
|
|
||||||
variable_name,
|
|
||||||
ts.createLiteral("\" already exists!\nIt has been already defined here: "),
|
|
||||||
other_path,
|
|
||||||
ts.createLiteral("\nAttempted to redefine here: "),
|
|
||||||
variable_path,
|
|
||||||
ts.createLiteral("\nRegenerate and/or fix your program!")
|
|
||||||
].reduce((a, b) => ts.createBinary(a, SyntaxKind.PlusToken, b));
|
|
||||||
};
|
|
||||||
|
|
||||||
let declarations_file: ts.Expression;
|
|
||||||
const unique_check_label_name = "unique_translation_check";
|
|
||||||
|
|
||||||
/* initialization */
|
|
||||||
{
|
|
||||||
const declarations = ts.createElementAccess(variable, ts.createLiteral("declared"));
|
|
||||||
nodes.push(ts.createAssignment(declarations, ts.createBinary(declarations, SyntaxKind.BarBarToken, ts.createAssignment(declarations, ts.createObjectLiteral()))));
|
|
||||||
|
|
||||||
declarations_file = ts.createElementAccess(variable, ts.createLiteral("declared_files"));
|
|
||||||
nodes.push(ts.createAssignment(declarations_file, ts.createBinary(declarations_file, SyntaxKind.BarBarToken, ts.createAssignment(declarations_file, ts.createObjectLiteral()))));
|
|
||||||
|
|
||||||
variable = declarations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test file already loaded */
|
|
||||||
{
|
|
||||||
const unique_id = sha256(source_file.fileName + " | " + (Date.now() / 1000));
|
|
||||||
const property = ts.createElementAccess(declarations_file, ts.createLiteral(unique_id));
|
|
||||||
|
|
||||||
const if_condition = ts.createBinary(property, SyntaxKind.ExclamationEqualsEqualsToken, ts.createIdentifier("undefined"));
|
|
||||||
//
|
|
||||||
let if_then: ts.Block;
|
|
||||||
{
|
|
||||||
const elements: ts.Statement[] = [];
|
|
||||||
|
|
||||||
const console = ts.createIdentifier("console.warn");
|
|
||||||
elements.push(ts.createCall(console, [], [ts.createLiteral("This file has already been loaded!\nAre you executing scripts twice?") as any]));
|
|
||||||
elements.push(ts.createBreak(unique_check_label_name));
|
|
||||||
|
|
||||||
if_then = ts.createBlock(elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
const if_else = ts.createAssignment(property, ts.createLiteral(unique_id));
|
|
||||||
blocked_nodes.push(ts.createIf(if_condition, if_then, if_else as any));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test if variable has been defined somewhere else */
|
|
||||||
{
|
|
||||||
const for_variable_name = ts.createLoopVariable();
|
|
||||||
const for_variable_path = ts.createLoopVariable();
|
|
||||||
const for_declaration = ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createObjectBindingPattern([
|
|
||||||
ts.createBindingElement(undefined, "name", for_variable_name, undefined),
|
|
||||||
ts.createBindingElement(undefined, "path", for_variable_path, undefined)])
|
|
||||||
, undefined, undefined)]);
|
|
||||||
|
|
||||||
let for_block: ts.Statement;
|
|
||||||
{ //Create the for block
|
|
||||||
const elements: ts.Statement[] = [];
|
|
||||||
|
|
||||||
|
|
||||||
const property = ts.createElementAccess(variable, for_variable_name);
|
|
||||||
const if_condition = ts.createBinary(property, SyntaxKind.ExclamationEqualsEqualsToken, ts.createIdentifier("undefined"));
|
|
||||||
|
|
||||||
//
|
|
||||||
const if_then = ts.createThrow(create_error(for_variable_name, for_variable_path, property));
|
|
||||||
const if_else = ts.createAssignment(property, for_variable_path);
|
|
||||||
const if_valid = ts.createIf(if_condition, if_then, if_else as any);
|
|
||||||
|
|
||||||
elements.push(if_valid);
|
|
||||||
|
|
||||||
for_block = ts.createBlock(elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
let block = ts.createForOf(undefined,
|
|
||||||
for_declaration, ts.createArrayLiteral(
|
|
||||||
[...variables.map(e => ts.createObjectLiteral([
|
|
||||||
ts.createPropertyAssignment("name", ts.createLiteral(e.name)),
|
|
||||||
ts.createPropertyAssignment("path", ts.createLiteral(node_path(e.node)))
|
|
||||||
]))
|
|
||||||
])
|
|
||||||
, for_block);
|
|
||||||
block = ts.addSyntheticLeadingComment(block, SyntaxKind.MultiLineCommentTrivia, "Auto generated helper for testing if the translation keys are unique", true);
|
|
||||||
blocked_nodes.push(block);
|
|
||||||
}
|
|
||||||
return [...nodes, ts.createLabel(unique_check_label_name, ts.createBlock(blocked_nodes))];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transform(config: Configuration, context: ts.TransformationContext, node: ts.SourceFile) : TransformResult {
|
|
||||||
const cache: VolatileTransformConfig = {} as any;
|
|
||||||
cache.translations = [];
|
|
||||||
|
|
||||||
//Initialize nodes
|
|
||||||
const extra_nodes: ts.Node[] = [];
|
|
||||||
{
|
|
||||||
cache.nodes = {} as any;
|
|
||||||
if(config.use_window) {
|
|
||||||
const window = ts.createIdentifier("window");
|
|
||||||
let translation_map = ts.createPropertyAccess(window, ts.createIdentifier("_translations"));
|
|
||||||
const new_translations = ts.createAssignment(translation_map, ts.createObjectLiteral());
|
|
||||||
|
|
||||||
let translation_map_init: ts.Expression = ts.createBinary(translation_map, ts.SyntaxKind.BarBarToken, new_translations);
|
|
||||||
translation_map_init = ts.createParen(translation_map_init);
|
|
||||||
|
|
||||||
cache.nodes = {
|
|
||||||
translation_map: translation_map,
|
|
||||||
translation_map_init: translation_map_init
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const variable_name = "_translations";
|
|
||||||
const variable_map = ts.createIdentifier(variable_name);
|
|
||||||
|
|
||||||
const inline_if = ts.createBinary(ts.createBinary(ts.createTypeOf(variable_map), SyntaxKind.ExclamationEqualsEqualsToken, ts.createLiteral("undefined")), ts.SyntaxKind.BarBarToken, ts.createAssignment(variable_map, ts.createObjectLiteral()));
|
|
||||||
|
|
||||||
cache.nodes = {
|
|
||||||
translation_map: variable_map,
|
|
||||||
translation_map_init: variable_map
|
|
||||||
};
|
|
||||||
|
|
||||||
//ts.createVariableDeclarationList([ts.createVariableDeclaration(variable_name)], ts.NodeFlags.Let)
|
|
||||||
extra_nodes.push(inline_if);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const generated_names: { name: string, node: ts.Node }[] = [];
|
|
||||||
cache.name_generator = (config, node, message) => {
|
|
||||||
const characters = "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
||||||
let name = "";
|
|
||||||
while(name.length < 8) {
|
|
||||||
const char = characters[Math.floor(Math.random() * characters.length)];
|
|
||||||
name = name + char;
|
|
||||||
if(name[0] >= '0' && name[0] <= '9')
|
|
||||||
name = name.substr(1) || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//FIXME
|
|
||||||
//if(generated_names.indexOf(name) != -1)
|
|
||||||
// return cache.name_generator(config, node, message);
|
|
||||||
|
|
||||||
generated_names.push({name: name, node: node});
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
|
|
||||||
function visit(node: ts.Node): ts.Node {
|
|
||||||
node = ts.visitEachChild(node, visit, context);
|
|
||||||
return replace_processor(config, cache, node);
|
|
||||||
}
|
|
||||||
node = ts.visitNode(node, visit);
|
|
||||||
extra_nodes.push(...create_unique_check(node, cache.nodes.translation_map_init, generated_names));
|
|
||||||
|
|
||||||
node = ts.updateSourceFileNode(node, [...(extra_nodes as any[]), ...node.statements], node.isDeclarationFile, node.referencedFiles, node.typeReferenceDirectives, node.hasNoDefaultLib, node.referencedFiles);
|
|
||||||
|
|
||||||
const result: TransformResult = {} as any;
|
|
||||||
result.node = node;
|
|
||||||
result.translations = cache.translations;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node) : ts.Node {
|
|
||||||
if(config.verbose)
|
|
||||||
console.log("Process %s", SyntaxKind[node.kind]);
|
|
||||||
if(ts.isCallExpression(node)) {
|
|
||||||
const call = <ts.CallExpression>node;
|
|
||||||
const call_name = call.expression["escapedText"] as string;
|
|
||||||
if(call_name != "tr") return node;
|
|
||||||
|
|
||||||
if(config.verbose) {
|
|
||||||
console.dir(call_name);
|
|
||||||
console.log("Parameters: %o", call.arguments.length);
|
|
||||||
}
|
|
||||||
if(call.arguments.length > 1) {
|
|
||||||
report(call, "Invalid argument count");
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
const object = <ts.StringLiteral>call.arguments[0];
|
|
||||||
if(object.kind != SyntaxKind.StringLiteral) {
|
|
||||||
report(call, "Invalid argument: " + SyntaxKind[object.kind]);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config.verbose)
|
|
||||||
console.log("Message: %o", object.text || object.getText());
|
|
||||||
|
|
||||||
const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText()));
|
|
||||||
const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name);
|
|
||||||
|
|
||||||
const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name);
|
|
||||||
const new_variable = ts.createAssignment(variable, call);
|
|
||||||
|
|
||||||
const source_file = node.getSourceFile();
|
|
||||||
let { line, character } = source_file ? source_file.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
|
||||||
|
|
||||||
cache.translations.push({
|
|
||||||
message: object.text || object.getText(),
|
|
||||||
line: line,
|
|
||||||
character: character,
|
|
||||||
filename: (source_file || {fileName: "unknown"}).fileName
|
|
||||||
});
|
|
||||||
|
|
||||||
return ts.createBinary(variable_init, ts.SyntaxKind.BarBarToken, new_variable);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TranslationEntry {
|
export interface TranslationEntry {
|
||||||
filename: string;
|
filename: string;
|
||||||
|
@ -283,24 +6,3 @@ export interface TranslationEntry {
|
||||||
|
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Configuration {
|
|
||||||
use_window?: boolean;
|
|
||||||
replace_cache?: boolean;
|
|
||||||
verbose?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransformResult {
|
|
||||||
node: ts.SourceFile;
|
|
||||||
translations: TranslationEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VolatileTransformConfig {
|
|
||||||
nodes: {
|
|
||||||
translation_map: ts.Expression;
|
|
||||||
translation_map_init: ts.Expression;
|
|
||||||
};
|
|
||||||
|
|
||||||
name_generator: (config: Configuration, node: ts.Node, message: string) => string;
|
|
||||||
translations: TranslationEntry[];
|
|
||||||
}
|
|
|
@ -1,9 +1,12 @@
|
||||||
import * as ts from "typescript";
|
import * as ts from "typescript";
|
||||||
import * as generator from "./generator";
|
import * as ts_generator from "./ts_generator";
|
||||||
|
import * as jsrender_generator from "./jsrender_generator";
|
||||||
import {readFileSync, writeFileSync} from "fs";
|
import {readFileSync, writeFileSync} from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as glob from "glob";
|
import * as glob from "glob";
|
||||||
import {isArray} from "util";
|
import {isArray} from "util";
|
||||||
|
import * as mkdirp from "mkdirp";
|
||||||
|
import {TranslationEntry} from "./generator";
|
||||||
|
|
||||||
console.log("TR GEN!");
|
console.log("TR GEN!");
|
||||||
|
|
||||||
|
@ -65,7 +68,7 @@ if(config.verbose) {
|
||||||
console.log("Input files:");
|
console.log("Input files:");
|
||||||
for(const file of config.source_files)
|
for(const file of config.source_files)
|
||||||
console.log(" - " + file);
|
console.log(" - " + file);
|
||||||
console.log("Target file: " + config.target_file;
|
console.log("Target file: " + config.target_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
const negate_files: string[] = [].concat.apply([], (config.excluded_files || []).map(file => glob.sync(config.base_bath + file))).map(file => path.normalize(file));
|
const negate_files: string[] = [].concat.apply([], (config.excluded_files || []).map(file => glob.sync(config.base_bath + file))).map(file => path.normalize(file));
|
||||||
|
@ -94,9 +97,10 @@ function print(nodes: ts.Node[] | ts.SourceFile) : string {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const translations: TranslationEntry[] = [];
|
||||||
config.source_files.forEach(file => {
|
config.source_files.forEach(file => {
|
||||||
if(config.verbose)
|
if(config.verbose)
|
||||||
console.log("iterating over %s", file)
|
console.log("iterating over %s (%s)", file, path.resolve(path.normalize(config.base_bath + file)));
|
||||||
glob.sync(config.base_bath + file).forEach(_file => {
|
glob.sync(config.base_bath + file).forEach(_file => {
|
||||||
_file = path.normalize(_file);
|
_file = path.normalize(_file);
|
||||||
for(const n_file of negate_files) {
|
for(const n_file of negate_files) {
|
||||||
|
@ -106,6 +110,8 @@ config.source_files.forEach(file => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const file_type = path.extname(_file);
|
||||||
|
if(file_type == ".ts") {
|
||||||
let source = ts.createSourceFile(
|
let source = ts.createSourceFile(
|
||||||
_file,
|
_file,
|
||||||
readFileSync(_file).toString(),
|
readFileSync(_file).toString(),
|
||||||
|
@ -116,27 +122,40 @@ config.source_files.forEach(file => {
|
||||||
|
|
||||||
console.log("Compile " + _file);
|
console.log("Compile " + _file);
|
||||||
|
|
||||||
const messages = generator.generate(source, {
|
const messages = ts_generator.generate(source, {});
|
||||||
replace_cache: true
|
translations.push(...messages);
|
||||||
});
|
|
||||||
|
/*
|
||||||
messages.forEach(message => {
|
messages.forEach(message => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("PRINT!");
|
console.log("PRINT!");
|
||||||
console.log(print(source));
|
console.log(print(source));
|
||||||
|
*/
|
||||||
|
} else if(file_type == ".html") {
|
||||||
|
const messages = jsrender_generator.generate({}, {
|
||||||
|
content: readFileSync(_file).toString(),
|
||||||
|
name: _file
|
||||||
|
});
|
||||||
|
translations.push(...messages);
|
||||||
/*
|
/*
|
||||||
result += "\n\n" + decl.print(decl.generate(source, {
|
messages.forEach(message => {
|
||||||
remove_private: false
|
console.log(message);
|
||||||
}));
|
});
|
||||||
*/
|
*/
|
||||||
|
} else {
|
||||||
|
console.log("Unknown file type \"%s\". Skipping file %s", file_type, _file);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
if(config.target_file) {
|
||||||
mkdirp(path.normalize(path.dirname(base_path + "/" + target_file)), error => {
|
mkdirp(path.normalize(path.dirname(config.base_bath + config.target_file)), error => {
|
||||||
if(error)
|
if(error)
|
||||||
throw error;
|
throw error;
|
||||||
writeFileSync(base_path + "/" + target_file, result);
|
writeFileSync(config.base_bath + config.target_file, JSON.stringify(translations, null, 2));
|
||||||
});
|
});
|
||||||
*/
|
} else {
|
||||||
|
console.log(JSON.stringify(translations, null, 2));
|
||||||
|
}
|
53
build/trgen/jsrender_generator.ts
Normal file
53
build/trgen/jsrender_generator.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import {TranslationEntry} from "./generator";
|
||||||
|
|
||||||
|
export interface Configuration {
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface File {
|
||||||
|
content: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Well my IDE hates me and does not support groups. By default with ES6 groups are supported... nvm */
|
||||||
|
//const regex = /{{ *tr *(?<message_expression>(("([^"]|\\")*")|('([^']|\\')*')|(`([^`]|\\`)+`)|( *\+ *)?)+) *\/ *}}/;
|
||||||
|
const regex = /{{ *tr *((("([^"]|\\")*")|('([^']|\\')*')|(`([^`]|\\`)+`)|([\n ]*\+[\n ]*)?)+) *\/ *}}/;
|
||||||
|
export function generate(config: Configuration, file: File) : TranslationEntry[] {
|
||||||
|
let result: TranslationEntry[] = [];
|
||||||
|
|
||||||
|
const lines = file.content.split('\n');
|
||||||
|
let match: RegExpExecArray;
|
||||||
|
let base_index = 0;
|
||||||
|
|
||||||
|
while(match = regex.exec(file.content.substr(base_index))) {
|
||||||
|
let expression = ((<any>match).groups || {})["message_expression"] || match[1];
|
||||||
|
//expression = expression.replace(/\n/g, "\\n");
|
||||||
|
|
||||||
|
let message;
|
||||||
|
try {
|
||||||
|
message = eval(expression);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to evaluate expression:\n%s", expression);
|
||||||
|
base_index += match.index + match[0].length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let character = base_index + match.index;
|
||||||
|
let line;
|
||||||
|
|
||||||
|
for(line = 0; line < lines.length; line++) {
|
||||||
|
const length = lines[line].length + 1;
|
||||||
|
if(length > character) break;
|
||||||
|
character -= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
filename: file.name,
|
||||||
|
character: character + 1,
|
||||||
|
line: line + 1,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
|
||||||
|
base_index += match.index + match[0].length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ debugger;
|
||||||
debugger;
|
debugger;
|
||||||
const zzz = true ? "yyy" : "bbb";
|
const zzz = true ? "yyy" : "bbb";
|
||||||
|
|
||||||
const y
|
const y = "";
|
||||||
debugger;
|
debugger;
|
||||||
debugger;
|
debugger;
|
||||||
debugger;
|
debugger;
|
||||||
|
|
66
build/trgen/test/test_02.html
Normal file
66
build/trgen/test/test_02.html
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script class="jsrender-template" id="tmpl_connect" type="text/html">
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<div style="display: flex; flex-direction: row; width: 100%; justify-content: space-between">
|
||||||
|
<div style="width: 68%; margin-bottom: 5px">
|
||||||
|
<div>{{tr "Remote address and port:" /}}</div>
|
||||||
|
<input type="text" style="width: 100%" class="connect_address" value="unknown">
|
||||||
|
</div>
|
||||||
|
<div style="width: 20%">
|
||||||
|
<div>{{tr "Server password:" /}}</div>
|
||||||
|
<form name="server_password_form" onsubmit="return false;">
|
||||||
|
<input type="password" id="connect_server_password_{{rnd '0~13377331'/}}" autocomplete="off" style="width: 100%" class="connect_password">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{{tr "Nickname:" /}}</div>
|
||||||
|
<input type="text" style="width: 100%" class="connect_nickname" value="">
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="group_box">
|
||||||
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
<div style="text-align: right;">{{tr "Identity Settings" /}}</div>
|
||||||
|
<select class="identity_select">
|
||||||
|
<option name="identity_type" value="TEAFORO">{{tr "Forum Account" /}}</option>
|
||||||
|
<option name="identity_type" value="TEAMSPEAK">{{tr "TeamSpeak" /}}</option>
|
||||||
|
<option name="identity_type" value="NICKNAME">{{tr "Nickname (Debug purposes only!)" /}}</option> <!-- Only available on localhost for debug -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="identity_config_TEAMSPEAK identity_config">
|
||||||
|
{{tr "Please enter your exported TS3 Identity string bellow or select your exported Identity"/}}<br>
|
||||||
|
<div style="width: 100%; display: flex; justify-content: stretch; flex-direction: row">
|
||||||
|
<input placeholder="Identity string" style="min-width: 60%; flex-shrink: 1; flex-grow: 2; margin: 5px;" class="identity_string">
|
||||||
|
<div style="max-width: 200px; flex-grow: 1; flex-shrink: 4; margin: 5px"><input style="display: flex; width: 100%;" class="identity_file" type="file"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="identity_config_TEAFORO identity_config">
|
||||||
|
<div class="connected">
|
||||||
|
{{tr "You're using your forum account as verification"/}}
|
||||||
|
</div>
|
||||||
|
<div class="disconnected">
|
||||||
|
<!-- TODO tr -->
|
||||||
|
You cant use your TeaSpeak forum account.<br>
|
||||||
|
You're not connected!<br>
|
||||||
|
Click {{if !client}}<a href="{{:forum_path}}login.php">here</a>{{else}}<a href="#" class="native-teaforo-login">here</a>{{/if}} to login via the TeaSpeak forum.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="identity_config_NICKNAME identity_config">
|
||||||
|
{{tr "This is just for debug and uses the name as unique identifier" /}}
|
||||||
|
{{tr "This is just for debug and uses the name as unique identifier" + "Hello World :D" /}}
|
||||||
|
{{tr "This is just for debug and uses the name as unique identifier" + `Hello World :D 2` /}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="background-color: red; border-radius: 1px; display: none" class="error_message"></div>
|
||||||
|
</div> <!-- <a href="<?php echo authPath() . "login.php"; ?>">Login</a> via the TeaSpeak forum. -->
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
297
build/trgen/ts_generator.ts
Normal file
297
build/trgen/ts_generator.ts
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
import * as ts from "typescript";
|
||||||
|
import * as sha256 from "sha256";
|
||||||
|
import {SyntaxKind} from "typescript";
|
||||||
|
import {TranslationEntry} from "./generator";
|
||||||
|
|
||||||
|
export function generate(file: ts.SourceFile, config: Configuration) : TranslationEntry[] {
|
||||||
|
let result: TranslationEntry[] = [];
|
||||||
|
|
||||||
|
file.forEachChild(n => _generate(config, n, result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function report(node: ts.Node, message: string) {
|
||||||
|
const sf = node.getSourceFile();
|
||||||
|
let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
||||||
|
console.log(`${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1}): ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _generate(config: Configuration, node: ts.Node, result: TranslationEntry[]) {
|
||||||
|
//console.log("Node: %s", SyntaxKind[node.kind]);
|
||||||
|
|
||||||
|
call_analize:
|
||||||
|
if(ts.isCallExpression(node)) {
|
||||||
|
const call = <ts.CallExpression>node;
|
||||||
|
const call_name = call.expression["escapedText"] as string;
|
||||||
|
if(call_name != "tr") break call_analize;
|
||||||
|
|
||||||
|
console.dir(call_name);
|
||||||
|
|
||||||
|
console.log("Parameters: %o", call.arguments.length);
|
||||||
|
if(call.arguments.length > 1) {
|
||||||
|
report(call, "Invalid argument count");
|
||||||
|
node.forEachChild(n => _generate(config, n, result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const object = <ts.StringLiteral>call.arguments[0];
|
||||||
|
if(object.kind != SyntaxKind.StringLiteral) {
|
||||||
|
report(call, "Invalid argument: " + SyntaxKind[object.kind]);
|
||||||
|
node.forEachChild(n => _generate(config, n, result));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Message: %o", object.text);
|
||||||
|
|
||||||
|
//FIXME
|
||||||
|
if(config.replace_cache) {
|
||||||
|
console.log("Update!");
|
||||||
|
ts.updateCall(call, call.expression, call.typeArguments, [ts.createLiteral("PENIS!")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
||||||
|
result.push({
|
||||||
|
filename: node.getSourceFile().fileName,
|
||||||
|
line: line,
|
||||||
|
character: character,
|
||||||
|
message: object.text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
node.forEachChild(n => _generate(config, n, result));
|
||||||
|
}
|
||||||
|
function create_unique_check(source_file: ts.SourceFile, variable: ts.Expression, variables: { name: string, node: ts.Node }[]) : ts.Node[] {
|
||||||
|
const nodes: ts.Node[] = [], blocked_nodes: ts.Statement[] = [];
|
||||||
|
|
||||||
|
const node_path = (node: ts.Node) => {
|
||||||
|
const sf = node.getSourceFile();
|
||||||
|
let { line, character } = sf ? sf.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
||||||
|
return `${(sf || {fileName: "unknown"}).fileName} (${line + 1},${character + 1})`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const create_error = (variable_name: ts.Expression, variable_path: ts.Expression, other_path: ts.Expression) => {
|
||||||
|
return [
|
||||||
|
ts.createLiteral("Translation with generated name \""),
|
||||||
|
variable_name,
|
||||||
|
ts.createLiteral("\" already exists!\nIt has been already defined here: "),
|
||||||
|
other_path,
|
||||||
|
ts.createLiteral("\nAttempted to redefine here: "),
|
||||||
|
variable_path,
|
||||||
|
ts.createLiteral("\nRegenerate and/or fix your program!")
|
||||||
|
].reduce((a, b) => ts.createBinary(a, SyntaxKind.PlusToken, b));
|
||||||
|
};
|
||||||
|
|
||||||
|
let declarations_file: ts.Expression;
|
||||||
|
const unique_check_label_name = "unique_translation_check";
|
||||||
|
|
||||||
|
/* initialization */
|
||||||
|
{
|
||||||
|
const declarations = ts.createElementAccess(variable, ts.createLiteral("declared"));
|
||||||
|
nodes.push(ts.createAssignment(declarations, ts.createBinary(declarations, SyntaxKind.BarBarToken, ts.createAssignment(declarations, ts.createObjectLiteral()))));
|
||||||
|
|
||||||
|
declarations_file = ts.createElementAccess(variable, ts.createLiteral("declared_files"));
|
||||||
|
nodes.push(ts.createAssignment(declarations_file, ts.createBinary(declarations_file, SyntaxKind.BarBarToken, ts.createAssignment(declarations_file, ts.createObjectLiteral()))));
|
||||||
|
|
||||||
|
variable = declarations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test file already loaded */
|
||||||
|
{
|
||||||
|
const unique_id = sha256(source_file.fileName + " | " + (Date.now() / 1000));
|
||||||
|
const property = ts.createElementAccess(declarations_file, ts.createLiteral(unique_id));
|
||||||
|
|
||||||
|
const if_condition = ts.createBinary(property, SyntaxKind.ExclamationEqualsEqualsToken, ts.createIdentifier("undefined"));
|
||||||
|
//
|
||||||
|
let if_then: ts.Block;
|
||||||
|
{
|
||||||
|
const elements: ts.Statement[] = [];
|
||||||
|
|
||||||
|
const console = ts.createIdentifier("console.warn");
|
||||||
|
elements.push(ts.createCall(console, [], [ts.createLiteral("This file has already been loaded!\nAre you executing scripts twice?") as any]) as any);
|
||||||
|
elements.push(ts.createBreak(unique_check_label_name));
|
||||||
|
|
||||||
|
if_then = ts.createBlock(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
const if_else = ts.createAssignment(property, ts.createLiteral(unique_id));
|
||||||
|
blocked_nodes.push(ts.createIf(if_condition, if_then, if_else as any));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test if variable has been defined somewhere else */
|
||||||
|
{
|
||||||
|
const for_variable_name = ts.createLoopVariable();
|
||||||
|
const for_variable_path = ts.createLoopVariable();
|
||||||
|
const for_declaration = ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createObjectBindingPattern([
|
||||||
|
ts.createBindingElement(undefined, "name", for_variable_name, undefined),
|
||||||
|
ts.createBindingElement(undefined, "path", for_variable_path, undefined)])
|
||||||
|
, undefined, undefined)]);
|
||||||
|
|
||||||
|
let for_block: ts.Statement;
|
||||||
|
{ //Create the for block
|
||||||
|
const elements: ts.Statement[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
const property = ts.createElementAccess(variable, for_variable_name);
|
||||||
|
const if_condition = ts.createBinary(property, SyntaxKind.ExclamationEqualsEqualsToken, ts.createIdentifier("undefined"));
|
||||||
|
|
||||||
|
//
|
||||||
|
const if_then = ts.createThrow(create_error(for_variable_name, for_variable_path, property));
|
||||||
|
const if_else = ts.createAssignment(property, for_variable_path);
|
||||||
|
const if_valid = ts.createIf(if_condition, if_then, if_else as any);
|
||||||
|
|
||||||
|
elements.push(if_valid);
|
||||||
|
|
||||||
|
for_block = ts.createBlock(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = ts.createForOf(undefined,
|
||||||
|
for_declaration, ts.createArrayLiteral(
|
||||||
|
[...variables.map(e => ts.createObjectLiteral([
|
||||||
|
ts.createPropertyAssignment("name", ts.createLiteral(e.name)),
|
||||||
|
ts.createPropertyAssignment("path", ts.createLiteral(node_path(e.node)))
|
||||||
|
]))
|
||||||
|
])
|
||||||
|
, for_block);
|
||||||
|
block = ts.addSyntheticLeadingComment(block, SyntaxKind.MultiLineCommentTrivia, "Auto generated helper for testing if the translation keys are unique", true);
|
||||||
|
blocked_nodes.push(block);
|
||||||
|
}
|
||||||
|
return [...nodes, ts.createLabel(unique_check_label_name, ts.createBlock(blocked_nodes))];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transform(config: Configuration, context: ts.TransformationContext, node: ts.SourceFile) : TransformResult {
|
||||||
|
const cache: VolatileTransformConfig = {} as any;
|
||||||
|
cache.translations = [];
|
||||||
|
|
||||||
|
//Initialize nodes
|
||||||
|
const extra_nodes: ts.Node[] = [];
|
||||||
|
{
|
||||||
|
cache.nodes = {} as any;
|
||||||
|
if(config.use_window) {
|
||||||
|
const window = ts.createIdentifier("window");
|
||||||
|
let translation_map = ts.createPropertyAccess(window, ts.createIdentifier("_translations"));
|
||||||
|
const new_translations = ts.createAssignment(translation_map, ts.createObjectLiteral());
|
||||||
|
|
||||||
|
let translation_map_init: ts.Expression = ts.createBinary(translation_map, ts.SyntaxKind.BarBarToken, new_translations);
|
||||||
|
translation_map_init = ts.createParen(translation_map_init);
|
||||||
|
|
||||||
|
cache.nodes = {
|
||||||
|
translation_map: translation_map,
|
||||||
|
translation_map_init: translation_map_init
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const variable_name = "_translations";
|
||||||
|
const variable_map = ts.createIdentifier(variable_name);
|
||||||
|
|
||||||
|
const inline_if = ts.createBinary(ts.createBinary(ts.createTypeOf(variable_map), SyntaxKind.ExclamationEqualsEqualsToken, ts.createLiteral("undefined")), ts.SyntaxKind.BarBarToken, ts.createAssignment(variable_map, ts.createObjectLiteral()));
|
||||||
|
|
||||||
|
cache.nodes = {
|
||||||
|
translation_map: variable_map,
|
||||||
|
translation_map_init: variable_map
|
||||||
|
};
|
||||||
|
|
||||||
|
//ts.createVariableDeclarationList([ts.createVariableDeclaration(variable_name)], ts.NodeFlags.Let)
|
||||||
|
extra_nodes.push(inline_if);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generated_names: { name: string, node: ts.Node }[] = [];
|
||||||
|
cache.name_generator = (config, node, message) => {
|
||||||
|
const characters = "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
let name = "";
|
||||||
|
while(name.length < 8) {
|
||||||
|
const char = characters[Math.floor(Math.random() * characters.length)];
|
||||||
|
name = name + char;
|
||||||
|
if(name[0] >= '0' && name[0] <= '9')
|
||||||
|
name = name.substr(1) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME
|
||||||
|
//if(generated_names.indexOf(name) != -1)
|
||||||
|
// return cache.name_generator(config, node, message);
|
||||||
|
|
||||||
|
generated_names.push({name: name, node: node});
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
|
function visit(node: ts.Node): ts.Node {
|
||||||
|
node = ts.visitEachChild(node, visit, context);
|
||||||
|
return replace_processor(config, cache, node);
|
||||||
|
}
|
||||||
|
node = ts.visitNode(node, visit);
|
||||||
|
extra_nodes.push(...create_unique_check(node, cache.nodes.translation_map_init, generated_names));
|
||||||
|
|
||||||
|
node = ts.updateSourceFileNode(node, [...(extra_nodes as any[]), ...node.statements], node.isDeclarationFile, node.referencedFiles, node.typeReferenceDirectives, node.hasNoDefaultLib, node.referencedFiles);
|
||||||
|
|
||||||
|
const result: TransformResult = {} as any;
|
||||||
|
result.node = node;
|
||||||
|
result.translations = cache.translations;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replace_processor(config: Configuration, cache: VolatileTransformConfig, node: ts.Node) : ts.Node {
|
||||||
|
if(config.verbose)
|
||||||
|
console.log("Process %s", SyntaxKind[node.kind]);
|
||||||
|
if(ts.isCallExpression(node)) {
|
||||||
|
const call = <ts.CallExpression>node;
|
||||||
|
const call_name = call.expression["escapedText"] as string;
|
||||||
|
if(call_name != "tr") return node;
|
||||||
|
|
||||||
|
if(config.verbose) {
|
||||||
|
console.dir(call_name);
|
||||||
|
console.log("Parameters: %o", call.arguments.length);
|
||||||
|
}
|
||||||
|
if(call.arguments.length > 1) {
|
||||||
|
report(call, "Invalid argument count");
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const object = <ts.StringLiteral>call.arguments[0];
|
||||||
|
if(object.kind != SyntaxKind.StringLiteral) {
|
||||||
|
report(call, "Invalid argument: " + SyntaxKind[object.kind]);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.verbose)
|
||||||
|
console.log("Message: %o", object.text || object.getText());
|
||||||
|
|
||||||
|
const variable_name = ts.createIdentifier(cache.name_generator(config, node, object.text || object.getText()));
|
||||||
|
const variable_init = ts.createPropertyAccess(cache.nodes.translation_map_init, variable_name);
|
||||||
|
|
||||||
|
const variable = ts.createPropertyAccess(cache.nodes.translation_map, variable_name);
|
||||||
|
const new_variable = ts.createAssignment(variable, call);
|
||||||
|
|
||||||
|
const source_file = node.getSourceFile();
|
||||||
|
let { line, character } = source_file ? source_file.getLineAndCharacterOfPosition(node.getStart()) : {line: -1, character: -1};
|
||||||
|
|
||||||
|
cache.translations.push({
|
||||||
|
message: object.text || object.getText(),
|
||||||
|
line: line,
|
||||||
|
character: character,
|
||||||
|
filename: (source_file || {fileName: "unknown"}).fileName
|
||||||
|
});
|
||||||
|
|
||||||
|
return ts.createBinary(variable_init, ts.SyntaxKind.BarBarToken, new_variable);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
export interface Configuration {
|
||||||
|
use_window?: boolean;
|
||||||
|
replace_cache?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransformResult {
|
||||||
|
node: ts.SourceFile;
|
||||||
|
translations: TranslationEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VolatileTransformConfig {
|
||||||
|
nodes: {
|
||||||
|
translation_map: ts.Expression;
|
||||||
|
translation_map_init: ts.Expression;
|
||||||
|
};
|
||||||
|
|
||||||
|
name_generator: (config: Configuration, node: ts.Node, message: string) => string;
|
||||||
|
translations: TranslationEntry[];
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
import * as ts from "typescript";
|
import * as ts from "typescript";
|
||||||
import * as generator from "./generator";
|
import * as ts_generator from "./ts_generator";
|
||||||
|
import * as path from "path";
|
||||||
|
import * as mkdirp from "mkdirp";
|
||||||
|
|
||||||
import {PluginConfig} from "ttypescript/lib/PluginCreator";
|
import {PluginConfig} from "ttypescript/lib/PluginCreator";
|
||||||
import {writeFileSync} from "fs";
|
import {writeFileSync} from "fs";
|
||||||
|
import {TranslationEntry} from "./generator";
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
target_file?: string;
|
target_file?: string;
|
||||||
|
@ -13,25 +17,30 @@ let process_config: Config;
|
||||||
export default function(program: ts.Program, config?: PluginConfig) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
export default function(program: ts.Program, config?: PluginConfig) : (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||||
process_config = config as any || {};
|
process_config = config as any || {};
|
||||||
|
|
||||||
if(process_config.verbose)
|
const base_path = path.dirname(program.getCompilerOptions().project || program.getCurrentDirectory());
|
||||||
|
if(process_config.verbose) {
|
||||||
console.log("TRGen transformer called");
|
console.log("TRGen transformer called");
|
||||||
|
console.log("Base path: %s", base_path);
|
||||||
|
}
|
||||||
|
|
||||||
process.on('exit', function () {
|
process.on('exit', function () {
|
||||||
|
const target = path.isAbsolute(process_config.target_file) ? process_config.target_file : path.join(base_path, process_config.target_file);
|
||||||
if(process_config.target_file) {
|
if(process_config.target_file) {
|
||||||
if(process_config.verbose)
|
if(process_config.verbose)
|
||||||
console.log("Writing translation file to " + process_config.target_file);
|
console.log("Writing translation file to " + target);
|
||||||
|
|
||||||
writeFileSync(process_config.target_file, JSON.stringify(translations, null, 2));
|
mkdirp.sync(path.dirname(target));
|
||||||
|
writeFileSync(target, JSON.stringify(translations, null, 2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return ctx => transformer(ctx);
|
return ctx => transformer(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: generator.TranslationEntry[] = [];
|
const translations: TranslationEntry[] = [];
|
||||||
const transformer = (context: ts.TransformationContext) => (rootNode: ts.SourceFile) => {
|
const transformer = (context: ts.TransformationContext) => (rootNode: ts.SourceFile) => {
|
||||||
console.log("Processing " + rootNode.fileName);
|
console.log("Processing " + rootNode.fileName);
|
||||||
const result = generator.transform({
|
const result = ts_generator.transform({
|
||||||
use_window: false,
|
use_window: false,
|
||||||
replace_cache: true
|
replace_cache: true
|
||||||
}, context, rootNode);
|
}, context, rootNode);
|
||||||
|
|
|
@ -75,6 +75,14 @@
|
||||||
"path" => "wasm/",
|
"path" => "wasm/",
|
||||||
"local-path" => "./asm/generated/"
|
"local-path" => "./asm/generated/"
|
||||||
],
|
],
|
||||||
|
[ /* translations */
|
||||||
|
"type" => "i18n",
|
||||||
|
"search-pattern" => "/.*\.(translation|json)/",
|
||||||
|
"build-target" => "dev|rel",
|
||||||
|
|
||||||
|
"path" => "i18n/",
|
||||||
|
"local-path" => "./shared/i18n/"
|
||||||
|
],
|
||||||
|
|
||||||
/* vendors */
|
/* vendors */
|
||||||
[
|
[
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"compile-sass": "sass --update .:.",
|
"compile-sass": "sass --update .:.",
|
||||||
"build-worker": "tsc -p shared/js/workers/tsconfig_worker_codec.json",
|
"build-worker": "tsc -p shared/js/workers/tsconfig_worker_codec.json",
|
||||||
"dtsgen": "node build/dtsgen/index.js",
|
"dtsgen": "node build/dtsgen/index.js",
|
||||||
"trgen": "node build/trgen/index.js"
|
"trgen": "node build/trgen/index.js",
|
||||||
|
"ttsc": "ttsc"
|
||||||
},
|
},
|
||||||
"author": "TeaSpeak (WolverinDEV)",
|
"author": "TeaSpeak (WolverinDEV)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|
123
shared/generate_i18n_gtranslate.py
Normal file
123
shared/generate_i18n_gtranslate.py
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
"""
|
||||||
|
We want python 2.7 again...
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
"""
|
||||||
|
from googletrans import Translator # Use the free webhook
|
||||||
|
def run_translate(messages, source_language, target_language):
|
||||||
|
translator = Translator()
|
||||||
|
_translations = translator.translate(messages, src=source_language, dest=target_language)
|
||||||
|
result = []
|
||||||
|
for translation in _translations:
|
||||||
|
result.append({
|
||||||
|
"source": translation.origin,
|
||||||
|
"translated": translation.text
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from google.cloud import translate # Use googles could solution
|
||||||
|
def run_translate(messages, source_language, target_language):
|
||||||
|
translate_client = translate.Client()
|
||||||
|
|
||||||
|
# The text to translate
|
||||||
|
text = u'Hello, world!'
|
||||||
|
# The target language
|
||||||
|
|
||||||
|
result = []
|
||||||
|
limit = 16
|
||||||
|
for chunk in [messages[i:i + limit] for i in xrange(0, len(messages), limit)]:
|
||||||
|
# Translates some text into Russian
|
||||||
|
print("Requesting {} translations".format(len(chunk)))
|
||||||
|
translations = translate_client.translate(chunk, target_language=target_language)
|
||||||
|
|
||||||
|
for translation in translations:
|
||||||
|
result.append({
|
||||||
|
"source": translation["input"],
|
||||||
|
"translated": translation["translatedText"]
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def translate_messages(source, destination, target_language):
|
||||||
|
with open(source) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
try:
|
||||||
|
with open(destination) as f:
|
||||||
|
result = json.load(f)
|
||||||
|
print("loaded old result")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
translations = result["translations"]
|
||||||
|
if translations is None:
|
||||||
|
print("Using new translation map")
|
||||||
|
translations = []
|
||||||
|
else:
|
||||||
|
print("Loaded {} old translations".format(len(translations)))
|
||||||
|
# TODO translate
|
||||||
|
|
||||||
|
messages = []
|
||||||
|
for message in data:
|
||||||
|
try:
|
||||||
|
messages.index(message["message"])
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
found = False
|
||||||
|
for entry in translations:
|
||||||
|
if entry["key"]["message"] == message["message"]:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise Exception('add message for translate')
|
||||||
|
except:
|
||||||
|
messages.append(message["message"])
|
||||||
|
|
||||||
|
print("Translating {} messages".format(len(messages)))
|
||||||
|
if len(messages) != 0:
|
||||||
|
_translations = run_translate(messages, 'en', target_language)
|
||||||
|
print("Messages translated, generating target file")
|
||||||
|
|
||||||
|
for translation in _translations:
|
||||||
|
translations.append({
|
||||||
|
"key": {
|
||||||
|
"message": translation["source"]
|
||||||
|
},
|
||||||
|
"translated": translation["translated"],
|
||||||
|
"flags": [
|
||||||
|
"google-translate"
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
print("Writing target file")
|
||||||
|
result["translations"] = translations
|
||||||
|
if result["info"] is None:
|
||||||
|
result["info"] = {
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "Google Translate, via script by Markus Hadenfeldt",
|
||||||
|
"email": "gtr.i18n.client@teaspeak.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Auto translated messages for language " + target_language
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(destination, 'w') as f:
|
||||||
|
f.write(json.dumps(result, indent=2))
|
||||||
|
print("Done")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
#print(run_translate(["Hello World", "Bla bla bla", "Im a unicorn"], "en", "de"))
|
||||||
|
translate_messages("generated/messages_script.json", "test.json", "de")
|
||||||
|
translate_messages("generated/messages_template.json", "test.json", "de")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
17
shared/generate_translations.sh
Executable file
17
shared/generate_translations.sh
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BASEDIR=$(dirname "$0")
|
||||||
|
cd "$BASEDIR"
|
||||||
|
|
||||||
|
#Generate the script translations
|
||||||
|
npm run ttsc -- -p $(pwd)/tsconfig/tsconfig.json
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to generate translation file for the script files"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm run trgen -- -f $(pwd)/html/templates.html -d $(pwd)/generated/messages_template.json
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to generate translations file for the template files"
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -1,6 +1,7 @@
|
||||||
"""
|
"""
|
||||||
This should be executed as python 2.7 (because of pydub)
|
This should be executed with python 2.7 (because of pydub)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
@ -25,7 +26,7 @@ def tts(text, file):
|
||||||
'Chrome/69.0.3497.100 Safari/537.36 OPR/56.0.3051.52',
|
'Chrome/69.0.3497.100 Safari/537.36 OPR/56.0.3051.52',
|
||||||
'content-type': 'application/x-www-form-urlencoded',
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
'referer': 'https://www.naturalreaders.com/online/',
|
'referer': 'https://www.naturalreaders.com/online/',
|
||||||
'authority': 'kfiuqykx63.execute-api.us-east-1.amazonaws.com'
|
'authority': 'kfiuqykx63.execute-api.us-east-1.amazonaws.com' #You may need to change that here
|
||||||
},
|
},
|
||||||
data=json.dumps({"t": text})
|
data=json.dumps({"t": text})
|
||||||
)
|
)
|
||||||
|
|
|
@ -990,8 +990,8 @@
|
||||||
<a>{{tr "Nickname"/}}</a>
|
<a>{{tr "Nickname"/}}</a>
|
||||||
<div class="help-tip tip-right tip-small" checked>
|
<div class="help-tip tip-right tip-small" checked>
|
||||||
<p>
|
<p>
|
||||||
{{tr "Bans the client by his current nickname.<br>
|
{{tr "Bans the client by his current nickname.<br>" +
|
||||||
The currently nickname cant be used until the ban expired"/}}
|
"The currently nickname cant be used until the ban expired"/}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1000,11 +1000,11 @@
|
||||||
<a>{{tr "Hardware ID" /}}</a>
|
<a>{{tr "Hardware ID" /}}</a>
|
||||||
<div class="help-tip tip-right tip-small">
|
<div class="help-tip tip-right tip-small">
|
||||||
<p>
|
<p>
|
||||||
{{tr "Bans the client by his hardware id.<br>
|
{{tr "Bans the client by his hardware id.<br>" +
|
||||||
The hardware id has different meanings, depends on the users agent<br>
|
"The hardware id has different meanings, depends on the users agent<br>" +
|
||||||
TeaClient: The hardware id will be equal to the mac address<br>
|
"TeaClient: The hardware id will be equal to the mac address<br>" +
|
||||||
TeaWeb: The TeaSpeak web client hasn't a hardware id, it will be random<br>
|
"TeaWeb: The TeaSpeak web client hasn't a hardware id, it will be random<br>" +
|
||||||
TeamSpeak 3 client: The hardware id will be a result of some hashes from hardware specific properties" /}}
|
"TeamSpeak 3 client: The hardware id will be a result of some hashes from hardware specific properties" /}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1087,10 +1087,10 @@
|
||||||
<a>{{tr "Use this ban as a global ban" /}}</a>
|
<a>{{tr "Use this ban as a global ban" /}}</a>
|
||||||
<div class="help-tip tip-center tip-small">
|
<div class="help-tip tip-center tip-small">
|
||||||
<p>
|
<p>
|
||||||
{{tr "Global bans are bans which apply instance wide.<br>
|
{{tr "Global bans are bans which apply instance wide.<br>" +
|
||||||
This means that (if this rule apply to a victim) cant join <b>any</b> virtual server!<br>
|
"This means that (if this rule apply to a victim) cant join <b>any</b> virtual server!<br>" +
|
||||||
Global bans are by default shown to every server admin group,<br>
|
"Global bans are by default shown to every server admin group,<br>" +
|
||||||
but could only be created with query rights"/}}
|
"but could only be created with query rights"/}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
32
shared/i18n/de_DE.translation
Normal file
32
shared/i18n/de_DE.translation
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "Markus Hadenfeldt",
|
||||||
|
"email": "i18n.client@teaspeak.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "German translations"
|
||||||
|
},
|
||||||
|
"translations": [
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"message": "Show permission description",
|
||||||
|
"line": 374,
|
||||||
|
"character": 30,
|
||||||
|
"filename": "/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/shared/js/ui/modal/ModalPermissionEdit.ts"
|
||||||
|
},
|
||||||
|
"translated": "Berechtigungsbeschreibung anzeigen",
|
||||||
|
"flags": [
|
||||||
|
"google-translate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"message": "Create a new connection"
|
||||||
|
},
|
||||||
|
"translated": "Verbinden",
|
||||||
|
"flags": [ ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
8
shared/i18n/info.json
Normal file
8
shared/i18n/info.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"translations": [
|
||||||
|
{
|
||||||
|
"key": "de_DE",
|
||||||
|
"path": "de_DE.translation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
27
shared/i18n/template.translation
Normal file
27
shared/i18n/template.translation
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"contributors": [
|
||||||
|
/* add yourself if you have done anything :) */
|
||||||
|
{
|
||||||
|
"name": "Markus Hadenfeldt", /* this field is required */
|
||||||
|
"email": "i18n.client@teaspeak.de" /* this field is required */
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "A template translation file" /* this field is required */
|
||||||
|
},
|
||||||
|
"translations": [ /* Array with all translation objects */
|
||||||
|
{ /* translation object */
|
||||||
|
"key": { /* the key */
|
||||||
|
"message": "Show permission description", /* necessary to identify the message */
|
||||||
|
"line": 374, /* optional, only for specify the translation for a specific case (Not supported yet!) */
|
||||||
|
"character": 30, /* optional, only for specify the translation for a specific case (Not supported yet!) */
|
||||||
|
"filename": "/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/shared/js/ui/modal/ModalPermissionEdit.ts" /* optional, only for specify the translation for a specific case (Not supported yet!) */
|
||||||
|
},
|
||||||
|
"translated": "Berechtigungsbeschreibung anzeigen", /* The actual translation */
|
||||||
|
"flags": [ /* some flags for this translation */
|
||||||
|
"google-translate", /* this translation has been made with google translator */
|
||||||
|
"verified" /* this translation has been verified by a native speaker */
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5413
shared/i18n/test.json
Normal file
5413
shared/i18n/test.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,124 @@
|
||||||
|
/*
|
||||||
|
"key": {
|
||||||
|
"message": "Show permission description",
|
||||||
|
"line": 374,
|
||||||
|
"character": 30,
|
||||||
|
"filename": "/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/shared/js/ui/modal/ModalPermissionEdit.ts"
|
||||||
|
},
|
||||||
|
"translated": "Berechtigungsbeschreibung anzeigen",
|
||||||
|
"flags": [
|
||||||
|
"google-translate",
|
||||||
|
"verified"
|
||||||
|
]
|
||||||
|
*/
|
||||||
namespace i18n {
|
namespace i18n {
|
||||||
|
interface TranslationKey {
|
||||||
|
message: string;
|
||||||
|
line?: number;
|
||||||
|
character?: number;
|
||||||
|
filename?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Translation {
|
||||||
|
key: TranslationKey;
|
||||||
|
translated: string;
|
||||||
|
flags?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Contributor {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileInfo {
|
||||||
|
name: string;
|
||||||
|
contributors: Contributor[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TranslationFile {
|
||||||
|
info: FileInfo;
|
||||||
|
translations: Translation[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let translations: Translation[] = [];
|
||||||
|
let fast_translate: { [key:string]:string; } = {};
|
||||||
export function tr(message: string, key?: string) {
|
export function tr(message: string, key?: string) {
|
||||||
|
const sloppy = fast_translate[message];
|
||||||
|
if(sloppy) return sloppy;
|
||||||
|
|
||||||
console.log("Translating \"%s\". Default: \"%s\"", key, message);
|
console.log("Translating \"%s\". Default: \"%s\"", key, message);
|
||||||
|
|
||||||
return message;
|
let translated = message;
|
||||||
|
for(const translation of translations) {
|
||||||
|
if(translation.key.message == message) {
|
||||||
|
translated = translation.translated;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_translate[message] = translated;
|
||||||
|
return translated;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function load_file(url: string) : Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
async: true,
|
||||||
|
success: result => {
|
||||||
|
console.dir(result);
|
||||||
|
const file = (typeof(result) === "string" ? JSON.parse(result) : result) as TranslationFile;
|
||||||
|
if(!file) {
|
||||||
|
reject("Invalid json");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO validate file
|
||||||
|
translations = file.translations;
|
||||||
|
log.info(LogCategory.I18N, tr("Successfully initialized up translation file from %s"), url);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
error: (xhr, error) => {
|
||||||
|
log.warn(LogCategory.I18N, "Failed to load translation file from \"%s\". Error: %o", url, error);
|
||||||
|
reject("Failed to load file: " + error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initialize() {
|
||||||
|
// await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/de_DE.translation");
|
||||||
|
await load_file("http://localhost/home/TeaSpeak/TeaSpeak/Web-Client/web/environment/development/i18n/test.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tr: typeof i18n.tr = i18n.tr;
|
const tr: typeof i18n.tr = i18n.tr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "Markus Hadenfeldt",
|
||||||
|
"email": "i18n.client@teaspeak.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "German translations"
|
||||||
|
},
|
||||||
|
"translations": [
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"message": "Show permission description",
|
||||||
|
"line": 374,
|
||||||
|
"character": 30,
|
||||||
|
"filename": "/home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/shared/js/ui/modal/ModalPermissionEdit.ts"
|
||||||
|
},
|
||||||
|
"translated": "Berechtigungsbeschreibung anzeigen",
|
||||||
|
"flags": [
|
||||||
|
"google-translate",
|
||||||
|
"verified"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
|
@ -5,7 +5,8 @@ enum LogCategory {
|
||||||
PERMISSIONS,
|
PERMISSIONS,
|
||||||
GENERAL,
|
GENERAL,
|
||||||
NETWORKING,
|
NETWORKING,
|
||||||
VOICE
|
VOICE,
|
||||||
|
I18N
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace log {
|
namespace log {
|
||||||
|
@ -17,7 +18,6 @@ namespace log {
|
||||||
ERROR
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add translation
|
|
||||||
let category_mapping = new Map<number, string>([
|
let category_mapping = new Map<number, string>([
|
||||||
[LogCategory.CHANNEL, "Channel "],
|
[LogCategory.CHANNEL, "Channel "],
|
||||||
[LogCategory.CLIENT, "Client "],
|
[LogCategory.CLIENT, "Client "],
|
||||||
|
@ -25,7 +25,8 @@ namespace log {
|
||||||
[LogCategory.PERMISSIONS, "Permission "],
|
[LogCategory.PERMISSIONS, "Permission "],
|
||||||
[LogCategory.GENERAL, "General "],
|
[LogCategory.GENERAL, "General "],
|
||||||
[LogCategory.NETWORKING, "Network "],
|
[LogCategory.NETWORKING, "Network "],
|
||||||
[LogCategory.VOICE, "Voice "]
|
[LogCategory.VOICE, "Voice "],
|
||||||
|
[LogCategory.I18N, "I18N "]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function logDirect(type: LogType, message: string, ...optionalParams: any[]) {
|
function logDirect(type: LogType, message: string, ...optionalParams: any[]) {
|
||||||
|
|
|
@ -91,16 +91,34 @@ function setup_jsrender() : boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
async function initialize() {
|
||||||
if(!setup_jsrender()) return;
|
if(!setup_jsrender()) return;
|
||||||
|
try {
|
||||||
//http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1
|
await i18n.initialize();
|
||||||
AudioController.initializeAudioController();
|
} catch(error) {
|
||||||
if(!TSIdentityHelper.setup()) {
|
console.error(tr("Failed to initialized the translation system!\nError: %o"), error);
|
||||||
console.error(tr( "Could not setup the TeamSpeak identity parser!"));
|
displayCriticalError("Failed to setup the translation system");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioController.initializeAudioController();
|
||||||
|
if(!TSIdentityHelper.setup()) {
|
||||||
|
console.error(tr("Could not setup the TeamSpeak identity parser!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ppt.initialize();
|
||||||
|
} catch(error) {
|
||||||
|
console.error(tr("Failed to initialize ppt!\nError: %o"), error);
|
||||||
|
displayCriticalError(tr("Failed to initialize ppt!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
//http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1
|
||||||
|
|
||||||
settings = new Settings();
|
settings = new Settings();
|
||||||
globalClient = new TSClient();
|
globalClient = new TSClient();
|
||||||
/** Setup the XF forum identity **/
|
/** Setup the XF forum identity **/
|
||||||
|
@ -150,11 +168,6 @@ function main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ppt.initialize().catch(error => {
|
|
||||||
console.error(tr("Failed to initialize ppt!"));
|
|
||||||
//TODO error notification?
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let tag = $("#tmpl_music_frame").renderTag({
|
let tag = $("#tmpl_music_frame").renderTag({
|
||||||
//thumbnail: "img/loading_image.svg"
|
//thumbnail: "img/loading_image.svg"
|
||||||
|
@ -183,8 +196,9 @@ function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.loadedListener.push(() => {
|
app.loadedListener.push(async () => {
|
||||||
try {
|
try {
|
||||||
|
await initialize();
|
||||||
main();
|
main();
|
||||||
if(!audio.player.initialized()) {
|
if(!audio.player.initialized()) {
|
||||||
log.info(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
log.info(LogCategory.VOICE, tr("Initialize audio controller later!"));
|
||||||
|
|
5413
shared/test.json
Normal file
5413
shared/test.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@
|
||||||
{
|
{
|
||||||
"transform": "../../build/trgen/ttsc_transformer.js",
|
"transform": "../../build/trgen/ttsc_transformer.js",
|
||||||
"type": "program",
|
"type": "program",
|
||||||
"target_file": "../generated/i18n.translation",
|
"target_file": "../generated/messages_script.json",
|
||||||
"verbose": true
|
"verbose": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue