Tool RPGM SLR Translator - Offline JP to EN Translation for RPG Maker VX, VX Ace, MV, and MZ

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Hi ,I'm back
I follow your guide but when i click replace,it said that Found nothing to replace
Don't know why , I checked again,it seem that I had typed it correctly
Like this
I think the reason might be that the folder is too large, and the search function can't keep up.
Anyone know how to fix it ?
Here is my error /dac... data folder too,someone can try to fix it on your device too
tks for reading
Oh.... I'm sorry, I so rarely work with unedited text that I forgot that the replace menu doesn't touch original cells.
It needs to be put in Initial Corrections or any of the other active cells first.
Since the fastest way to do that is a custom script we might as well do it all in one go without the replace menu though.
Rightclick any object.
"With all"
"Create Automation"
"For each row"
1.png
In that new menu put in:
JavaScript:
if (!this.cells[0]) return;
this.cells[1] = this.cells[0];
this.cells[1] = this.cells[1].replaceAll(/\\dac */g, "\\dac ");
And run it.

Edit: If you need to do more replacements afterwards, you can use the replace menu, because everything will have been placed in Initial Translations now.
 
Last edited:
  • Heart
Reactions: taodentumienque

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Hi ,I'm back
I follow your guide but when i click replace,it said that Found nothing to replace
Don't know why , I checked again,it seem that I had typed it correctly
Like this
I think the reason might be that the folder is too large, and the search function can't keep up.
Anyone know how to fix it ?
Here is my error /dac... data folder too,someone can try to fix it on your device too
tks for reading
One note though. This will have placed a space after \dac in ALL cells, meaning also in plugins, which could "potentially" cause issues.
So make sure to only export/replace the files that actually need the changes.
Or to only select the correct objects before running the script.
 
Oct 21, 2022
38
3
Oh.... I'm sorry, I so rarely work with unedited text that I forgot that the replace menu doesn't touch original cells.
It needs to be put in Initial Corrections or any of the other active cells first.
Since the fastest way to do that is a custom script we might as well do it all in one go without the replace menu though.
Rightclick any object.
"With all"
"Create Automation"
"For each row"
View attachment 4199357
In that new menu put in:
JavaScript:
if (!this.cells[0]) return;
this.cells[1] = this.cells[0];
this.cells[1] = this.cells[1].replaceAll(/\\dac */g, "\\dac ");
And run it.

Edit: If you need to do more replacements afterwards, you can use the replace menu, because everything will have been placed in Initial Translations now.
the problem seem to be fixed,thank you so much
 

rday49

New Member
Nov 6, 2019
4
0
Hi, im very new to translators and programs of this context. I've attempted to use slr to translate a demo for an upcoming RPGMMV called The Tale of the Ninja Shrine Maiden Chiyome's Monster Extermination (RJ01109235) on DLsite. Following previous instructions in this thread and instructions that came with SLR I managed to extract the translations to the game and was succesful in the translations. However, while running the game several images fail to load. Are these an issue with plugins? I'm hoping for a bit of knowledge on how to identify the issue and rectify said issues so I can use SLR in the future. There is many underscore symbol errors detected during the fix cells and error check. I deleted each initial correction cell with this error. Is that the issue?
 

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Hi, im very new to translators and programs of this context. I've attempted to use slr to translate a demo for an upcoming RPGMMV called The Tale of the Ninja Shrine Maiden Chiyome's Monster Extermination (RJ01109235) on DLsite. Following previous instructions in this thread and instructions that came with SLR I managed to extract the translations to the game and was succesful in the translations. However, while running the game several images fail to load. Are these an issue with plugins? I'm hoping for a bit of knowledge on how to identify the issue and rectify said issues so I can use SLR in the future. There is many underscore symbol errors detected during the fix cells and error check. I deleted each initial correction cell with this error. Is that the issue?
"Com" refers to a plugin that is giving menu buttons pictures. Usually the pictures are named after the action name or even other menu buttons, which is why they should still be translated.
It's a bit of a stupid system, I don't really know why devs use it, but to prevent the crashes go into the files and look for pictures (Usually encrypted ones so they have a ".rpgmvp" ending) that start with "Com_".
Most of the time they will have a separate folder.
Now feed the SLR Translator search menu the text behind "Com_" so you can find the cells used.
Usually it's stuff like "Attack" "Guard" "Item" "Equipment" "Special" "Escape" etc. for a combat menu.
Make sure they are translated consistently and for example not the same Japanese text one time as "Defense" and one time as "Guard" and then rename those "Com_" files to match the translation.

