diff --git a/.contrib/.tools/Lua/normalize.js b/.contrib/.tools/Lua/normalize.js new file mode 100644 index 00000000000..66331de32fa --- /dev/null +++ b/.contrib/.tools/Lua/normalize.js @@ -0,0 +1,121 @@ +const fs = require("fs"); + +/** + * Normalize ATT localization tables into the canonical locale order. + * + * This script is intentionally limited to the simple, one-locale-per-line + * tables used by the parser data. It updates the supplied file in place, so + * callers should review the resulting Git diff before committing it. + */ + +// Keep this order aligned with the locale order used by ATT data files. +const expectedKeys = [ + "en","de","es","mx", + "fr","it","ko","pt", + "ru","cn","tw" +]; + +const file = process.argv[2]; +if (!file) { + console.error("Please provide the path of the file to process"); + process.exit(1); +} + +let content = fs.readFileSync(file, "utf8"); + +// Match only localization-bearing fields. The non-greedy body stops at the +// first closing brace on its own line, which is valid for the flat tables this +// tool supports. The final group preserves an optional Lua comma or semicolon. +const tableRegex = + /(^[ \t]*(text|description|lore)\s*=\s*\{)([\s\S]*?)(^\s*\})([ \t]*[,;]?)/gm; + +content = content.replace( + tableRegex, + (fullMatch, tableHeader, tableName, body, closingBraceLine, afterBrace) => { + + // Reuse the table header's indentation for the closing brace so running + // the tool does not disturb the surrounding Lua structure. + const headerIndent = tableHeader.match(/^(\s*)/)[1]; + + // Empty lines are removed during normalization. Only recognized locale + // assignments are retained; other content inside a matched table is out + // of scope for this deliberately narrow formatter. + const lines = body.split(/\r?\n/).filter((l) => l.trim() !== ""); + + const presentKeys = []; + const valueLines = []; + + for (const line of lines) { + // A translated locale, for example: en = "Example", + const normal = line.match(/^\s*(\w+)\s*=/); + + // Commented translations and TODO placeholders both count as present; + // otherwise a second placeholder for the same locale would be created. + const commented = line.match(/^\s*--\s*(?:TODO:\s*)?(\w+)\s*=/); + + if (normal) { + presentKeys.push(normal[1]); + valueLines.push(line); // Preserve as-is + } else if (commented) { + presentKeys.push(commented[1]); // Commented fields count as present + valueLines.push(line); // Preserve as-is + } + } + + // English-only entries are intentionally compact. TODO placeholders become + // useful only after an entry has started receiving additional translations. + if (presentKeys.length === 1 && presentKeys[0] === "en") { + return ( + `${tableHeader}\n` + + valueLines.join("\n") + + `\n${headerIndent}}${afterBrace}` + ); + } + + // Match generated TODO entries to the indentation of the first locale. + let firstIndent = ""; + if (valueLines.length > 0) { + const m = valueLines[0].match(/^(\s*)/); + firstIndent = m ? m[1] : ""; + } + + // Rebuild the table in canonical order. Values are reused verbatim so this + // step changes position and completeness, but never translation content. + const finalLines = []; + + for (const k of expectedKeys) { + // Prefer an active translation when both active and commented variants + // somehow exist for the same locale. + const existing = valueLines.find((l) => + l.trimStart().startsWith(k + " =") + ); + + const commented = valueLines.find((l) => + l.trimStart().startsWith("-- " + k + " =") || + l.trimStart().startsWith("-- TODO: " + k + " =") + ); + + if (existing) { + finalLines.push(existing); + } else if (commented) { + finalLines.push(commented); // Preserve commented line + } else { + // Make missing translations visible without creating executable Lua + // fields whose empty values might be mistaken for real translations. + finalLines.push(`${firstIndent}-- TODO: ${k} = "",`); + } + } + + return ( + `${tableHeader}\n` + + finalLines.join("\n") + + `\n${headerIndent}}${afterBrace}` + ); + } +); + +// Write once after all matching tables have been normalized. No backup file is +// created because Git is expected to provide review and recovery for this tool. +fs.writeFileSync(file, content, "utf8"); + +console.log("✅ Done: Commented fields are treated as existing, format fully preserved and keys reordered"); diff --git a/.contrib/.tools/README.md b/.contrib/.tools/README.md new file mode 100644 index 00000000000..8f0adffb328 --- /dev/null +++ b/.contrib/.tools/README.md @@ -0,0 +1,95 @@ +# Contributor Tools + +## Normalize Lua localization tables + +[`Lua/normalize.js`](Lua/normalize.js) normalizes localization tables in a Lua data file. It is intended for contributors who edit `text`, `description`, or `lore` tables in the parser data. + +The tool: + +- processes every `text`, `description`, and `lore` table in the selected file; +- orders locale keys as `en`, `de`, `es`, `mx`, `fr`, `it`, `ko`, `pt`, `ru`, `cn`, and `tw`; +- adds a commented `TODO` entry for each missing locale; +- preserves existing locale values and existing commented locale entries; and +- leaves an English-only table English-only instead of adding `TODO` entries. + +### Requirements + +- [Node.js](https://nodejs.org/) must be installed and available as `node`. +- To use the included task, open the repository in Visual Studio Code. + +### Run from Visual Studio Code + +1. Open the Lua file that you want to normalize and make sure it is the active editor. +2. Open the Command Palette. +3. Select **Tasks: Run Task**. +4. Select **Normalize Lua Table**. +5. Wait for the terminal to report that the operation is complete. +6. Review the file's Git diff before keeping the result. + +The task uses the active editor's file. Save or switch to the intended file before running it. + +### Run from a terminal + +From the repository root, run: + +```sh +node .contrib/.tools/Lua/normalize.js "path/to/data-file.lua" +``` + +For example: + +```sh +node .contrib/.tools/Lua/normalize.js ".contrib/Parser/DATAS/00 - DB/ObjectDB.lua" +``` + +### Example + +Input: + +```lua +text = { + en = "Example", + fr = "Exemple", +} +``` + +Output: + +```lua +text = { + en = "Example", + -- TODO: de = "", + -- TODO: es = "", + -- TODO: mx = "", + fr = "Exemple", + -- TODO: it = "", + -- TODO: ko = "", + -- TODO: pt = "", + -- TODO: ru = "", + -- TODO: cn = "", + -- TODO: tw = "", +} +``` + +An English-only table remains compact: + +```lua +text = { + en = "English only", +} +``` + +### Safety and limitations + +The tool edits the selected file in place and does not create a backup. Commit or stash unrelated work first, then review the complete diff after it runs. + +Use it only on localization tables containing one locale assignment per line, for example `de = "..."`, `-- de = "..."`, or `-- TODO: de = "..."`. Blank lines and content that is not recognized as a locale assignment are not retained inside a matched table. + +To inspect the result: + +```sh +git diff -- "path/to/data-file.lua" +git diff --check +``` + +If the result is not expected, restore it with your normal Git workflow before making additional edits. diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000000..013c53c7b68 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Normalize Lua Table", + "type": "shell", + "command": "node", + // Run against the file in the active editor, rather than a fixed data file. + // Explicit quoting keeps paths containing spaces as one shell argument. + "args": ["${workspaceFolder}/.contrib/.tools/Lua/normalize.js", "\"${file}\""], + "problemMatcher": [], + "group": "none" + } + ] +}