From d099e5e84e191d67a7bffa574ab39b6d0d4f6adf Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 12 Oct 2025 22:49:47 +0000 Subject: [PATCH] tl-split: draw the rest of the owl --- CONTRIBUTING.md | 2 +- copyparty/__init__.py | 19 ++++++++++ copyparty/httpcli.py | 4 +- copyparty/web/Makefile | 4 +- copyparty/web/browser.html | 3 ++ copyparty/web/browser.js | 76 +++++++++++++++++++++++--------------- copyparty/web/splash.html | 3 ++ copyparty/web/splash.js | 11 ++---- copyparty/web/util.js | 9 +++++ docs/devnotes.md | 1 + docs/rice/README.md | 18 ++------- pyproject.toml | 2 + scripts/make-sfx.sh | 10 +++-- scripts/sfx.ls | 20 ++++++++++ scripts/tl.py | 76 +++++++++++++++----------------------- scripts/tlcheck.sh | 12 +++--- setup.py | 1 + 17 files changed, 159 insertions(+), 112 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a067404f..65f5319e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ if you wanna have a go at coding it up yourself then maybe mention the idea on d aside from documentation and ideas, some other things that would be cool to have some help with is: -* **translations** -- the copyparty web-UI has translations for english and norwegian at the top of [browser.js](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/web/browser.js); if you'd like to add a translation for another language then that'd be welcome! and if that language has a grammar that doesn't fit into the way the strings are assembled, then we'll fix that as we go :> +* **translations** -- the copyparty web-UI has translations in [copyparty/web/tl](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl); if you'd like to [add a translation](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#translations) for another language then that'd be welcome! and if that language has a grammar that doesn't fit into the way the strings are assembled, then we'll fix that as we go :> * but please note that support for [RTL (Right-to-Left) languages](https://en.wikipedia.org/wiki/Right-to-left_script) is currently not planned, since the javascript is a bit too jank for that diff --git a/copyparty/__init__.py b/copyparty/__init__.py index eba3651e..44ddd3c9 100644 --- a/copyparty/__init__.py +++ b/copyparty/__init__.py @@ -100,6 +100,25 @@ web/splash.html web/splash.js web/svcs.html web/svcs.js +web/tl/chi.js +web/tl/cze.js +web/tl/deu.js +web/tl/epo.js +web/tl/fin.js +web/tl/fra.js +web/tl/grc.js +web/tl/ita.js +web/tl/kor.js +web/tl/nld.js +web/tl/nno.js +web/tl/nor.js +web/tl/pol.js +web/tl/por.js +web/tl/rus.js +web/tl/spa.js +web/tl/swe.js +web/tl/tur.js +web/tl/ukr.js web/ui.css web/up2k.js web/util.js diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index ed5ac993..9020db3a 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -275,7 +275,7 @@ class HttpCli(object): tpl = self.conn.hsrv.j2[name] ka["r"] = self.args.SR if self.is_vproxied else "" ka["ts"] = self.conn.hsrv.cachebuster() - ka["lang"] = self.args.lang + ka["lang"] = self.cookies.get("cplng") or self.args.lang ka["favico"] = self.args.favico ka["s_doctitle"] = self.args.doctitle ka["tcolor"] = self.vn.flags["tcolor"] @@ -5077,7 +5077,7 @@ class HttpCli(object): "edit": "edit" in self.uparam, "title": html_escape(self.vpath, crlf=True), "lastmod": int(ts_md * 1000), - "lang": self.args.lang, + "lang": self.cookies.get("cplng") or self.args.lang, "favico": self.args.favico, "have_emp": int(self.args.emp), "md_no_br": int(vn.flags.get("md_no_br") or 0), diff --git a/copyparty/web/Makefile b/copyparty/web/Makefile index 3989e931..99fd53f2 100644 --- a/copyparty/web/Makefile +++ b/copyparty/web/Makefile @@ -2,8 +2,8 @@ # which should help on really slow connections # but then why are you using copyparty in the first place -pk: $(addsuffix .gz, $(wildcard *.js *.css)) -un: $(addsuffix .un, $(wildcard *.gz)) +pk: $(addsuffix .gz, $(wildcard tl/*.js *.js *.css)) +un: $(addsuffix .un, $(wildcard tl/*.gz *.gz)) %.gz: % pigz -11 -J 34 -I 573 $< diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index 2201ab58..9470465b 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -145,6 +145,9 @@ document.documentElement.className = (STG && STG.cpp_thm) || dtheme; + {%- if lang != "eng" %} + + {%- endif %} diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 4ad645c4..74fcdac5 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -3,10 +3,8 @@ var XHR = XMLHttpRequest, img_re = /\.(a?png|avif|bmp|gif|heif|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov)(\?|$)/i; -// please add translations in alphabetic order, but keep "eng" and "nor" first -// (lines ending with //m are machine translations) -var Ls = { - "eng": { +if (1) + Ls.eng = { "tt": "English", "cols": { @@ -26,7 +24,7 @@ var Ls = { "resw": "horizontal resolution", "resh": "vertical resolution", "chs": "audio channels", - "hz": "sample rate" + "hz": "sample rate", }, "hks": [ @@ -639,18 +637,43 @@ var Ls = { "lang_set": "refresh to make the change take effect?", }; -var LANGS = ["eng", "nor", "chi", "cze", "deu", "epo", "fin", "fra", "grc", "ita", "kor", "nld", "nno", "pol", "por", "rus", "spa", "swe", "tur", "ukr"]; +var LANGN = [ + ["eng", "English"], + ["nor", "Norsk"], + ["chi", "中文"], + ["cze", "Čeština"], + ["deu", "Deutsch"], + ["epo", "Esperanto"], + ["fin", "Suomi"], + ["fra", "français"], + ["grc", "Ελληνικά"], + ["ita", "Italiano"], + ["kor", "한국어"], + ["nld", "Nederlands"], + ["nno", "Nynorsk"], + ["pol", "Polski"], + ["por", "Português"], + ["rus", "Русский"], + ["spa", "Español"], + ["swe", "Svenska"], + ["tur", "Türkçe"], + ["ukr", "Українська"], +]; if (window.langmod) langmod(); -for (var a = LANGS.length; a > 0;) - if (!Ls[LANGS[--a]]) - LANGS.splice(a, 1); +var L = Ls[lang] || Ls.eng, LANGS = []; +for (var a = 0; a < LANGN.length; a++) + LANGS.push(LANGN[a][0]); -var L = Ls[sread("cpp_lang", LANGS) || lang] || - Ls.eng || Ls.nor || Ls.chi; +function langtest() { + var n = LANGS.length - 1; + for (var a = 1; a < LANGS.length; a++) + import_js(SR + '/.cpr/tl/' + LANGS[a] + '.js', function () { if (!--n) langtest2(); }); +} +function langtest2() { for (var a = 0; a < LANGS.length; a++) { for (var b = a + 1; b < LANGS.length; b++) { var i1 = Object.keys(Ls[LANGS[a]]).length > Object.keys(Ls[LANGS[b]]).length ? a : b, @@ -665,8 +688,11 @@ for (var a = 0; a < LANGS.length; a++) { } } } +} -if (!has(LANGS, lang)) + + +if (!Ls[lang]) alert('unsupported --lang "' + lang + '" specified in server args;\nplease use one of these: ' + LANGS); modal.load(); @@ -6581,9 +6607,7 @@ var treectl = (function () { bcfg_bind(r, 'csel', 'csel', dgsel); bcfg_bind(r, 'dots', 'dotfiles', see_dots, function (v) { r.goto(); - var xhr = new XHR(); - xhr.open('GET', SR + '/?setck=dots=' + (v ? 'y' : ''), true); - xhr.send(); + setck('dots=' + (v ? 'y' : '')); }); bcfg_bind(r, 'utctid', 'utctid', dutc, function (v) { window.unix2ui = v ? unix2iso : unix2iso_localtime; @@ -6620,9 +6644,7 @@ var treectl = (function () { if (!v == !/\bidxh=y\b/.exec('' + document.cookie)) return; - var xhr = new XHR(); - xhr.open('GET', SR + '/?setck=idxh=' + (v ? 'y' : 'n'), true); - xhr.send(); + setck('idxh=' + (v ? 'y' : 'n')); } setidxh(r.idxh); @@ -7368,10 +7390,7 @@ var treectl = (function () { qsr('#bbsw'); srvinf = ebi('srv_info').innerHTML.slice(6, -7); if (ls0 === null) { - var xhr = new XHR(); - xhr.open('GET', SR + '/?setck=js=y', true); - xhr.send(); - + setck('js=y'); r.ls_cb = showfile.addlinks; return r.reqls(get_evpath(), false, undefined, true); } @@ -8164,11 +8183,9 @@ var setfszf = (function () { (function () { function freshen() { - lang = sread("cpp_lang", LANGS) || lang; - var k, cb = ebi('langs'), html = []; - for (var a = 0; a < LANGS.length; a++) { - k = LANGS[a]; - html.push(''.format(k, Ls[k].tt)); + var cb = ebi('langs'), html = []; + for (var a = 0; a < LANGN.length; a++) { + html.push(''.format(LANGN[a][0], LANGN[a][1])); } cb.innerHTML = html.join(''); cb.onchange = setlang; @@ -8177,11 +8194,10 @@ var setfszf = (function () { function setlang(e) { ev(e); - var t = L.lang_set; lang = ebi('langs').value; - L = Ls[lang]; - swrite("cpp_lang", lang); + setck('cplng=' + lang); freshen(); + var t = L.tt == 'English' ? '' : Ls.eng.lang_set; modal.confirm(L.lang_set + "\n\n" + t, location.reload.bind(location), null); } diff --git a/copyparty/web/splash.html b/copyparty/web/splash.html index e98d5f63..1dd635d6 100644 --- a/copyparty/web/splash.html +++ b/copyparty/web/splash.html @@ -211,6 +211,9 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme +{%- if lang != "eng" %} + +{%- endif %} {%- if js %} diff --git a/copyparty/web/splash.js b/copyparty/web/splash.js index 11bb714b..6185f341 100644 --- a/copyparty/web/splash.js +++ b/copyparty/web/splash.js @@ -1,7 +1,5 @@ -// please add translations in alphabetic order, but keep "nor" and "eng" first -// (lines ending with //m are machine translations) -var Ls = { - "eng": { +Ls.eng = { + "splash": { "d2": "shows the state of all active threads", "e2": "reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes$N$Nnote: any changes to global settings$Nrequire a full restart to take effect", "lo2": "ends the session on all browsers", @@ -10,14 +8,13 @@ var Ls = { "ta1": "fill in your new password first", "ta2": "repeat to confirm new password:", "ta3": "found a typo; please try again", - }, + } }; if (window.langmod) langmod(); -var d = Ls[sread("cpp_lang", Object.keys(Ls)) || lang] || - Ls.eng || Ls.nor || Ls.chi; +var d = (Ls[lang] || Ls.eng).splash; d.wb = d.w; diff --git a/copyparty/web/util.js b/copyparty/web/util.js index ee8e5b98..9a97b32a 100644 --- a/copyparty/web/util.js +++ b/copyparty/web/util.js @@ -296,6 +296,10 @@ function ignex(all) { window.onerror = vis_exh; +if (!window.Ls || !window.langmod) + var Ls = {}; + + function noop() { } @@ -1307,6 +1311,11 @@ function scfg_bind(obj, oname, cname, defval, cb) { return v; } +function setck(v) { + var xhr = new XHR(); + xhr.open('GET', SR + '/?setck=' + v, true); + xhr.send(); +} window.unix2ui = (function () { var v = sread('utctid'); diff --git a/docs/devnotes.md b/docs/devnotes.md index 50bafc89..140f6bbf 100644 --- a/docs/devnotes.md +++ b/docs/devnotes.md @@ -415,6 +415,7 @@ if you are unable to use `build`, you can use the old setuptools approach instea ```bash python3 setup.py install --user setuptools wheel jinja2 python3 setup.py build +python3 setup.py bdist_wheel # you now have a wheel which you can install. or extract and repackage: python3 setup.py install --skip-build --prefix=/usr --root=$HOME/pe/copyparty ``` diff --git a/docs/rice/README.md b/docs/rice/README.md index 3d3b7cfe..4573a085 100644 --- a/docs/rice/README.md +++ b/docs/rice/README.md @@ -69,23 +69,13 @@ there is also `--html-head-s` and volflag `html_head_s` to add a plain static bi # translations -add your own translations by using the english or norwegian one from `browser.js` as a template +add your own translations by using [tl.js](https://github.com/9001/copyparty/blob/hovudstraum/scripts/tl.js) as a base, and add a new file in [copyparty/web/tl](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl) when you're happy with it > ⚠ Please do not contribute translations to [RTL (Right-to-Left) languages](https://en.wikipedia.org/wiki/Right-to-left_script) for now; the javascript is [not ready](https://github.com/9001/copyparty/blob/hovudstraum/docs/rice/rtl.patch) to deal with it -the easy way is to open up and modify `browser.js` in your own installation; depending on how you installed copyparty it might be named `browser.js.gz` instead, in which case just decompress it, restart copyparty, and start editing it anyways - you will be delighted to see inline html in the translation strings; to help prevent syntax errors, there is [a very jank linux script](https://github.com/9001/copyparty/blob/hovudstraum/scripts/tlcheck.sh) which is slightly better than nothing -- just beware the false-positives, so even if it complains it's not necessarily wrong/bad -if you're running `copyparty-sfx.py` then you'll find it at `/tmp/pe-copyparty.1000/copyparty/web` (on linux) or `%TEMP%\pe-copyparty\copyparty\web` (on windows) -* make sure to keep backups of your work religiously! since that location is volatile af - - -## translations (docker-friendly) - -if editing `browser.js` is inconvenient in your setup, for example if you're running in docker, then you can instead do this: -* if you have python, go to the `scripts` folder and run `./tl.py fra Français` to generate a `tl.js` which is perfect for translating to French, using the three-letter language code `fra` - * if you do not have python, you can also just grab `tl.js` from the scripts folder, but I'll probably forget to keep that up to date... and then you'll have to find/replace all `"eng"` and `Ls.eng` to your three-letter language code +to see your translation taking shape in the copyparty ui as you work on it: * put your `tl.js` inside a folder that is being shared by your copyparty, preferably the webroot * run copyparty with the argument `--html-head=''` * if you placed `tl.js` in the webroot then you're all good, but if you put it somewhere else then change `/tl.js` accordingly @@ -97,6 +87,4 @@ if editing `browser.js` is inconvenient in your setup, for example if you're run you can now edit `tl.js` and press CTRL-SHIFT-R in the browser to see your changes take effect as you go -if you want to contribute your translation back to the project (please do!) then you'll want to... -* grab all of the text inside your `var tl_cpanel = {` and add it to the translations inside `copyparty/web/splash.js` in the repo -* and the text inside your `var tl_browser = {` and add that to the translations inside `copyparty/web/browser.js` in the repo +if you want to contribute your translation back to the project (please do!) then grab most of the text inside your `tl.js` , starting from the line that starts with `Ls.` and put it into a new file inside [the translations folder](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/web/tl) diff --git a/pyproject.toml b/pyproject.toml index 54668406..6041a3ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,8 @@ copyparty = [ "web/*.css", "web/*.html", "web/*.xml", + "web/tl/*.js", + "web/tl/*.gz", "web/a/*.bat", "web/deps/*.gz", "web/deps/*.woff*", diff --git a/scripts/make-sfx.sh b/scripts/make-sfx.sh index 592b9096..e9aa80fb 100755 --- a/scripts/make-sfx.sh +++ b/scripts/make-sfx.sh @@ -481,10 +481,12 @@ rm -f ftp/pyftpdlib/{__main__,prefork}.py langs="eng|$langs" aerr "ERROR: removing english is not supported; will do this instead: $langs" } - for f in copyparty/web/{browser.js,splash.js}; do - gzip -d "$f.gz" || true - iawk '/^\}/{l=0} !l; /^var Ls =/{l=1;next} !l{next} o; /^\t["}]/{o=0} /^\t"'"$langs"'"/{o=1;print}' $f - done + f=copyparty/web/browser.js + gzip -d "$f.gz" || true + iawk '/^\]/{s=0} !s; /^var LANGN /{s=1;next} !s{next} /"'"$langs"'"/' $f + ls -1 copyparty/web/tl/* >t + grep -vE "/($langs)\." (you're not logged in)", "c1": "logout", @@ -115,21 +110,9 @@ var tl_cpanel = {{ "ac1": "enable no304", "ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!", "ae1": "active downloads:", - "af1": "show recent uploads", - }}, + "af1": "show recent uploads", + }} }}; - - -//////////////////////////////////////////////////////////////////////// -// translation of browser.js (the filebrowser): - -var tl_browser = {{ - "{lang3}": {{ - "tt": "{native_name}", - - {tl_browser} -}}; - """ @@ -158,7 +141,8 @@ def main(): browserjs = f.read().decode("utf-8") _, browserjs = browserjs.split('\n\t\t"tt": "English",\n', 1) - browserjs, _ = browserjs.split('\n\t"nor": {', 1) + browserjs, _ = browserjs.split('\n}', 1) + browserjs = browserjs.replace("\n\t", "\n") try: lang3 = sys.argv[1] diff --git a/scripts/tlcheck.sh b/scripts/tlcheck.sh index 736c4bd5..6bd81c09 100755 --- a/scripts/tlcheck.sh +++ b/scripts/tlcheck.sh @@ -1,13 +1,15 @@ #!/bin/bash set -e -# usage: ./scripts/tlcheck.sh eng chi copyparty/web/browser.js +[ -f "$1" ] && [ -f "$2" ] && [ $# = 2 ] || { + echo usage: ./scripts/tlcheck.sh scripts/tl.js copyparty/web/tl/nor.js + exit 1 +} -awk <"$3" -v lang1=\"$1\": -v lang2=\"$2\": ' - /^\t\}/{fa=0;fb=0} +cat "$1" "$2" | awk ' + /^\}/{fa=0;fb=0} + /^Ls\./{if(nln++){fb=1}else{fa=1}} !/":/{next} - $0~lang1{fa=1} - $0~lang2{fb=1} fa{a[ia++]=$0} fb{b[ib++]=$0} END{for (i=0;i