Edit: As for the "POTENTIALFILENAME" stuff, that is really just "potential" chances are those cells are fine and you can just get rid of those cells with the error code in it. It's just meant to make you aware that cells are named funky in case you run into issues.
 
Last edited:
  • Like
Reactions: rday49

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Due to how low accuracy the POTENTIALFILENAME error is, I'm thinking about getting rid of it.
First I thought about doing the UNUSUALLYSHORT route of just removing the error code and instead giving it a fancy color, but that would be just another annoying thing to check or will be ignored in general.

I think instead what I'm going to do is escape all text combined with _ and have it not be translated.
I don't mean the entire cell, just if there is something in there that is "characterswithoutspacesorlinebreaks_characterswithoutspacesorlinebreaks", then that specific bit will be preserved.

Because almost always that is going to be something that doesn't even need translation in the first place, because it's some skill name only an enemy can actually cast, a plugin placeholder, or something like that.

That way SLR is still a lot less likely to allow problematic translations of file names and you're no longer annoyed by the error codes every damn translation.
 
  • Like
Reactions: rday49
Oct 21, 2022
38
3
Hello guys
I wrote a Python program to extract all Chinese text from JSON files in the 'data' folder for translation.
However, extracting all text means it also includes image names, music, system code, etc.
And if I translate all of them, it would conflict with files in the img, audio folders,...
Therefore, I want to adjust it so that it only extracts the necessary text, similar to SLR or Translator++.

Could someone help me to edit this code, or suggest ideas for a better way?
Python:
import os
import re

def extract_chinese_words(text):
    pattern = re.compile(r'[\u4e00-\u9fff]+')  # Regex to filter Chinese words
    chinese_words = pattern.findall(text)
    return chinese_words

def process_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
        chinese_words = extract_chinese_words(content)
        return chinese_words

def find_json_files(root_dir):
    json_files = []
    for root, dirs, files in os.walk(root_dir):
        for file in files:
            if file.endswith(".json"):
                file_path = os.path.join(root, file)
                json_files.append(file_path)
    return json_files

def main():
    input_dir = "www"
    output_file = "extracted_words.lua"

    chinese_words_set = set()

    json_files = find_json_files(input_dir)

    for file_path in json_files:
        chinese_words = process_file(file_path)
        chinese_words_set.update(chinese_words)

    with open(output_file, 'w', encoding='utf-8') as outfile:
        for word in chinese_words_set:
            outfile.write(word + '\n')

    print("Chinese texts have been written to", output_file)

if __name__ == "__main__":
    main()
 

aru6551

Newbie
Oct 28, 2019
56
85
Hello guys
I wrote a Python program to extract all Chinese text from JSON files in the 'data' folder for translation.
However, extracting all text means it also includes image names, music, system code, etc.
And if I translate all of them, it would conflict with files in the img, audio folders,...
Therefore, I want to adjust it so that it only extracts the necessary text, similar to SLR or Translator++.

Could someone help me to edit this code, or suggest ideas for a better way?
I think you need to figure out what the things in the json file mean.
DazedMTLTool's RPGMakerEventCodes.info and SLR's rmmv.js both have instructions for RPGM EVENT CODE.
There are also many rpg_maker translators on github for reference.
There is also a copy of the code here.
https://f95zone.to/threads/creando-...n-scripts-to-translate-rpgs-mv-and-mz.226406/
 
