diff --git a/docs/docs/content/i18n.md b/docs/docs/content/i18n.md index 2b3b0a630..633481237 100644 --- a/docs/docs/content/i18n.md +++ b/docs/docs/content/i18n.md @@ -17,9 +17,17 @@ To customize an existing language or to load a new language, put one or more `.j ## Contributing a new language +### Using the basic editor + - Visit [https://listmonk.app/i18n](https://listmonk.app/i18n) -- To make changes to an existing language, use the "Load language" option on the top right. -- To create a new language, use the "Load language" option on the top right and select "Default". -- Translate the text in the input fields on the UI. -- Once done, use the `Switch to raw JSON` and copy the JSON data and save it to a file named `xx.json`, where `xx` is the two letter code of the language. -- Send a pull request to add the file to the [i18n directory on the GitHub repo](https://github.com/knadh/listmonk/tree/master/i18n). If you are not familiar with pull requests, share the file by creating a new "issue" (comment) on the [GitHub issues page](https://github.com/knadh/listmonk/issues) +- Click on `Createa new language`, or to make changes to an existing language, use `Load language`. +- Translate the text in the text fields on the UI. +- Once done, use the `Download raw JSON` to download the language file. +- Send a pull request to add the file to the [i18n directory on the GitHub repo](https://github.com/knadh/listmonk/tree/master/i18n). + +### Using InLang (external service) + +- Visit [https://inlang.com/editor/github.com/knadh/listmonk](https://inlang.com/editor/github.com/knadh/listmonk) +- To make changes and push them, you need to log in to GitHub using OAuth and fork the project from the UI. +- Translate the text in the input fields on the UI. You can use the filters to see only the necessary translations. +- Once you're done, push the changes from the UI and click on "Open a pull request." This will take you to GitHub, where you can write a PR message. diff --git a/docs/i18n/index.html b/docs/i18n/index.html index 3afcea136..e45a573ac 100644 --- a/docs/i18n/index.html +++ b/docs/i18n/index.html @@ -13,7 +13,11 @@

{{ values["_.name"] }}

- Switch to raw JSON + + + + + Download raw JSON Switch to editor
@@ -33,7 +37,7 @@

{{ values["_.name"] }}

- Load language + Load existing language + +     + + Create new language
diff --git a/docs/i18n/main.js b/docs/i18n/main.js index 65f52c787..985915bf0 100644 --- a/docs/i18n/main.js +++ b/docs/i18n/main.js @@ -23,41 +23,41 @@ var app = new Vue({ loadBaseLang(url) { return fetch(url).then(response => response.json()).then(data => { - // Retain the base values. - Object.assign(this.base, data); - - // Get the sorted keys from the language map. - const keys = []; - const visibleKeys = {}; - let head = null; - Object.entries(this.base).sort((a, b) => a[0].localeCompare(b[0])).forEach((v) => { - const h = v[0].split('.')[0]; - keys.push({ - "key": v[0], - "head": (head !== h ? h : null) // eg: campaigns on `campaigns.something.else` - }); - - visibleKeys[v[0]] = true; - head = h; + // Retain the base values. + Object.assign(this.base, data); + + // Get the sorted keys from the language map. + const keys = []; + const visibleKeys = {}; + let head = null; + Object.entries(this.base).sort((a, b) => a[0].localeCompare(b[0])).forEach((v) => { + const h = v[0].split('.')[0]; + keys.push({ + "key": v[0], + "head": (head !== h ? h : null) // eg: campaigns on `campaigns.something.else` }); - this.keys = keys; - this.visibleKeys = visibleKeys; - this.values = { ...this.base }; - - // Is there cached localStorage data? - if(localStorage.data) { - try { - this.loadData(JSON.parse(localStorage.data)); - } catch(e) { - console.log("Bad JSON in localStorage: " + e.toString()); - } - return; + visibleKeys[v[0]] = true; + head = h; + }); + + this.keys = keys; + this.visibleKeys = visibleKeys; + this.values = { ...this.base }; + + // Is there cached localStorage data? + if (localStorage.data) { + try { + this.populateData(JSON.parse(localStorage.data)); + } catch (e) { + console.log("Bad JSON in localStorage: " + e.toString()); } + return; + } }); }, - loadData(data) { + populateData(data) { // Filter out all keys from data except for the base ones // in the base language. const vals = this.keys.reduce((a, key) => { @@ -69,6 +69,15 @@ var app = new Vue({ this.saveData(); }, + loadLanguage(lang) { + return fetch(BASEURL + lang + ".json").then(response => response.json()).then(data => { + this.populateData(data); + }).catch((e) => { + console.log(e); + alert("error fetching file: " + e.toString()); + }); + }, + saveData() { localStorage.data = JSON.stringify(this.values); }, @@ -87,7 +96,7 @@ var app = new Vue({ this.rawData = JSON.stringify(this.values, Object.keys(this.values).sort(), 4); } else { try { - this.loadData(JSON.parse(this.rawData)); + this.populateData(JSON.parse(this.rawData)); } catch (e) { alert("error parsing JSON: " + e.toString()); return false; @@ -98,16 +107,37 @@ var app = new Vue({ }, onLoadLanguage() { - if(!confirm("Loading this language will overwrite your local changes. Continue?")) { + if (!confirm("Loading this language will overwrite your local changes. Continue?")) { return false; } - fetch(BASEURL + this.loadLang + ".json").then(response => response.json()).then(data => { - this.loadData(data); - }).catch((e) => { - console.log(e); - alert("error fetching file: " + e.toString()); - }); + this.loadLanguage(this.loadLang); + }, + + onNewLang() { + if (!confirm("Creating a new language will overwrite your local changes. Continue?")) { + return false; + } + + let data = { ...this.base }; + data["_.code"] = "iso-code-here" + data["_.name"] = "New language" + this.populateData(data); + }, + + onDownloadJSON() { + // Create a Blob using the content, mimeType, and optional encoding + const blob = new Blob([JSON.stringify(this.values, Object.keys(this.values).sort(), 4)], { type: "" }); + + // Create an anchor element with a download attribute + const link = document.createElement('a'); + link.download = `${this.values["_.code"]}.json`; + link.href = URL.createObjectURL(blob); + + // Append the link to the DOM, click it to start the download, and remove it + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); } }, @@ -128,10 +158,10 @@ var app = new Vue({ if (v === "pending") { visible = !this.isDone(k.key); } else if (v === "complete") { - visible = this.isDone(k.key); + visible = this.isDone(k.key); } - if(visible) { + if (visible) { visibleKeys[k.key] = true; } }); @@ -145,7 +175,7 @@ var app = new Vue({ let n = 0; this.keys.forEach(k => { - if(this.values[k.key] !== this.base[k.key]) { + if (this.values[k.key] !== this.base[k.key]) { n++; } }); diff --git a/docs/i18n/style.css b/docs/i18n/style.css index 23d1da31c..7d8e5e0c7 100644 --- a/docs/i18n/style.css +++ b/docs/i18n/style.css @@ -12,6 +12,10 @@ h1, h2, h3, h4, h5 { margin: 0 0 15px 0; } +a { + color: #0055d4; +} + .container { padding: 30px; } @@ -20,6 +24,10 @@ h1, h2, h3, h4, h5 { align-items: center; margin-bottom: 30px; } + .header a { + display: inline-block; + margin-right: 15px; + } .header .controls { display: flex; }