339 lines
10 KiB
TypeScript
339 lines
10 KiB
TypeScript
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
|
|
import axios from 'axios';
|
|
import dotenv from 'dotenv';
|
|
|
|
dotenv.config({ path: '.env' }).parsed;
|
|
|
|
const normalizeTranslation = (translation: string | null | undefined) => {
|
|
if (translation == null) {
|
|
return '';
|
|
}
|
|
return translation.replace(/\n/g, '\\n').replace(/"/g, "'");
|
|
};
|
|
|
|
function getSplitByIndexes(input: string, indexes: number[]): string[] {
|
|
const parts = input.split('_');
|
|
return indexes.map((index) => parts[index]).filter((item) => item !== undefined);
|
|
}
|
|
|
|
const prepareLuaWithFemaleVersion = (quest: Quest, type: keyof Quest, key: string) => {
|
|
let text = '';
|
|
const value = quest[type] as string;
|
|
const normalizedMaleValue = normalizeTranslation(value?.[0]);
|
|
const normalizedFemaleValue = normalizeTranslation(value?.[1]);
|
|
if (normalizedMaleValue != '') {
|
|
text += '\t' + key + 'Male = "' + normalizedMaleValue + '", \n';
|
|
}
|
|
if (normalizedFemaleValue != '') {
|
|
text += '\t' + key + 'Female = "' + normalizedFemaleValue + '", \n';
|
|
}
|
|
return text;
|
|
};
|
|
|
|
const callTolgee = async <R>(
|
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
|
|
path: string,
|
|
params?: any,
|
|
data?: any,
|
|
) => {
|
|
const args = process.argv.slice(2);
|
|
const projectId = args[1];
|
|
try {
|
|
return await axios<R>(`https://translate.romanjaros.local/v2/projects/${projectId}${path}`, {
|
|
method,
|
|
data,
|
|
params,
|
|
headers: {
|
|
'X-API-KEY': process.env.TOLGEE_PAT as string,
|
|
},
|
|
});
|
|
} catch (e) {
|
|
throw e;
|
|
}
|
|
};
|
|
|
|
function makeChunks(input: AddonData, chunkSize: number = 1000) {
|
|
const keys = Object.keys(input);
|
|
const result = [];
|
|
|
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
const chunk = keys.slice(i, i + chunkSize).reduce((acc: AddonData, key) => {
|
|
acc[key] = input[key];
|
|
return acc;
|
|
}, {});
|
|
result.push(chunk);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const splitFirst = (text: string, delimiter: string) => {
|
|
const index = text.indexOf(delimiter);
|
|
if (index === -1) return text;
|
|
return text.slice(index + delimiter.length);
|
|
};
|
|
|
|
const generateRoleTactic = (role: (string | null)[]) => {
|
|
let luaQuestRecord = '';
|
|
for (const ability of role) {
|
|
if (ability == null) continue;
|
|
const information = ability.split('\n');
|
|
luaQuestRecord += `\t\t{"${information[0].trim()}","${information[1].trim()}","${information[2].trim()}","${information[3].trim()}"},\n`;
|
|
}
|
|
return luaQuestRecord;
|
|
};
|
|
|
|
(async () => {
|
|
const args = process.argv.slice(2);
|
|
const addonDir = path.join(process.cwd(), `../Addon/Data/${args[0]}`);
|
|
|
|
const addonData: Record<string, Quest> = {};
|
|
let pageNumber = 0;
|
|
|
|
try {
|
|
// load all data from tolgee
|
|
while (true) {
|
|
const response = await callTolgee<TolgeeTranslationsResponse>('GET', `/translations`, {
|
|
filterTranslatedInLang: 'cs',
|
|
languages: 'cs,csf,en',
|
|
size: 2000,
|
|
page: pageNumber,
|
|
});
|
|
const translations = response.data?._embedded?.keys ?? [];
|
|
if (response.data?.page.totalPages > response.data?.page.number) {
|
|
pageNumber++;
|
|
|
|
// for each page from tolgee
|
|
for (const tolgeeKey of translations) {
|
|
let key = tolgeeKey.keyName;
|
|
|
|
if (tolgeeKey.keyNamespace === 'tactic') {
|
|
key = key.split('_')[0];
|
|
}
|
|
|
|
const instanceName = getSplitByIndexes(tolgeeKey.keyName, [1, 2]).join('_');
|
|
|
|
addonData[key] = {
|
|
...addonData[tolgeeKey.keyName],
|
|
...(tolgeeKey.keyNamespace === 'name' && {
|
|
names: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'objective' && {
|
|
objectives: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'description' && {
|
|
descriptions: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
tolgeeKey.translations.en.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'progress' && {
|
|
progresses: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'completion' && {
|
|
completions: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'speech' && {
|
|
speeches: [
|
|
tolgeeKey.translations.cs.text,
|
|
tolgeeKey.translations.cs.text === tolgeeKey.translations.csf.text
|
|
? null
|
|
: tolgeeKey.translations.csf.text,
|
|
tolgeeKey.translations.en.text,
|
|
],
|
|
}),
|
|
...(tolgeeKey.keyNamespace === 'tactic' && {
|
|
tactics: {
|
|
[instanceName]: {
|
|
...(addonData[key]?.tactics?.[instanceName] ?? {}),
|
|
...(tolgeeKey.keyName.endsWith('_summary') && {
|
|
summary: tolgeeKey.translations.cs.text,
|
|
}),
|
|
...(tolgeeKey.keyName.includes('_tank_') && {
|
|
tank: [...(addonData[key].tactics?.[instanceName]?.tank ?? []), tolgeeKey.translations.cs.text],
|
|
}),
|
|
...(tolgeeKey.keyName.includes('_healer_') && {
|
|
healer: [...(addonData[key].tactics?.[instanceName]?.healer ?? []), tolgeeKey.translations.cs.text],
|
|
}),
|
|
...(tolgeeKey.keyName.includes('_dps_') && {
|
|
dps: [...(addonData[key].tactics?.[instanceName]?.dps ?? []), tolgeeKey.translations.cs.text],
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
name: tolgeeKey.keyDescription,
|
|
id: tolgeeKey.keyName.replace('q', '').replace('i', ''),
|
|
isQuest: tolgeeKey.keyName.startsWith('q'),
|
|
isQuestItem: tolgeeKey.keyName.startsWith('i'),
|
|
};
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// split into chunks
|
|
const chunks = makeChunks(addonData);
|
|
|
|
// build addon file
|
|
chunks.forEach((chunk, index) => {
|
|
const fileName = path.join(addonDir, `${index++}.lua`);
|
|
try {
|
|
fs.unlinkSync(fileName);
|
|
} catch (e) {}
|
|
fs.appendFileSync(fileName, 'local _, addon = ...\n', 'utf8');
|
|
for (const [, czechQuest] of Object.entries(chunk)) {
|
|
// prepare variables
|
|
if (czechQuest.isQuest) {
|
|
let luaQuestRecord = '';
|
|
luaQuestRecord += `addon.data.quest[${czechQuest.id}] = {\n`;
|
|
luaQuestRecord += prepareLuaWithFemaleVersion(czechQuest, 'names', 'title');
|
|
luaQuestRecord += prepareLuaWithFemaleVersion(czechQuest, 'objectives', 'objective');
|
|
luaQuestRecord += prepareLuaWithFemaleVersion(czechQuest, 'descriptions', 'description');
|
|
luaQuestRecord += prepareLuaWithFemaleVersion(czechQuest, 'progresses', 'progress');
|
|
luaQuestRecord += prepareLuaWithFemaleVersion(czechQuest, 'completions', 'completion');
|
|
luaQuestRecord += `}\n`;
|
|
fs.appendFileSync(fileName, luaQuestRecord, 'utf8');
|
|
}
|
|
|
|
if (czechQuest.isQuestItem) {
|
|
const page = czechQuest.id.split('_page')?.[1] ?? null;
|
|
const variableId = `"${czechQuest.name}${page ? `__${page}` : ''}"`;
|
|
let luaQuestRecord = '';
|
|
luaQuestRecord += `addon.data.item[${variableId}] = {\n`;
|
|
luaQuestRecord += '\ttitle = "' + normalizeTranslation(czechQuest.name) + '", \n';
|
|
luaQuestRecord += '\ttext = "' + normalizeTranslation(czechQuest.descriptions?.[0]) + '", \n';
|
|
luaQuestRecord += `}\n`;
|
|
fs.appendFileSync(fileName, luaQuestRecord, 'utf8');
|
|
}
|
|
|
|
if (czechQuest.speeches) {
|
|
let luaQuestRecord = '';
|
|
const key = splitFirst(normalizeTranslation(czechQuest.speeches?.[2]) ?? '', ': ').trim();
|
|
const npcNameKey = normalizeTranslation(czechQuest.name).trim();
|
|
luaQuestRecord += `addon.data.speech["${npcNameKey}_${key}"] = {\n`;
|
|
luaQuestRecord += '\ttext = "' + normalizeTranslation(czechQuest.speeches?.[0]).trim() + '", \n';
|
|
luaQuestRecord += `}\n`;
|
|
fs.appendFileSync(fileName, luaQuestRecord, 'utf8');
|
|
}
|
|
|
|
if (czechQuest.tactics) {
|
|
let luaQuestRecord = '';
|
|
const npcNameKey = normalizeTranslation(czechQuest.name).trim();
|
|
luaQuestRecord += `addon.data.tactic["${npcNameKey}"] = {\n`;
|
|
for (const [instance, roles] of Object.entries(czechQuest.tactics)) {
|
|
luaQuestRecord += `\t${instance} = {\n`;
|
|
luaQuestRecord += `\t\tsummary = "${roles.summary}",\n`;
|
|
luaQuestRecord += `\t\ttank = {\n`;
|
|
if (roles.tank) luaQuestRecord += generateRoleTactic(roles.tank);
|
|
luaQuestRecord += `\t\t},\n`;
|
|
luaQuestRecord += `\t\theal = {\n`;
|
|
if (roles.healer) luaQuestRecord += generateRoleTactic(roles.healer);
|
|
luaQuestRecord += `\t\t},\n`;
|
|
luaQuestRecord += `\t\tdps = {\n`;
|
|
if (roles.dps) luaQuestRecord += generateRoleTactic(roles.dps);
|
|
luaQuestRecord += `\t\t},\n`;
|
|
luaQuestRecord += `\t},\n`;
|
|
}
|
|
luaQuestRecord += `}\n`;
|
|
fs.appendFileSync(fileName, luaQuestRecord, 'utf8');
|
|
}
|
|
}
|
|
});
|
|
} catch (e) {
|
|
throw e;
|
|
}
|
|
})();
|
|
|
|
type AddonData = Record<string, Quest>;
|
|
|
|
type Tactic = {
|
|
summary?: string | null;
|
|
tank?: (string | null)[];
|
|
healer?: (string | null)[];
|
|
dps?: (string | null)[];
|
|
};
|
|
|
|
type InstanceTactic = {
|
|
[key: string]: Tactic;
|
|
};
|
|
|
|
type Quest = {
|
|
id: string;
|
|
name: string;
|
|
names?: (string | null)[];
|
|
descriptions?: (string | null)[];
|
|
progresses?: (string | null)[];
|
|
completions?: (string | null)[];
|
|
objectives?: (string | null)[];
|
|
speeches?: (string | null)[];
|
|
tactics?: InstanceTactic;
|
|
isQuest: boolean;
|
|
isQuestItem: boolean;
|
|
};
|
|
|
|
type TolgeeKeysData = {
|
|
keyId: number;
|
|
keyName: string;
|
|
keyNamespace: string;
|
|
keyDescription: string;
|
|
keyTags: TolgeeTag[];
|
|
translations: {
|
|
en: {
|
|
id: number;
|
|
text: string;
|
|
state: 'REVIEWED' | 'TRANSLATED';
|
|
};
|
|
cs: {
|
|
id: number;
|
|
text: string | null;
|
|
state: 'REVIEWED' | 'TRANSLATED';
|
|
};
|
|
csf: {
|
|
id: number;
|
|
text: string | null;
|
|
state: 'REVIEWED' | 'TRANSLATED';
|
|
};
|
|
};
|
|
};
|
|
|
|
type TolgeeTag = {
|
|
name: string;
|
|
};
|
|
|
|
type TolgeeTranslationsResponse = {
|
|
_embedded: {
|
|
keys: TolgeeKeysData[];
|
|
};
|
|
page: {
|
|
size: number;
|
|
totalElements: number;
|
|
totalPages: number;
|
|
number: number;
|
|
};
|
|
};
|