Oct 21, 2022
38
3
I think you need to figure out what the things in the json file mean.
DazedMTLTool's RPGMakerEventCodes.info and SLR's rmmv.js both have instructions for RPGM EVENT CODE.
There are also many rpg_maker translators on github for reference.
There is also a copy of the code here.
https://f95zone.to/threads/creando-...n-scripts-to-translate-rpgs-mv-and-mz.226406/
Is there anyway to customize SLR translator API ?
I mean i want to translate game's data by my own api that's made for translating chinese to vietnamese(my native language),not by sugoi,google,...
I asked how to extract all text before because after that i'd translate the extracted text file by my tool and put the translated text to original data file
If i can set up my api to SLR or translator++ ,i dont need to follow that extracting_text way again,just use SLR with my custom api to translate game
 

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Is there anyway to customize SLR translator API ?
I mean i want to translate game's data by my own api that's made for translating chinese to vietnamese(my native language),not by sugoi,google,...
I asked how to extract all text before because after that i'd translate the extracted text file by my tool and put the translated text to original data file
If i can set up my api to SLR or translator++ ,i dont need to follow that extracting_text way again,just use SLR with my custom api to translate game
If you want to use your own translator you need to make a completely new T++ style addon for it, or add another engine entry to SLRtrans to communicate with whatever translation server api you have.
For example this is the SugoiV4 entry SLR Translator uses.
JavaScript:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedSugoiEngine = void 0;
const TranslationEngineOption_1 = require("./TranslationEngineOption");
const TranslationEngineWrapper_1 = require("./TranslationEngineWrapper");
class RedSugoiEngine extends TranslationEngineWrapper_1.TranslationEngineWrapper {
    constructor(processor, thisAddon) {
        super(processor, thisAddon, {
            id: 'slr',
            name: 'SLR',
            languages: {
                en: 'English',
                ja: 'Japanese'
            },
            batchDelay: 1,
            description: thisAddon.package.description,
            mode: 'rowByRow'
        });
        this.aborted = false;
        this.paused = false;
        this.pauseQueue = [];
        this.urlUsage = [];
        this.optionUrls = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'urls',
            default: ['http://localhost:14366/'].join('\n'),
            description: "URLs for the Sugoi Translator servers. Place one per line. There will be an attempt to balance the load between servers such that every server has the same amount of active requests open, so it's also important to pick a good request count number to match your servers (usually two times the number of servers is good enough).\n" +
                "*VERY IMPORTANT:* SLR only works with Sugoi Translator Servers which have been patched to accept Arrays (text in groups rather than single texts). If you haven't done so yet, it is necessary to use the T++ Sugoi Server Manager to patch your Sugoi - the button is available below, but the original Sugoi addon is necessary to use it.\n" +
                'Also important: always double check that your server addresses are correct. If setting up with the Sugoi Server Manager, you can use their button to set values at the default Sugoi addon, and we have a second button here to copy those values.',
            name: 'Sugoi Translator URLs',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -10,
            formType: 'textarea',
            formOptions: {
                height: '50px'
            },
            childForm: [
                {
                    type: 'actions',
                    title: 'Local Server Manager',
                    fieldHtmlClass: 'actionButtonSet',
                    items: [
                        {
                            type: 'button',
                            title: 'Open Sugoi Server Manager',
                            onClick: function () {
                                try {
                                    trans.sugoitrans.openServerManager();
                                }
                                catch (e) {
                                    alert("");
                                }
                            }
                        },
                        {
                            type: 'button',
                            title: 'Copy Sugoi Addon Server URLs',
                            onClick: (evt) => {
                                try {
                                    window.clicked = evt;
                                    var optionWindow = $(evt.target.parentNode.parentNode);
                                    let engine = this.getEngine();
                                    optionWindow.find(`[name="urls"]`).val(trans.sugoitrans.targetUrl);
                                    engine.update('urls', trans.sugoitrans.targetUrl);
                                }
                                catch (e) {
                                    alert("");
                                }
                            }
                        }
                    ]
                }
            ]
        });
        /* this.optionRemoveBreaks = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'removeBreaks',
            default: false,
            description: [
                'Sugoi Translator does not understand line breaks.',
                "This option replaces all line breaks with an space before translation. This hasn't been thoroughly tested, so there is no knowledge of whether this improves translation quality or not, but it should, in theory."
            ].join('\n'),
            name: 'Remove Line Breaks',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.OPTIONS
        }); */
        /**
         * Limits
         */
        this.optionMaxLength = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'maxTranslationLength',
            default: 5000,
            description: [
                'Maximum amount of characters that will be sent per server request.',
                "Sugoi translator can crash if you send text that is bigger than your RAM/VRAM can handle, so you can set an upper limit here. In general, the higher this number is, the faster the translation process will be - so long as you don't run out of memory. The default value is very conservative, feel free to increase it until your hardware cries.",
                "Note: if an atomic string (that can't be split) is larger than this amount, it will still be sent in full, but it will be sent alone."
            ].join('\n'),
            name: 'Request Character Limit',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -9
        });
        this.optionConcurrency = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'maxTranslationJobs',
            default: 1,
            description: 'Maximum amount of requests sent to the servers at the same time. The best number is the one that results in no downtime for the servers.',
            name: 'Maximum Request Count',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -8
        });
    }
    getUrl() {
        let urls = this.optionUrls.getValue().split(/\r?\n/g);
        if (this.urlUsage.length != urls.length) {
            this.urlUsage = new Array(urls.length).fill(0);
        }
        let leastUsed = this.urlUsage.indexOf(Math.min(...this.urlUsage));
        this.urlUsage[leastUsed]++;
        return {
            url: urls[leastUsed],
            index: leastUsed
        };
    }
    freeUrl(urlIndex) {
        this.urlUsage[urlIndex]--;
    }
    doTranslate(toTranslate, options) {
        this.resetStatus();
        let returnTranslations;
        let returnError;
        if (options.sl != 'ja') {
            this.error(`[SLR] The project specifies the source language as not being Japanese (${options.sl}). Since Sugoi Translator only supports Japanese as source, we will use Japanese instead.`);
        }
        if (options.tl != 'en') {
            this.error(`[SLR] The project specifies the destination language as not being English (${options.tl}). Since Sugoi Translator only supports English as destination, we will use English instead.`);
        }
        let translations = new Array(toTranslate.length);
        let translatingIndex = 0;
        let completeThreads = 0;
        let totalThreads = this.optionConcurrency.getValue();
        totalThreads = totalThreads < 1 ? 1 : totalThreads;
        let maximumBatchSize = this.optionMaxLength.getValue();
        let complete = () => {
            if (++completeThreads == totalThreads) {
                returnTranslations(translations);
            }
        };
        let translationProgress = document.createTextNode('0');
        let translatedCount = 0;
        let errorProgress = document.createTextNode('');
        let errorCount = 0;
        this.print(document.createTextNode('[SLR] Translating texts: '), translationProgress, document.createTextNode('/' + toTranslate.length), errorProgress);
        const updateTranslatedCount = (count) => {
            translatedCount += count;
            translationProgress.nodeValue = translatedCount.toString();
            options.progress(Math.round((100 * translatedCount) / toTranslate.length));
        };
        const updateErrorCount = (count) => {
            errorCount += count;
            errorProgress.nodeValue = ' (' + errorCount.toString() + ' failed translations)';
        };
        let serverScores = {};
        let startThread = () => {
            if (this.aborted) {
                returnError('Aborted.');
            }
            if (this.paused) {
                this.pauseQueue.push(() => {
                    startThread();
                });
                return;
            }
            let batchLength = 0;
            let batch = [];
            let batchIndexes = [];
            while (translatingIndex < toTranslate.length) {
                let index = translatingIndex;
                if (toTranslate[index] !== undefined &&
                    (batchLength == 0 ||
                        batchLength + toTranslate[index].length <= maximumBatchSize)) {
                    /* if (this.optionRemoveBreaks.getValue()) {
                        toTranslate[index] = toTranslate[index].replaceAll(/\r?\n/g, ' ');
                    } */
                    batch.push(toTranslate[index]);
                    batchIndexes.push(index);
                    batchLength += toTranslate[index].length;
                    translatingIndex++;
                }
                else {
                    break;
                }
            }
            if (batch.length == 0) {
                complete();
            }
            else {
                let myServer = this.getUrl();
                if (serverScores[myServer.url] == undefined) {
                    serverScores[myServer.url] = 0;
                }
                fetch(myServer.url, {
                    method: 'post',
                    body: JSON.stringify({ content: batch, message: 'translate sentences' }),
                    headers: { 'Content-Type': 'application/json' }
                })
                    .then(async (response) => {
                    if (response.ok) {
                        serverScores[myServer.url] += batch.length;
                        let result = await response.json();
                        console.log('[SLR] Fetched from ' + myServer.url, result);
                        if (result.length != batch.length) {
                            console.error('[SLR] MISMATCH ON RESPONSE:', batch, result);
                            throw new Error(`Received invalid response - length mismatch, check server stability.`);
                        }
                        else {
                            for (let i = 0; i < batch.length; i++) {
                                translations[batchIndexes[i]] = result[i];
                            }
                        }
                        updateTranslatedCount(batch.length);
                    }
                    else {
                        throw new Error(`${response.status.toString()} - ${response.statusText}`);
                    }
                })
                    .catch((error) => {
                    updateErrorCount(batch.length);
                    console.error('[SLR] ERROR ON FETCH USING ' + myServer, '   Payload: ' + batch.join('\n'), error);
                    this.error(`[SLR] Error while fetching from ${myServer.url} - ${error.name}: ${error.message}\n${' '.repeat(11)}If all fetch attempts fail on this server, check if it's still up.`);
                })
                    .finally(() => {
                    this.freeUrl(myServer.index);
                    startThread();
                });
            }
        };
        return new Promise((resolve, reject) => {
            returnTranslations = resolve;
            returnError = reject;
            for (let i = 0; i < totalThreads; i++) {
                startThread();
            }
        });
    }
    resetStatus() {
        this.aborted = false;
        this.paused = false;
        this.pauseQueue = [];
    }
    abort() {
        this.aborted = true;
    }
    pause() {
        this.paused = true;
    }
    resume() {
        this.paused = false;
        this.pauseQueue.forEach((action) => {
            action();
        });
        this.pauseQueue = [];
    }
}
exports.RedSugoiEngine = RedSugoiEngine;
Depending on how your Translation service works you might need more than a simple addon entry like that though.

