Add lines of text to a file

Hi all,
I would like to use Node-Red to add foreign languages to my file written in C which looks like this:

static const char lg_ok_fr[] = {"Ok"};
static const char lg_ok_en[] = {"Ok"};
const char * lg_ok[] = {lg_ok_fr, lg_ok_en};

static const char lg_no_fr[] = {"Non"};
static const char lg_no_en[] = {"No"};
const char * lg_no[] = {lg_no_fr, lg_no_en};

static const char lg_yes_fr[] = {"Oui"};
static const char lg_yes_en[] = {"Yes"};
const char * lg_yes[] = {lg_yes_fr, lg_yes_en};

Today I would like to add the German that my friends will be happy to translate. (There are 600 same like lines, so that's why I can't do it by hand! :stuck_out_tongue_winking_eye:)

To do this I suggest :

1/ Can we first extract each String and save them in an array file like this:
[["Ok","Ok"], ["Non","No"], ["Oui","Yes"]]

2/ I give my file to my friend who adds his German translation:
[["Ok","Ok","Ok"], ["Non","No","Nicht"], ["Oui","Yes","Ja"]]

3/ we could create a second table with the title of the languages:
["fr","en","de"]
like that, if we add more languages, we will refer to this table of languages to know how many lines of languages to add

4/ you have to look for each new word and implement the language.c file like this:

static const char lg_yes_fr[] = {"Oui"};
static const char lg_yes_en[] = {"Yes"};
static const char lg_yes_de[] = {"Ja"};
const char * lg_yes[] = {lg_yes_fr, lg_yes_en, lg_yes_de};

we see xxxx_de[] has been added on the 3rd line, and xxx_de in the 3rd position in the moustaches

image

let lines = msg.payload.split("\n")
node.warn(lines)
lines.forEach((line, index) => {
    if (line.startsWith("static const char")&& line.endsWith("};")){
        lines[index] = "\n" ;
    }
})
msg.payload = lines.join("\n")
return msg;

I started doing this flow as you can see, I only managed to remove all the lines that start with static const char with a new line... then my skills suddenly crashed :face_with_spiral_eyes:!

A charitable soul to help me?
THANKS

It would be best to have a file per language, then load the file for the specific language, then when you add new languages you just need to add a new file. Eg load users specific locale, if not available load default language.

1 Like

unfortunately this file is created by another developer, and I can't ask him to rebuild everything :worried:

Sorry can not help you then, as why waste time adding to madness. my advise is spend the time correcting the mess, for future ease.

Just for info for others this would be one of the simplest ways. Create an object of word and what they should be
e.g.

{
    "gb": {
        "Ok": "Ok",
        "no": "no",
        "yes": "yes"
    }
}

then to add new language replacements, you add an new langauge section

{
    "gb": {
        "Ok": "Ok",
        "no": "no",
        "yes": "yes"
    },
    "fr": {
        "Ok": "Ok",
        "no": "non",
        "yes": "qui"
    }
}

Then in your code you can load the laguage you require

msg.language = msg.language[msg.locale];

Where msg.locale would hold the locale i.e. gb or fr
Then when you want to use the word Ok, no or yes ,you simple add

msg.language.OK

Example flow

