# Add lines of text to a file

**URL:** <https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745>\
**Category:** General\
**Tags:** function-node\
**Created:** [20 April 2023 14:27 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745 "2023-04-20T14:27:13Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [20 April 2023 14:27 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/1 "2023-04-20T14:27:13Z")

</div>

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

```auto
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! 😜)

**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:**

```auto
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](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/5/25354e01aa0b987a9b49fdaa6049540cce4656d6.png)

```auto
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 😵‍💫!

A charitable soul to help me?  
THANKS

---

<div class="post-metadata">

**Author:** ![E1cid](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/e1cid/32/77971_2.png) [@E1cid](https://discourse.nodered.org/u/E1cid)\
**Post date:** [20 April 2023 15:05 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/2 "2023-04-20T15:05:35Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [20 April 2023 15:12 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/3 "2023-04-20T15:12:22Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![E1cid](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/e1cid/32/77971_2.png) [@E1cid](https://discourse.nodered.org/u/E1cid)\
**Post date:** [20 April 2023 15:30 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/4 "2023-04-20T15:30:01Z")

</div>

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.

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

```

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

```auto
{
    "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

```auto
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

```auto
msg.language.OK

```

Example flow

```auto
[{"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":[]}]

```

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [21 April 2023 07:55 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/5 "2023-04-21T07:55:30Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [21 April 2023 22:25 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/6 "2023-04-21T22:25:01Z")

</div>

I think this does what you want

```auto
[{"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

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [24 April 2023 08:08 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/7 "2023-04-24T08:08:22Z")

</div>

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.

```auto
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:

```auto
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

```

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [24 April 2023 09:45 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/8 "2023-04-24T09:45:34Z")

</div>

@Buckskin , I found the problem:

```auto
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 :

```auto
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"`

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [25 April 2023 17:39 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/9 "2023-04-25T17:39:04Z")

</div>

Ahh, not included in first sample 😀

Try

```auto
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

```

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [26 April 2023 08:26 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/10 "2023-04-26T08:26:22Z")

</div>

Great work! I learn a lot with your code. THANK YOU  
And yes there are pitfalls that come out little by little 😄... 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"};`

> [@How to tell the difference between \\n in a string and a newline?](https://discourse.nodered.org/t/how-to-tell-the-difference-between-n-in-a-string-and-a-newline/77922/9):
>
> Try let lines = file.split(/(?\<!:)\n/) But it would be best to correct the code and remove non valid property characters from your property names, then you would not have to patch the issue. Just for info const foo = file.replace( ":\n", ":::") will only replace first occurance. it should be const foo = file.replace( /:\n/g, ":::") for multiple replaces.

**[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:

```auto
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

```

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [27 April 2023 15:34 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/11 "2023-04-27T15:34:22Z")

</div>

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

```auto
[{"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

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [28 April 2023 06:48 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/12 "2023-04-28T06:48:14Z")

</div>

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 . 🙂

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [28 April 2023 11:43 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/13 "2023-04-28T11:43:00Z")

</div>

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

```auto
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])

    }

}

```

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [28 April 2023 12:41 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/14 "2023-04-28T12:41:21Z")

</div>

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

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

```

and termCount = Number  
i don't understand ?

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [28 April 2023 13:55 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/15 "2023-04-28T13:55:46Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![SuperNinja](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/superninja/32/5761_2.png) [@SuperNinja](https://discourse.nodered.org/u/SuperNinja)\
**Post date:** [2 May 2023 07:38 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/16 "2023-05-02T07:38:49Z")

</div>

> [@Buckskin](#):
>
> termCount - this points to the current entry in the translation array as I am using the as a pointer to where each translation should be added and counts every line in the file.`'const char *'` `i`

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 👍

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [2 May 2023 17:42 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/17 "2023-05-02T17:42:04Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [16 May 2023 17:42 UTC](https://discourse.nodered.org/t/add-lines-of-text-to-a-file/77745/18 "2023-05-16T17:42:44Z")

</div>

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