The SLR dictionary and script protection stuff will not interfere with anything and you can use the wrapper, as long as the target language is still English.

Edit: But you would have to remove the checks for translatable Japanese in the "prepare for batch translation" button. Or the tagging gets messed up. You can find all the tagging related stuff in the transredbatch.js in SLRbatch.
The best way would probably not to remove the checks, but to change the regex constants to search for Chinese instead of Japanese.
1731021995614.png
 
Last edited:

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
I've released v1.112.
The POTENTIALFILENAME error code is now fully removed.
It will now always escape text around _ and I've significantly improved detection for cells including filenames.
I've also made yellow tags and a popup to prevent filenames from being mistakenly translated if the MOG Battle Commands plugin is used.
(The "Com_" picture stuff.)
Also lines that only include "//" escaped text will no longer be sent to Sugoi.

I realize the always escaping text around _ bit is likely going to cause issues with a few quirky devs because they actually use it in text, but I think that's still less bad than the constant error spam.
(And when I asked about doing this, nobody gave me any feedback.)
 
  • Like
Reactions: rday49
Oct 21, 2022
38
3
If you want to use your own translator you need to make a completely new T++ style addon for it, or add another engine entry to SLRtrans to communicate with whatever translation server api you have.
For example this is the SugoiV4 entry SLR Translator uses.
JavaScript:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedSugoiEngine = void 0;
const TranslationEngineOption_1 = require("./TranslationEngineOption");
const TranslationEngineWrapper_1 = require("./TranslationEngineWrapper");
class RedSugoiEngine extends TranslationEngineWrapper_1.TranslationEngineWrapper {
    constructor(processor, thisAddon) {
        super(processor, thisAddon, {
            id: 'slr',
            name: 'SLR',
            languages: {
                en: 'English',
                ja: 'Japanese'
            },
            batchDelay: 1,
            description: thisAddon.package.description,
            mode: 'rowByRow'
        });
        this.aborted = false;
        this.paused = false;
        this.pauseQueue = [];
        this.urlUsage = [];
        this.optionUrls = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'urls',
            default: ['http://localhost:14366/'].join('\n'),
            description: "URLs for the Sugoi Translator servers. Place one per line. There will be an attempt to balance the load between servers such that every server has the same amount of active requests open, so it's also important to pick a good request count number to match your servers (usually two times the number of servers is good enough).\n" +
                "*VERY IMPORTANT:* SLR only works with Sugoi Translator Servers which have been patched to accept Arrays (text in groups rather than single texts). If you haven't done so yet, it is necessary to use the T++ Sugoi Server Manager to patch your Sugoi - the button is available below, but the original Sugoi addon is necessary to use it.\n" +
                'Also important: always double check that your server addresses are correct. If setting up with the Sugoi Server Manager, you can use their button to set values at the default Sugoi addon, and we have a second button here to copy those values.',
            name: 'Sugoi Translator URLs',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -10,
            formType: 'textarea',
            formOptions: {
                height: '50px'
            },
            childForm: [
                {
                    type: 'actions',
                    title: 'Local Server Manager',
                    fieldHtmlClass: 'actionButtonSet',
                    items: [
                        {
                            type: 'button',
                            title: 'Open Sugoi Server Manager',
                            onClick: function () {
                                try {
                                    trans.sugoitrans.openServerManager();
                                }
                                catch (e) {
                                    alert("");
                                }
                            }
                        },
                        {
                            type: 'button',
                            title: 'Copy Sugoi Addon Server URLs',
                            onClick: (evt) => {
                                try {
                                    window.clicked = evt;
                                    var optionWindow = $(evt.target.parentNode.parentNode);
                                    let engine = this.getEngine();
                                    optionWindow.find(`[name="urls"]`).val(trans.sugoitrans.targetUrl);
                                    engine.update('urls', trans.sugoitrans.targetUrl);
                                }
                                catch (e) {
                                    alert("");
                                }
                            }
                        }
                    ]
                }
            ]
        });
        /* this.optionRemoveBreaks = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'removeBreaks',
            default: false,
            description: [
                'Sugoi Translator does not understand line breaks.',
                "This option replaces all line breaks with an space before translation. This hasn't been thoroughly tested, so there is no knowledge of whether this improves translation quality or not, but it should, in theory."
            ].join('\n'),
            name: 'Remove Line Breaks',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.OPTIONS
        }); */
        /**
         * Limits
         */
        this.optionMaxLength = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'maxTranslationLength',
            default: 5000,
            description: [
                'Maximum amount of characters that will be sent per server request.',
                "Sugoi translator can crash if you send text that is bigger than your RAM/VRAM can handle, so you can set an upper limit here. In general, the higher this number is, the faster the translation process will be - so long as you don't run out of memory. The default value is very conservative, feel free to increase it until your hardware cries.",
                "Note: if an atomic string (that can't be split) is larger than this amount, it will still be sent in full, but it will be sent alone."
            ].join('\n'),
            name: 'Request Character Limit',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -9
        });
        this.optionConcurrency = new TranslationEngineOption_1.TranslationEngineOption({
            wrapper: this,
            id: 'maxTranslationJobs',
            default: 1,
            description: 'Maximum amount of requests sent to the servers at the same time. The best number is the one that results in no downtime for the servers.',
            name: 'Maximum Request Count',
            category: TranslationEngineWrapper_1.TranslationEngineOptionCategories.LIMITS,
            priority: -8
        });
    }
    getUrl() {
        let urls = this.optionUrls.getValue().split(/\r?\n/g);
        if (this.urlUsage.length != urls.length) {
            this.urlUsage = new Array(urls.length).fill(0);
        }
        let leastUsed = this.urlUsage.indexOf(Math.min(...this.urlUsage));
        this.urlUsage[leastUsed]++;
        return {
            url: urls[leastUsed],
            index: leastUsed
        };
    }
    freeUrl(urlIndex) {
        this.urlUsage[urlIndex]--;
    }
    doTranslate(toTranslate, options) {
        this.resetStatus();
        let returnTranslations;
        let returnError;
        if (options.sl != 'ja') {
            this.error(`[SLR] The project specifies the source language as not being Japanese (${options.sl}). Since Sugoi Translator only supports Japanese as source, we will use Japanese instead.`);
        }
        if (options.tl != 'en') {
            this.error(`[SLR] The project specifies the destination language as not being English (${options.tl}). Since Sugoi Translator only supports English as destination, we will use English instead.`);
        }
        let translations = new Array(toTranslate.length);
        let translatingIndex = 0;
        let completeThreads = 0;
        let totalThreads = this.optionConcurrency.getValue();
        totalThreads = totalThreads < 1 ? 1 : totalThreads;
        let maximumBatchSize = this.optionMaxLength.getValue();
        let complete = () => {
            if (++completeThreads == totalThreads) {
                returnTranslations(translations);
            }
        };
        let translationProgress = document.createTextNode('0');
        let translatedCount = 0;
        let errorProgress = document.createTextNode('');
        let errorCount = 0;
        this.print(document.createTextNode('[SLR] Translating texts: '), translationProgress, document.createTextNode('/' + toTranslate.length), errorProgress);
        const updateTranslatedCount = (count) => {
            translatedCount += count;
            translationProgress.nodeValue = translatedCount.toString();
            options.progress(Math.round((100 * translatedCount) / toTranslate.length));
        };
        const updateErrorCount = (count) => {
            errorCount += count;
            errorProgress.nodeValue = ' (' + errorCount.toString() + ' failed translations)';
        };
        let serverScores = {};
        let startThread = () => {
            if (this.aborted) {
                returnError('Aborted.');
            }
            if (this.paused) {
                this.pauseQueue.push(() => {
                    startThread();
                });
                return;
            }
            let batchLength = 0;
            let batch = [];
            let batchIndexes = [];
            while (translatingIndex < toTranslate.length) {
                let index = translatingIndex;
                if (toTranslate[index] !== undefined &&
                    (batchLength == 0 ||
                        batchLength + toTranslate[index].length <= maximumBatchSize)) {
                    /* if (this.optionRemoveBreaks.getValue()) {
                        toTranslate[index] = toTranslate[index].replaceAll(/\r?\n/g, ' ');
                    } */
                    batch.push(toTranslate[index]);
                    batchIndexes.push(index);
                    batchLength += toTranslate[index].length;
                    translatingIndex++;
                }
                else {
                    break;
                }
            }
            if (batch.length == 0) {
                complete();
            }
            else {
                let myServer = this.getUrl();
                if (serverScores[myServer.url] == undefined) {
                    serverScores[myServer.url] = 0;
                }
                fetch(myServer.url, {
                    method: 'post',
                    body: JSON.stringify({ content: batch, message: 'translate sentences' }),
                    headers: { 'Content-Type': 'application/json' }
                })
                    .then(async (response) => {
                    if (response.ok) {
                        serverScores[myServer.url] += batch.length;
                        let result = await response.json();
                        console.log('[SLR] Fetched from ' + myServer.url, result);
                        if (result.length != batch.length) {
                            console.error('[SLR] MISMATCH ON RESPONSE:', batch, result);
                            throw new Error(`Received invalid response - length mismatch, check server stability.`);
                        }
                        else {
                            for (let i = 0; i < batch.length; i++) {
                                translations[batchIndexes[i]] = result[i];
                            }
                        }
                        updateTranslatedCount(batch.length);
                    }
                    else {
                        throw new Error(`${response.status.toString()} - ${response.statusText}`);
                    }
                })
                    .catch((error) => {
                    updateErrorCount(batch.length);
                    console.error('[SLR] ERROR ON FETCH USING ' + myServer, '   Payload: ' + batch.join('\n'), error);
                    this.error(`[SLR] Error while fetching from ${myServer.url} - ${error.name}: ${error.message}\n${' '.repeat(11)}If all fetch attempts fail on this server, check if it's still up.`);
                })
                    .finally(() => {
                    this.freeUrl(myServer.index);
                    startThread();
                });
            }
        };
        return new Promise((resolve, reject) => {
            returnTranslations = resolve;
            returnError = reject;
            for (let i = 0; i < totalThreads; i++) {
                startThread();
            }
        });
    }
    resetStatus() {
        this.aborted = false;
        this.paused = false;
        this.pauseQueue = [];
    }
    abort() {
        this.aborted = true;
    }
    pause() {
        this.paused = true;
    }
    resume() {
        this.paused = false;
        this.pauseQueue.forEach((action) => {
            action();
        });
        this.pauseQueue = [];
    }
}
exports.RedSugoiEngine = RedSugoiEngine;
Depending on how your Translation service works you might need more than a simple addon entry like that though.

