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, "'"); }; 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 ( 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(`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); }; (async () => { const args = process.argv.slice(2); const addonDir = path.join(process.cwd(), `../Addon/Data/${args[0]}`); const addonData: Record = {}; let pageNumber = 0; try { // load all data from tolgee while (true) { const response = await callTolgee('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; 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, ], }), 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'); } } }); } catch (e) { throw e; } })(); type AddonData = Record; type Quest = { id: string; name: string; names?: (string | null)[]; descriptions?: (string | null)[]; progresses?: (string | null)[]; completions?: (string | null)[]; objectives?: (string | null)[]; speeches?: (string | null)[]; 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; }; };