[{"id":"c69aa3d5ec72345b","type":"inject","z":"b9860b4b9de8c8da","name":"","props":[{"p":"language","v":"{\"gb\":{\"Ok\":\"Ok\",\"no\":\"no\",\"yes\":\"yes\"},\"fr\":{\"Ok\":\"Ok\",\"no\":\"non\",\"yes\":\"qui\"}}","vt":"json"},{"p":"locale","v":"fr","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":400,"wires":[["3523e2da851538b4"]]},{"id":"3523e2da851538b4","type":"function","z":"b9860b4b9de8c8da","name":"function 23","func":"msg.language = msg.language[msg.locale];\n\nmsg.payload = `${msg.language.Ok}, ${msg.language.no}, ${msg.language.yes}`\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":400,"wires":[["ac9640f2921e025c"]]},{"id":"ac9640f2921e025c","type":"debug","z":"b9860b4b9de8c8da","name":"debug 334","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":550,"y":400,"wires":[]}]

thank you for this proposal (I would not fail to pass it on to my C developer for a next project). In the meantime, anyone else to help me?

I think this does what you want

[{"id":"576c1dd0f1e49712","type":"function","z":"8428164b1645b4b8","name":"function 1","func":"const file = `static const char lg_ok_fr[] = {\"Ok\"};\nstatic const char lg_ok_en[] = { \"Ok\"};\nconst char * lg_ok[] = { lg_ok_fr, lg_ok_en };\n\nstatic const char lg_no_fr[] = { \"Non\"};\nstatic const char lg_no_en[] = { \"No\"};\nconst char * lg_no[] = { lg_no_fr, lg_no_en };\n\nstatic const char lg_yes_fr[] = { \"Oui\"};\nstatic const char lg_yes_en[] = { \"Yes\"};\nconst char * lg_yes[] = { lg_yes_fr, lg_yes_en };`\n\nlet lines = file.split('\\n')\n\nnode.warn(lines)\n\nlet result = {}\nlet languageData = {}\nfor (let i = 0; i < lines.length; i++) {\n    if (lines[i].startsWith('static const char')) {\n        let translationStart = (lines[i].indexOf('{')) + 1\n        let translationEnd = (lines[i].indexOf('}'))\n        let translation = lines[i].slice(translationStart, translationEnd).replaceAll('\"', '').trim()\n\n\n        let dataStart = (lines[i].indexOf('lg_')) + 3\n        let dataEnd = (lines[i].indexOf('['))\n        let data = lines[i].slice(dataStart, dataEnd)\n\n        let language = data.split('_')[1]\n        let word = data.split('_')[0]\n\n        //languageData = { ...languageData, [word]: translation}\n        languageData = { [word]: translation }\n\n        result[language] = { ...result[language], ...languageData }\n\n    }\n\n}\n\nlet deTranslation = ['Ok', 'Nicht', 'Ja']\nlet words = Object.keys(result.en)\nlet data = {}\nfor (let i = 0; i < deTranslation.length; i++) {\n    data = { ...data, ...{[words[i]]: deTranslation[i] }}\n    result = { ...result, ...{'de': data}}\n}\n\n\nlet newFile = ''\nlet languages = Object.keys(result)\nfor (let i = 0; i < words.length; i++) {\n    for (let j = 0; j < languages.length; j++) {\n        newFile = newFile + '\\n' + `static const char lg_${words[i]}_${languages[j]}[] = {\"${result[languages[j]][words[i]]}\"};`\n    }\n    newFile = newFile + '\\n' + `const char * lg_${ words[i] } [] = {`\n\n    let addition = ''\n    for (let j = 0; j < languages.length - 1; j++) {\n        addition = addition + `lg_${words[i]}_${languages[j]},`\n    }\n    newFile = newFile + addition + `lg_${words[i]}_${languages[languages.length - 1]}};`\n}\n\nnode.warn(newFile)\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":680,"wires":[["b9a3b4d32add88bb"]]},{"id":"2b4937b94960567e","type":"inject","z":"8428164b1645b4b8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":380,"y":680,"wires":[["576c1dd0f1e49712"]]},{"id":"b9a3b4d32add88bb","type":"debug","z":"8428164b1645b4b8","name":"Lines Out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":680,"wires":[]}]

Edit: This is very rough and ready but may give you some ideas, can be cleaned up a LOT

Thanks Jeff @Buckskin for this flow. I try to decrypt with my weak knowledge. It works perfectly until I add a 6th paragraph of translation where everything gets mixed up.

static const char lg_ok_fr[] = {"Ok"};
static const char lg_ok_en[] = {"Ok"};
static const char lg_ok_l[] = {"undefined"};
static const char lg_ok_de[] = {"Ok"};
const char * lg_ok [] = {lg_ok_fr,lg_ok_en,lg_ok_l,lg_ok_de};

static const char lg_no_fr[] = {"Non"};
static const char lg_no_en[] = {"No"};
static const char lg_no_l[] = {"undefined"};
static const char lg_no_de[] = {"Nicht"};
const char * lg_no [] = {lg_no_fr,lg_no_en,lg_no_l,lg_no_de};

I do not understand why ?

The code of the Function node with the 6th paragraph which mixes everything:

const file = `
#include <asf.h>
#include "language.h"
//Divers
static const char lg_ok_fr[] = {"Ok"};
static const char lg_ok_en[] = {"Ok"};
const char * lg_ok[] = {lg_ok_fr, lg_ok_en};

static const char lg_no_fr[] = {"Non"};
static const char lg_no_en[] = {"No"};
const char * lg_no[] = {lg_no_fr, lg_no_en};

static const char lg_yes_fr[] = {"Oui"};
static const char lg_yes_en[] = {"Yes"};
const char * lg_yes[] = {lg_yes_fr, lg_yes_en};

static const char lg_off_fr[] = {"OFF"};
static const char lg_off_en[] = {"OFF"};
const char * lg_off[] = {lg_off_fr, lg_off_en};

static const char lg_on_fr[] = {"ON"};
static const char lg_on_en[] = {"ON"};
const char * lg_on[] = {lg_on_fr, lg_on_en};

static const char lg_unit_l_per_h_fr[] = {"L/h"};
static const char lg_unit_l_per_h_en[] = {"L/h"};
const char * lg_unit_l_per_h[] = {lg_unit_l_per_h_fr, lg_unit_l_per_h_en};

`


/*



static const char lg_unit_liter_fr[] = {"L"};
static const char lg_unit_liter_en[] = {"L"};
const char * lg_unit_liter[] = {lg_unit_liter_fr, lg_unit_liter_en};
	
static const char lg_unit_centi_liter_fr[] = {"cl"};
static const char lg_unit_centi_liter_en[] = {"cl"};
const char * lg_unit_centi_liter[] = {lg_unit_centi_liter_fr, lg_unit_centi_liter_en};

static const char lg_unit_h_hour_fr[] = {"h"};
static const char lg_unit_h_hour_en[] = {"h"};
const char * lg_unit_h_hour[] = {lg_unit_h_hour_fr, lg_unit_h_hour_en};

static const char lg_unit_s_second_fr[] = {"s"};
static const char lg_unit_s_second_en[] = {"s"};
const char * lg_unit_s_second[] = {lg_unit_s_second_fr, lg_unit_s_second_en};
	
static const char lg_unit_minute_fr[] = {"min"};
static const char lg_unit_minute_en[] = {"min"};
const char * lg_unit_minute[] = {lg_unit_minute_fr, lg_unit_minute_en};	

static const char lg_unit_j_jour_fr[] = {"j"};
static const char lg_unit_j_jour_en[] = {"d"};
const char * lg_unit_j_jour[] = {lg_unit_j_jour_fr, lg_unit_j_jour_en};

static const char lg_unit_ph_plus_fr[] = {"pH+"};
static const char lg_unit_ph_plus_en[] = {"pH+"};
const char * lg_unit_ph_plus[] = {lg_unit_ph_plus_fr, lg_unit_ph_plus_en};
*/

let lines = file.split('\n')

node.warn(lines)

let result = {}
let languageData = {}
for (let i = 0; i < lines.length; i++) {
    if (lines[i].startsWith('static const char')) {
        let translationStart = (lines[i].indexOf('{')) + 1
        //node.warn(translationStart)

        let translationEnd = (lines[i].indexOf('}'))
        let translation = lines[i].slice(translationStart, translationEnd).replaceAll('"', '').trim()

        let dataStart = (lines[i].indexOf('lg_')) + 3
        let dataEnd = (lines[i].indexOf('['))
        let data = lines[i].slice(dataStart, dataEnd)

        let language = data.split('_')[1]
        let word = data.split('_')[0]

        //languageData = { ...languageData, [word]: translation}
        languageData = { [word]: translation }

        result[language] = { ...result[language], ...languageData }
    }
}

let deTranslation = ['Ok', 'Nicht', 'Ja','AUS','AN','L/Std','L','cl','s','z','m','t','pH+']
let words = Object.keys(result.en)
let data = {}
for (let i = 0; i < deTranslation.length; i++) {
    data = { ...data, ...{[words[i]]: deTranslation[i] }}
    result = { ...result, ...{'de': data}}
}

let newFile = ''
let languages = Object.keys(result)
for (let i = 0; i < words.length; i++) {
    for (let j = 0; j < languages.length; j++) {
        newFile = newFile + '\n' + `static const char lg_${words[i]}_${languages[j]}[] = {"${result[languages[j]][words[i]]}"};`
    }
    newFile = newFile + '\n' + `const char * lg_${ words[i] } [] = {`

    let addition = ''
    for (let j = 0; j < languages.length - 1; j++) {
        addition = addition + `lg_${words[i]}_${languages[j]},`
    }
    newFile = newFile + addition + `lg_${words[i]}_${languages[languages.length - 1]}};`
}

//node.warn(newFile)
msg.payload=newFile;
return msg

@Buckskin , I found the problem:

let language = data.split('_')[1]         

it works fine if: static const char lg_yes_fr[] . So, data = "yes_fr" => language = "fr"
BUTnot work if: "static const char lg_unit_l_per_h_fr[]" So, data = "unit_l_per_h_fr" => language = "l"
it should be : language = "fr"

how to ignore the _ in between?

[EDIT]
the same thing for :

let word = data.split('_')[0]

if "static const char lg_unit_l_per_h_fr[]" So, data = "unit_l_per_h_fr" => word = "unit"
it should be : word= "unit_l_per_h"

Ahh, not included in first sample :grinning:

Try

const file = `static const char lg_ok_fr[] = {"Ok"};
static const char lg_ok_en[] = { "Ok"};
const char * lg_ok[] = { lg_ok_fr, lg_ok_en };

static const char lg_no_fr[] = { "Non"};
static const char lg_no_en[] = { "No"};
const char * lg_no[] = { lg_no_fr, lg_no_en };

static const char lg_yes_fr[] = { "Oui"};
static const char lg_yes_en[] = { "Yes"};
const char * lg_yes[] = { lg_yes_fr, lg_yes_en };

static const char lg_unit_l_per_h_fr[] = {"L/h"};
static const char lg_unit_l_per_h_en[] = {"L/h"};
const char * lg_unit_l_per_h[] = {lg_unit_l_per_h_fr, lg_unit_l_per_h_en};

static const char lg_unit_liter_fr[] = {"L"};
static const char lg_unit_liter_en[] = {"L"};
const char * lg_unit_liter[] = {lg_unit_liter_fr, lg_unit_liter_en};
	
static const char lg_unit_centi_liter_fr[] = {"cl"};
static const char lg_unit_centi_liter_en[] = {"cl"};
const char * lg_unit_centi_liter[] = {lg_unit_centi_liter_fr, lg_unit_centi_liter_en};

static const char lg_unit_h_hour_fr[] = {"h"};
static const char lg_unit_h_hour_en[] = {"h"};
const char * lg_unit_h_hour[] = {lg_unit_h_hour_fr, lg_unit_h_hour_en};

static const char lg_unit_s_second_fr[] = {"s"};
static const char lg_unit_s_second_en[] = {"s"};
const char * lg_unit_s_second[] = {lg_unit_s_second_fr, lg_unit_s_second_en};
	
static const char lg_unit_minute_fr[] = {"min"};
static const char lg_unit_minute_en[] = {"min"};
const char * lg_unit_minute[] = {lg_unit_minute_fr, lg_unit_minute_en};	

static const char lg_unit_j_jour_fr[] = {"j"};
static const char lg_unit_j_jour_en[] = {"d"};
const char * lg_unit_j_jour[] = {lg_unit_j_jour_fr, lg_unit_j_jour_en};

static const char lg_unit_ph_plus_fr[] = {"pH+"};
static const char lg_unit_ph_plus_en[] = {"pH+"};
const char * lg_unit_ph_plus[] = {lg_unit_ph_plus_fr, lg_unit_ph_plus_en};`

let lines = file.split('\n')

node.warn(lines)

let result = {}
let languageData = {}
for (let i = 0; i < lines.length; i++) {
    if (lines[i].startsWith('static const char')) {
        let translationStart = (lines[i].indexOf('{')) + 1
        let translationEnd = (lines[i].indexOf('}'))
        let translation = lines[i].slice(translationStart, translationEnd).replaceAll('"', '').trim()


        let dataStart = (lines[i].indexOf('lg_')) + 3
        let dataEnd = (lines[i].indexOf('['))
        let data = lines[i].slice(dataStart, dataEnd)

        let parts = data.split('_')

        let language = parts.pop()
        let word = parts.join('_')
 
        languageData = { [word]: translation }

        result[language] = { ...result[language], ...languageData }

    }

}

let deTranslation = ['Ok', 'Nicht', 'Ja', 'L/h']
let words = Object.keys(result.en)
let data = {}
for (let i = 0; i < deTranslation.length; i++) {
    data = { ...data, ...{[words[i]]: deTranslation[i] }}
    result = { ...result, ...{'de': data}}
}


let newFile = ''
let languages = Object.keys(result)
for (let i = 0; i < words.length; i++) {
    for (let j = 0; j < languages.length; j++) {
        newFile = newFile + '\n' + `static const char lg_${words[i]}_${languages[j]}[] = {"${result[languages[j]][words[i]]}"};`
    }
    newFile = newFile + '\n' + `const char * lg_${ words[i] } [] = {`

    let addition = ''
    for (let j = 0; j < languages.length - 1; j++) {
        addition = addition + `lg_${words[i]}_${languages[j]},`
    }
    newFile = newFile + addition + `lg_${words[i]}_${languages[languages.length - 1]}};`
    newFile = newFile + '\n'
}

node.warn(newFile)
return msg

Great work! I learn a lot with your code. THANK YOU
And yes there are pitfalls that come out little by little :smile:... here's another one when I thought I was done with everything : I would like to display the comment lines.

Do you have an idea ?

... and if it tells you... While waiting for your answer I noticed a \n in the file I want to modify:
static const char lg_clic1sec_cancel_en[] = {"Click 1sec:\nCancel"};.
As a result, the lines are cut and the final text incomplete::
static const char lg_clic1sec_cancel_en[] = {"Click 1sec"};

[EDIT] line breaks have been resolved and included in the code below.

We are nearing the end ! Here is your last modified code to add comment lines to the newFile:

let file = `
/*
 * language.c
 *
 * Created: 01/02/2022 15:15:09
 *  Author: 
 */ 

#include <asf.h>

#include "language.h"

//Divers
static const char lg_ok_fr[] = {"Ok"};
static const char lg_ok_en[] = {"Ok"};
const char * lg_ok[] = {lg_ok_fr, lg_ok_en};

static const char lg_no_fr[] = {"Non"};
static const char lg_no_en[] = {"No"};
const char * lg_no[] = {lg_no_fr, lg_no_en};

static const char lg_yes_fr[] = {"Oui"};
static const char lg_yes_en[] = {"Yes"};
const char * lg_yes[] = {lg_yes_fr, lg_yes_en};

//Annule 
static const char lg_clic1sec_cancel_fr[] = {"Clic 1sec:\nAnnule"};
static const char lg_clic1sec_cancel_en[] = {"Click 1sec:\nCancel"};
const char * lg_clic1sec_cancel[] = {lg_clic1sec_cancel_fr, lg_clic1sec_cancel_en};	


`
//replace & split pour ne pas avoir de saut de ligne dans le resultat
file = file.replace(/:\n/g, ":\\n");//remplace les :\n par des :\\n
//node.warn(file)
let lines = file.split(/(?<!\\)\n/)//sépare ligne par ligne avec les \n en saut de ligne
//node.warn(lines)//affiche lignes par ligne originales


node.warn(lines)

let result = {}
let languageData = {}
for (let i = 0; i < lines.length; i++) {
    if (lines[i].startsWith('static const char')) {
        let translationStart = (lines[i].indexOf('{')) + 1
        let translationEnd = (lines[i].indexOf('}'))
        let translation = lines[i].slice(translationStart, translationEnd).replaceAll('"', '').trim()


        let dataStart = (lines[i].indexOf('lg_')) + 3
        let dataEnd = (lines[i].indexOf('['))
        let data = lines[i].slice(dataStart, dataEnd)

        let parts = data.split('_')

        let language = parts.pop()
        let word = parts.join('_')
 
        languageData = { [word]: translation }

        result[language] = { ...result[language], ...languageData }

    }

}

let deTranslation = ['Ok', 'Nicht', 'Ja']
let words = Object.keys(result.en)
let data = {}
for (let i = 0; i < deTranslation.length; i++) {
    data = { ...data, ...{[words[i]]: deTranslation[i] }}
    result = { ...result, ...{'de': data}}
}


let newFile = ''
let languages = Object.keys(result)
for (let i = 0; i < words.length; i++) {
    for (let j = 0; j < languages.length; j++) {
        newFile = newFile + '\n' + `static const char lg_${words[i]}_${languages[j]}[] = {"${result[languages[j]][words[i]]}"};`
    }
    newFile = newFile + '\n' + `const char * lg_${ words[i] } [] = {`

    let addition = ''
    for (let j = 0; j < languages.length - 1; j++) {
        addition = addition + `lg_${words[i]}_${languages[j]},`
    }
    newFile = newFile + addition + `lg_${words[i]}_${languages[languages.length - 1]}};`
    newFile = newFile + '\n'
}
//if replace (at first)
//newFile = file.replace( /:::/g, ":\n")

//node.warn(newFile)
msg.payload = newFile
return msg

OK, rewritten. Seems to do all that is required and is a lot tidier. I have added 3 more function nodes

  1. Simulates reading of original file
  2. Simulates reading of translation file (includes the language code (de) and an array of translated terms.
  3. Outputs a list of terms to translate with the English translation already supplied as an example
[{"id":"2b4937b94960567e","type":"inject","z":"8428164b1645b4b8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":360,"y":740,"wires":[["9d5f907fd6f463aa"]]},{"id":"b9a3b4d32add88bb","type":"debug","z":"8428164b1645b4b8","name":"File Out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1220,"y":740,"wires":[]},{"id":"8ecf55a6ba007218","type":"function","z":"8428164b1645b4b8","name":"Creat File With \\n New Translation","func":"/**\n* Example of file input\n* /*\n* language.c\n*\n* Created: 01/02/2022 15:15:09\n*  Author:\n* \n* #include < asf.h >\n*\n*\n*    #include \"language.h\"\n*\n* //Divers\n* static const char lg_ok_fr[] = { \"Ok\"};\n* static const char lg_ok_en[] = { \"Ok\"};\n* const char * lg_ok[] = { lg_ok_fr, lg_ok_en };\n*\n* Note: Assumes that the language term translation array is in exactly the same order as the terms in the input file\n* msg.language  {string}    Example 'de'\n* msg.translation {Array}   Example ['Ok', 'Nicht', 'Ja', 'L/h', 'L', 'cl', 'h', 's', 'min', '', 'pH+', 'clic 1 sec:\\nStornieren']\n*\n*/\n\n// Read in File \nlet oldFile = msg.payload\n\n// Read in term translations\nconst language = msg.language\nconst translation = msg.translation\n\n// Has to be done this way as some lines have embedded '\\n'\noldFile = oldFile.replace(/:\\n/g, \":\\\\n\")\nlet fileLines = oldFile.split(/(?<!\\\\)\\n/)\n\n// Create array with new language translations\nlet result = []\nlet termCount = 0\nfor (let i = 0; i < fileLines.length; i++) {\n    if (fileLines[i].startsWith('const char *')) {\n\n        let dataStart = (fileLines[i].indexOf('lg_'))\n        let dataEnd = (fileLines[i].indexOf('['))\n        let data = fileLines[i].slice(dataStart, dataEnd)\n\n        result.push(`static const char ${data}_${language}[] = { \"${translation[termCount]}\" };`)\n        result.push(`${fileLines[i].slice(0, -2).trim()}, ${data}_${language} };`)\n\n        termCount++\n\n    } else {\n        result.push(fileLines[i])\n\n    }\n\n}\n\n// Tidy output format\nfor (let i = 0; i < result.length; i++) {\n    if (result[i].includes('{') && !result[i].includes('{ ')) {\n        result[i] = result[i].replace('{', '{ ')\n\n    }\n\n    if (result[i].includes('}') && !result[i].includes(' }')) {\n        result[i] = result[i].replace('}', ' }')\n\n    }\n\n}\n\nnode.warn(result)\n\n// create new file\nmsg.result = result\nmsg.payload = result.join('\\n')\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":740,"wires":[["b9a3b4d32add88bb"]]},{"id":"9d5f907fd6f463aa","type":"function","z":"8428164b1645b4b8","name":"Read In File To \\n Add Translation","func":"msg.payload = `\n/*\n * language.c\n *\n * Created: 01/02/2022 15:15:09\n *  Author: \n */ \n\n#include <asf.h>\n\n#include \"language.h\"\n\n//Divers\nstatic const char lg_ok_fr[] = {\"Ok\"};\nstatic const char lg_ok_en[] = { \"Ok\"};\nconst char * lg_ok[] = { lg_ok_fr, lg_ok_en };\n\nstatic const char lg_no_fr[] = { \"Non\"};\nstatic const char lg_no_en[] = { \"No\"};\nconst char * lg_no[] = { lg_no_fr, lg_no_en };\n\nstatic const char lg_yes_fr[] = { \"Oui\"};\nstatic const char lg_yes_en[] = { \"Yes\"};\nconst char * lg_yes[] = { lg_yes_fr, lg_yes_en };\n\nstatic const char lg_unit_l_per_h_fr[] = {\"L/h\"};\nstatic const char lg_unit_l_per_h_en[] = {\"L/h\"};\nconst char * lg_unit_l_per_h[] = {lg_unit_l_per_h_fr, lg_unit_l_per_h_en};\n\nstatic const char lg_unit_liter_fr[] = {\"L\"};\nstatic const char lg_unit_liter_en[] = {\"L\"};\nconst char * lg_unit_liter[] = {lg_unit_liter_fr, lg_unit_liter_en};\n\t\nstatic const char lg_unit_centi_liter_fr[] = {\"cl\"};\nstatic const char lg_unit_centi_liter_en[] = {\"cl\"};\nconst char * lg_unit_centi_liter[] = {lg_unit_centi_liter_fr, lg_unit_centi_liter_en};\n\nstatic const char lg_unit_h_hour_fr[] = {\"h\"};\nstatic const char lg_unit_h_hour_en[] = {\"h\"};\nconst char * lg_unit_h_hour[] = {lg_unit_h_hour_fr, lg_unit_h_hour_en};\n\nstatic const char lg_unit_s_second_fr[] = {\"s\"};\nstatic const char lg_unit_s_second_en[] = {\"s\"};\nconst char * lg_unit_s_second[] = {lg_unit_s_second_fr, lg_unit_s_second_en};\n\t\nstatic const char lg_unit_minute_fr[] = {\"min\"};\nstatic const char lg_unit_minute_en[] = {\"min\"};\nconst char * lg_unit_minute[] = {lg_unit_minute_fr, lg_unit_minute_en};\t\n\nstatic const char lg_unit_j_jour_fr[] = {\"j\"};\nstatic const char lg_unit_j_jour_en[] = {\"d\"};\nconst char * lg_unit_j_jour[] = {lg_unit_j_jour_fr, lg_unit_j_jour_en};\n\nstatic const char lg_unit_ph_plus_fr[] = {\"pH+\"};\nstatic const char lg_unit_ph_plus_en[] = {\"pH+\"};\nconst char * lg_unit_ph_plus[] = {lg_unit_ph_plus_fr, lg_unit_ph_plus_en};\n\n//Annule \nstatic const char lg_clic1sec_cancel_fr[] = {\"Clic 1sec:\\nAnnule\"};\nstatic const char lg_clic1sec_cancel_en[] = {\"Click 1sec:\\nCancel\"};\nconst char * lg_clic1sec_cancel[] = {lg_clic1sec_cancel_fr, lg_clic1sec_cancel_en};\n`\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":740,"wires":[["ed7d1a55800d3102","22c7a91cb2a97e03"]]},{"id":"ed7d1a55800d3102","type":"function","z":"8428164b1645b4b8","name":"Read In \\n Translation","func":"msg.language = 'de'\nmsg.translation = ['Ok', 'Nicht', 'Ja', 'L/h', 'L', 'cl', 'h', 's', 'min', 't', 'pH+', 'Klicken 1sec:\\\\nStornieren']\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":740,"wires":[["8ecf55a6ba007218"]]},{"id":"22c7a91cb2a97e03","type":"function","z":"8428164b1645b4b8","name":"Get Terms to \\n Translate","func":"\n// Read in File \nlet file = msg.payload\n\n// Has to be done this way as some lines have embedded '\\n'\nfile = file.replace(/:\\n/g, \":\\\\n\")\nlet fileLines = file.split(/(?<!\\\\)\\n/)\n\n// Create array with terms to be translated\nlet result = []\nlet translation = ''\nfor (let i = 0; i < fileLines.length; i++) {\n    if (fileLines[i].startsWith('static const char') && fileLines[i].includes('en')) {         \n        let translationStart = (fileLines[i].indexOf('{')) + 1\n        let translationEnd = (fileLines[i].indexOf('}'))\n        translation = fileLines[i].slice(translationStart, translationEnd).replaceAll('\"', '').trim()\n\n    }\n\n    if (fileLines[i].startsWith('const char *')) {\n        let dataStart = (fileLines[i].indexOf('lg_')) + 3\n        let dataEnd = (fileLines[i].indexOf('['))\n        let term = fileLines[i].slice(dataStart, dataEnd)\n\n        term = term.replaceAll('_', ' ')\n\n        result.push(`Term: ${term}, English: ${translation}`)\n\n    }\n\n}\n\nnode.warn(result)\n\nmsg = {}\nmsg.payload = result\n\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":660,"wires":[["afe32bafb2260001"]]},{"id":"afe32bafb2260001","type":"debug","z":"8428164b1645b4b8","name":"Term List","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":960,"y":660,"wires":[]}]

Edit. Updated to include splits taking embedded \n into account

1 Like

it sounds really very good, it's great. I will scrutinize all this to understand and above all to learn. thank you for your help @Buckskin . :slightly_smiling_face:

Update, I forgot to take into account 'escape' codes in the translations. See line added to code below

for (let i = 0; i < fileLines.length; i++) {
    if (fileLines[i].startsWith('const char *')) {

        let dataStart = (fileLines[i].indexOf('lg_'))
        let dataEnd = (fileLines[i].indexOf('['))
        let data = fileLines[i].slice(dataStart, dataEnd)

        // Deal with any escape codes in the translation
        translation[termCount] = translation[termCount].replace(/:\n/g, ":\\n")

        result.push(`static const char ${data}_${language}[] = { "${translation[termCount]}" };`)
        result.push(`${fileLines[i].slice(0, -2).trim()}, ${data}_${language} };`)

        termCount++

    } else {
        result.push(fileLines[i])

    }

}

Weird :
TypeError: Cannot read properties of undefined (reading 'replace')
yet translation =

["Ok","Nicht","Ja","L/h","L","cl","h","s","min","t","pH+","Klicken 1sec:\\nStornieren"]

and termCount = Number
i don't understand ?

Not sure about error, can you show me the full contents of your copy of the function node? as this is all working for me.

Note: with the addition of the last edit translation[termCount] = translation[termCount].replace(/:\n/g, ":\\n") the translation no longer requires a '\\n' to deal with escape codes. (Example "Klicken 1sec:\\nStornieren" now becomes "Klicken 1sec:\nStornieren")

termCount - this points to the current entry in the translation array as I am using the 'const char *' as a pointer to where each translation should be added and i counts every line in the file.

The cause is there: it's because I have more rows to translate than the table has any translation. I will handle this.

thanks again :+1:

Just put and empty string in the translation array and check before adding (or just accept an empty string as the translation) :wink:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.