The SLR dictionary and script protection stuff will not interfere with anything and you can use the wrapper, as long as the target language is still English.

Edit: But you would have to remove the checks for translatable Japanese in the "prepare for batch translation" button. Or the tagging gets messed up. You can find all the tagging related stuff in the transredbatch.js in SLRbatch.
The best way would probably not to remove the checks, but to change the regex constants to search for Chinese instead of Japanese.
View attachment 4210567
Hello,here is my custom api , only use in cases of translating Chinese to Vietnamese

JavaScript:
/**
 *
 * Used to run translation with Sangtacviet if there's an error with Gemini translating Chinese characters
 * @param {string} text
 * @returns {string} the translated text (converted) into Chinese
 */
async translateWithBackupSTV(text) {
  if (text.length == 0) {
    throw new Error("Cannot convert null string!");
  }

  const transUrl = new URL("https://sangtacviet.app/index.php");
  transUrl.searchParams.set("langhint", "chinese");

  let formData = new FormData();
  formData.append("sajax", "trans");
  formData.append("content", text);

  const response = await fetch(transUrl, { body: formData, method: "POST" });
  const translated = await response.text();
  return translated;
}
Could you suggest ideas on how to create a T++ style addon or engine entry for this API?
 

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Hello,here is my custom api , only use in cases of translating Chinese to Vietnamese

