Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
305 lines
8.0 KiB
JavaScript
305 lines
8.0 KiB
JavaScript
//-----------------------------------------------------------------------------
|
|
// Get the first non empty character (not space or tab)
|
|
|
|
//=============================================================================
|
|
// DEFINES
|
|
//=============================================================================
|
|
|
|
//-- Node.js libraries
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
//-- MSXgl JS libraries
|
|
const util = require("./util.js");
|
|
const locUtil = require("./localize/localize_util.js");
|
|
const locArb = require("./localize/localize_arb.js");
|
|
const locCyr = require("./localize/localize_cyr.js");
|
|
const locLat = require("./localize/localize_lat.js");
|
|
const locJap = require("./localize/localize_jap.js");
|
|
const locKor = require("./localize/localize_kor.js");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constants
|
|
|
|
const version = "1.0.0";
|
|
|
|
const lineTypeSection = 1;
|
|
const lineTypeText = 2;
|
|
const lineTypeSpecial = 3;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Variables
|
|
|
|
var bComment = true;
|
|
|
|
//=============================================================================
|
|
// FUNCTIONS
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parse a line and return a descriptor according to line type
|
|
function getLineType(str)
|
|
{
|
|
var obj = {};
|
|
const chr = locUtil.getFirstChar(str);
|
|
if((chr == '#') || (chr == ';')) // Comment
|
|
return null;
|
|
else if(chr == '[') // Language code
|
|
{
|
|
const reg = /\[\s*(?<section>[\w]+)\s*\]/;
|
|
const res = str.match(reg);
|
|
if(res == null)
|
|
{
|
|
console.log(`Error: Invalid section! ${str}`);
|
|
return null;
|
|
}
|
|
obj['type'] = lineTypeSection;
|
|
obj['value'] = res.groups.section.toUpperCase();
|
|
}
|
|
else if(chr != 0) // Text entry
|
|
{
|
|
const reg = /\s*(?<key>[\w]+)\s*\=\s*"(?<value>[^"]*)"/;
|
|
const res = str.match(reg);
|
|
if(res == null)
|
|
{
|
|
console.log(`Error: Invalid text! ${str}`);
|
|
return null;
|
|
}
|
|
obj['type'] = (chr == '@') ? lineTypeSpecial : lineTypeText;
|
|
obj['key'] = res.groups.key.toUpperCase();
|
|
obj['value'] = res.groups.value;
|
|
}
|
|
else
|
|
return null;
|
|
|
|
return obj;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parse a localisation file
|
|
function parseLocFile(filename, locTable, langTable)
|
|
{
|
|
locUtil.log("Parsing " + filename + "...");
|
|
|
|
var lang = null;
|
|
const fileData = fs.readFileSync(filename, 'utf8');
|
|
const lines = fileData.split('\n');
|
|
for (var line = 0; line < lines.length; line++)
|
|
{
|
|
const obj = getLineType(lines[line]);
|
|
if(obj != null)
|
|
{
|
|
switch(obj.type)
|
|
{
|
|
// Start a new language section
|
|
case lineTypeSection:
|
|
if(obj.value != lang)
|
|
{
|
|
lang = obj.value;
|
|
}
|
|
locUtil.resetSpecial(); // Reset language parameters
|
|
break;
|
|
|
|
// Special character entry
|
|
case lineTypeSpecial:
|
|
if(langTable[lang] == null)
|
|
langTable[lang] = {};
|
|
|
|
langTable[lang][obj.key] = obj.value;
|
|
locUtil.handleSpecial(obj.key, obj.value);
|
|
break;
|
|
|
|
// Localization entry
|
|
case lineTypeText:
|
|
if(locTable[lang] == null)
|
|
locTable[lang] = [];
|
|
|
|
var str = "";
|
|
switch(lang)
|
|
{
|
|
case "INT": // International
|
|
case "ENG": // English
|
|
case "EN":
|
|
case "FRA": // French
|
|
case "FRE":
|
|
case "FR":
|
|
case "GER": // German
|
|
case "DEU":
|
|
case "DE":
|
|
case "SPA": // Spanish
|
|
case "ES":
|
|
case "DAN": // Danish
|
|
case "DA":
|
|
case "NO": // Norwegian
|
|
case "NOR":
|
|
case "NL": // Dutch
|
|
case "NLD":
|
|
case "DUT":
|
|
case "FI": // Finnish
|
|
case "FIN":
|
|
case "DE": // German
|
|
case "DEU":
|
|
case "GER":
|
|
case "IS": // Icelandic
|
|
case "ISL":
|
|
case "ICE":
|
|
case "GA": // Irish
|
|
case "GLE":
|
|
case "IT": // Italian
|
|
case "ITA":
|
|
case "PL": // Polish
|
|
case "POL":
|
|
case "PT": // Portuguese
|
|
case "POR":
|
|
case "ES": // Spanish, Castilian
|
|
case "SPA":
|
|
case "SK": // Slovak
|
|
case "SLK":
|
|
case "SLO":
|
|
case "SV": // Swedish
|
|
case "SWE":
|
|
case "TR": // Turkish
|
|
case "TUR":
|
|
str = locLat.convertToMSX(obj.value);
|
|
break;
|
|
|
|
case "JA": // Japanese
|
|
case "JPN":
|
|
str = locJap.convertToMSX(obj.value);
|
|
break;
|
|
|
|
case "KO": // Korean
|
|
case "KOR":
|
|
str = locKor.convertToMSX(obj.value);
|
|
break;
|
|
|
|
case "AR": // Arabic
|
|
case "ARA":
|
|
str = locArb.convertToMSX(obj.value);
|
|
break;
|
|
|
|
case "UK": // Ukrainian
|
|
case "UKR":
|
|
case "RU": // Russian
|
|
case "RUS":
|
|
str = locCyr.convertToMSX(obj.value);
|
|
break;
|
|
|
|
default:
|
|
str = locLat.convertToMSX(obj.value);
|
|
// str = obj.value;
|
|
break;
|
|
}
|
|
|
|
if(locTable[lang][obj.key] == null)
|
|
locTable[lang][obj.key] = str; // set
|
|
else
|
|
locTable[lang][obj.key] += "\\n" + str; // concatenate
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Main programm
|
|
//=============================================================================
|
|
|
|
module.exports.generate = function (inputFiles, outFilename, structName, splitDef, verbose)
|
|
{
|
|
invalidChar = 0x1C; // 0x3F
|
|
bVerbose = verbose;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parse the input file
|
|
var locTable = [];
|
|
var langTable = [];
|
|
for(var i = 0; i < inputFiles.length; i++)
|
|
parseLocFile(inputFiles[i], locTable, langTable);
|
|
|
|
// locUtil.log("locTable:");
|
|
// locUtil.log(locTable);
|
|
// locUtil.log("langTable:");
|
|
// locUtil.log(langTable);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Analyze localisation data
|
|
var langIDs = [];
|
|
var textIDs = [];
|
|
var langKeys = Object.keys(locTable);
|
|
for(var i = 0; i < langKeys.length; i++)
|
|
{
|
|
langIDs.push(langKeys[i]);
|
|
var textKeys = Object.keys(locTable[langKeys[i]]);
|
|
for(var j = 0; j < textKeys.length; j++)
|
|
if(!textIDs.includes(textKeys[j]))
|
|
textIDs.push(textKeys[j]);
|
|
}
|
|
|
|
// locUtil.log("langIDs:");
|
|
// locUtil.log(langIDs);
|
|
// locUtil.log("textIDs:");
|
|
// locUtil.log(textIDs);
|
|
|
|
var outDefFilename = "";
|
|
if (splitDef)
|
|
outDefFilename = path.parse(outFilename).dir + "/" + path.parse(outFilename).name + "_def.h";
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Generate header file
|
|
var i;
|
|
var headContent = `// Generated by MSXgl on ${util.getDateString()}\n`
|
|
headContent += `#pragma once\n#include "core.h"\n\n`;
|
|
|
|
headContent += "// Language definition\n";
|
|
for(i = 0; i < langIDs.length; i++)
|
|
headContent += `#define LANG_${langIDs[i]} ${i}\n`;
|
|
headContent += `#define LANG_MAX ${i}\n\n`;
|
|
|
|
headContent += "// Text ID definition\n";
|
|
for(i = 0; i < textIDs.length; i++)
|
|
headContent += `#define TEXT_${textIDs[i]} ${i}\n`;
|
|
headContent += `#define TEXT_MAX ${i}\n\n`;
|
|
|
|
// Handle definition split in separate file
|
|
if (splitDef)
|
|
{
|
|
headContent += "// Localization data table\n";
|
|
headContent += `extern const c8* const ${structName}[LANG_MAX][TEXT_MAX];\n`;
|
|
|
|
fs.writeFileSync(outDefFilename, headContent);
|
|
|
|
headContent = `// Generated by MSXgl on ${util.getDateString()}\n`
|
|
headContent += `#pragma once\n#include "core.h"\n#include "${outDefFilename}"\n\n`;
|
|
}
|
|
|
|
headContent += "// Localization data table\n";
|
|
headContent += `const c8* const ${structName}[LANG_MAX][TEXT_MAX] =\n{\n`;
|
|
|
|
var langKeys = Object.keys(locTable);
|
|
for(var i = 0; i < langKeys.length; i++)
|
|
{
|
|
headContent += `\t// ${langKeys[i]}\n\t{\n`;
|
|
for(var j = 0; j < textIDs.length; j++)
|
|
{
|
|
if(locTable[langKeys[i]][textIDs[j]] == null)
|
|
{
|
|
headContent += `\t\tNULL,`;
|
|
locUtil.log(`Warning: Text ID '${textIDs[j]}' not defined for language '${langKeys[i]}'`);
|
|
}
|
|
else
|
|
headContent += `\t\t"${locTable[langKeys[i]][textIDs[j]]}",`;
|
|
if(bComment)
|
|
headContent += ` // TEXT_${textIDs[j]}`;
|
|
headContent += "\n";
|
|
}
|
|
headContent += `\t},\n`;
|
|
}
|
|
|
|
headContent += `};`;
|
|
|
|
// locUtil.log("headContent:");
|
|
// locUtil.log(headContent);
|
|
|
|
fs.writeFileSync(outFilename, headContent);
|
|
} |