JavaScript:
/**
*
* Used to run translation with Sangtacviet if there's an error with Gemini translating Chinese characters
* @param {string} text
* @returns {string} the translated text (converted) into Chinese
*/
async translateWithBackupSTV(text) {
  if (text.length == 0) {
    throw new Error("Cannot convert null string!");
  }

  const transUrl = new URL("https://sangtacviet.app/index.php");
  transUrl.searchParams.set("langhint", "chinese");

  let formData = new FormData();
  formData.append("sajax", "trans");
  formData.append("content", text);

  const response = await fetch(transUrl, { body: formData, method: "POST" });
  const translated = await response.text();
  return translated;
}
Could you suggest ideas on how to create a T++ style addon or engine entry for this API?
No, for Chinese to Vietnamese to work with SLR Translator you would have to re-write a lot of the code.
The tool is designed for translations to English, and using it for something else is just going to be a broken mess.
Adding support for that is out of the current scope of the project.
Also because I neither speak Chinese nor Vietnamese, I would have no idea if it's even working correctly.
 
Oct 21, 2022
38
3
No, for Chinese to Vietnamese to work with SLR Translator you would have to re-write a lot of the code.
The tool is designed for translations to English, and using it for something else is just going to be a broken mess.
Adding support for that is out of the current scope of the project.
Also because I neither speak Chinese nor Vietnamese, I would have no idea if it's even working correctly.
So do you know any RPG maker mtl tool that can edit with custom api ?
Can i edit it on translator ++?
 

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
So do you know any RPG maker mtl tool that can edit with custom api ?
Can i edit it on translator ++?
I'm sorry, but while it would certainly be possible on Translator++, I cannot give any guidance there.

I've built SLR on top of the existing Sugoi and Google addons by Reddo9999 to get compatibility with the T++ UI.
As a result I've never actually built a T++ translation addon from scratch.
 
  • Like
Reactions: taodentumienque
Oct 21, 2022
38
3
I'm sorry, but while it would certainly be possible on Translator++, I cannot give any guidance there.

I've built SLR on top of the existing Sugoi and Google addons by Reddo9999 to get compatibility with the T++ UI.
As a result I've never actually built a T++ translation addon from scratch.
Is there a way to extract all the data in the 'original text' column into a file?
Then I will translate that file using my custom machine translation
Afterward, place the translated data back into the 'Initial' or 'Machine translation' column?
1731157277419.png
 

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
Is there a way to extract all the data in the 'original text' column into a file?
Then I will translate that file using my custom machine translation
Afterward, place the translated data back into the 'Initial' or 'Machine translation' column?
View attachment 4215288
I don't think that would work. Getting it out would be simple enough, but not getting it back in.

But there is actually functionality for that in MTool.
Load the game into MTool, start the game through MTool, switch to the "translate" tab, and then click on "Export the original text needs to be translate".

That will create a json called "ManualTransFile" with most of the translatable text. Once translated you can load it back into MTool using the "Load translation file" button.

But let's please not turn this thread into a general RPGM translation thread, this is supposed to be about SLR and its current scope.
 
  • Like
Reactions: taodentumienque

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
I've released v1.113.
Basically just a whole bunch of small fixes and some slightly better tagging for plugins.

Edit: BTW if you notice a cell that has untranslated text because the cell includes a "_", but you are sure that it should be translated, please post me the contents and context of the cell so I can adjust the escaper to let it through.
I purposely made it very broad right now so it rather doesn't translate something than crash.
 
Last edited:

Shisaye

Engaged Member
Modder
Dec 29, 2017
3,181
5,505
I've released v1.114.
Main change is that I added manual translations for the standard MV/MZ system messages.
Stuff like "Enemy has appeared!" "Obtained Item!" "Protag's party is victorious!" etc. I could have sworn I've done that before, but apparently they got lost at some point during some update.