From 9a56475f4c1566925536bdf8bbd7a36c1d0c39f8 Mon Sep 17 00:00:00 2001 From: default Date: Fri, 14 Feb 2025 09:54:58 +0100 Subject: New function lang_str(). --- data.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'data.c') diff --git a/data.c b/data.c index 9fc54a0..3aba471 100644 --- a/data.c +++ b/data.c @@ -4064,3 +4064,28 @@ void badlogin_inc(const char *user, const char *addr) pthread_mutex_unlock(&data_mutex); } } + + +/** language strings **/ + +const char *lang_str(const char *str, const snac *user) +/* returns a translated string */ +{ + if (user && xs_is_string(str) && xs_is_dict(srv_langs)) { + /* get user preference */ + const char *lang = xs_dict_get(user->config, "lang"); + + if (xs_is_string(lang)) { + const xs_dict *strs = xs_dict_get(srv_langs, lang); + + if (xs_is_dict(strs)) { + const char *n_str = xs_dict_get(strs, str); + + if (xs_is_string(n_str)) + str = n_str; + } + } + } + + return str; +} -- cgit From e020ca84718d99401a99af0363992b4661a1d16e Mon Sep 17 00:00:00 2001 From: default Date: Fri, 14 Feb 2025 10:24:13 +0100 Subject: xs_po.h new file. --- Makefile | 14 +++++----- Makefile.NetBSD | 14 +++++----- data.c | 1 + snac.c | 1 + xs_po.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ xs_version.h | 2 +- 6 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 xs_po.h (limited to 'data.c') diff --git a/Makefile b/Makefile index 1fddbb2..178cfdd 100644 --- a/Makefile +++ b/Makefile @@ -44,20 +44,20 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ snac.h http_codes.h data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ - xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \ - http_codes.h + xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ + xs_po.h snac.h http_codes.h format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h xs_match.h snac.h http_codes.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ - xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ - http_codes.h + xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ + snac.h http_codes.h http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ snac.h http_codes.h httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ http_codes.h -main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h snac.h \ - http_codes.h +main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \ + snac.h http_codes.h mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ snac.h http_codes.h @@ -65,7 +65,7 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ - xs_match.h xs_fcgi.h xs_html.h snac.h http_codes.h + xs_match.h xs_fcgi.h xs_html.h xs_po.h snac.h http_codes.h upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h diff --git a/Makefile.NetBSD b/Makefile.NetBSD index 93222b2..51c8181 100644 --- a/Makefile.NetBSD +++ b/Makefile.NetBSD @@ -39,20 +39,20 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ snac.h http_codes.h data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ - xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \ - http_codes.h + xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ + xs_po.h snac.h http_codes.h format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ xs_time.h xs_match.h snac.h http_codes.h html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ - xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ - http_codes.h + xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ + snac.h http_codes.h http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ snac.h http_codes.h httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ http_codes.h -main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h snac.h \ - http_codes.h +main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \ + snac.h http_codes.h mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ snac.h http_codes.h @@ -60,7 +60,7 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ - xs_match.h xs_fcgi.h xs_html.h snac.h http_codes.h + xs_match.h xs_fcgi.h xs_html.h xs_po.h snac.h http_codes.h upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h diff --git a/data.c b/data.c index 3aba471..f935b39 100644 --- a/data.c +++ b/data.c @@ -13,6 +13,7 @@ #include "xs_match.h" #include "xs_unicode.h" #include "xs_random.h" +#include "xs_po.h" #include "snac.h" diff --git a/snac.c b/snac.c index 541828e..d4a96e9 100644 --- a/snac.c +++ b/snac.c @@ -24,6 +24,7 @@ #include "xs_match.h" #include "xs_fcgi.h" #include "xs_html.h" +#include "xs_po.h" #include "snac.h" diff --git a/xs_po.h b/xs_po.h new file mode 100644 index 0000000..c6feec9 --- /dev/null +++ b/xs_po.h @@ -0,0 +1,86 @@ +/* copyright (c) 2025 grunfink et al. / MIT license */ + +#ifndef _XS_PO_H + +#define _XS_PO_H + +xs_dict *xs_po_to_dict(const char *fn); + +#ifdef XS_IMPLEMENTATION + +xs_dict *xs_po_to_dict(const char *fn) +/* converts a PO file to a dict */ +{ + xs_dict *d = NULL; + FILE *f; + + if ((f = fopen(fn, "r")) != NULL) { + d = xs_dict_new(); + + xs *k = NULL; + xs *v = NULL; + enum { IN_NONE, IN_K, IN_V } mode = IN_NONE; + + while (!feof(f)) { + xs *l = xs_strip_i(xs_readline(f)); + + /* discard empty lines and comments */ + if (*l == '\0' || *l == '#') + continue; + + if (xs_startswith(l, "msgid ")) { + if (mode == IN_V) { + /* flush */ + if (xs_is_string(k) && xs_is_string(v) && *v) + d = xs_dict_set(d, k, v); + + k = xs_free(k); + v = xs_free(v); + } + + l = xs_replace_i(l, "msgid ", ""); + mode = IN_K; + + k = xs_str_new(NULL); + } + else + if (xs_startswith(l, "msgstr ")) { + if (mode != IN_K) + break; + + l = xs_replace_i(l, "msgstr ", ""); + mode = IN_V; + + v = xs_str_new(NULL); + } + + l = xs_replace_i(l, "\\n", "\n"); + l = xs_strip_chars_i(l, "\""); + + switch (mode) { + case IN_K: + k = xs_str_cat(k, l); + break; + + case IN_V: + v = xs_str_cat(v, l); + break; + + case IN_NONE: + break; + } + } + + /* final flush */ + if (xs_is_string(k) && xs_is_string(v) && *v) + d = xs_dict_set(d, k, v); + + fclose(f); + } + + return d; +} + +#endif /* XS_IMPLEMENTATION */ + +#endif /* XS_PO_H */ diff --git a/xs_version.h b/xs_version.h index 7314133..f899dcb 100644 --- a/xs_version.h +++ b/xs_version.h @@ -1 +1 @@ -/* 2f43b93e9d2b63360c802e09f4c68adfef74c673 2025-01-28T07:40:50+01:00 */ +/* d467dc71e518603250a55c8a67e26cf40e1710e9 2025-02-14T10:21:15+01:00 */ -- cgit From f0f93b84bec5373f9c6567b7b415ea77ca0bd064 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 15 Feb 2025 06:00:19 +0100 Subject: Optimized lang_str(). --- data.c | 19 ++++++------------- html.c | 17 +++++++++++++++-- snac.h | 1 + 3 files changed, 22 insertions(+), 15 deletions(-) (limited to 'data.c') diff --git a/data.c b/data.c index f935b39..4308aa5 100644 --- a/data.c +++ b/data.c @@ -4072,21 +4072,14 @@ void badlogin_inc(const char *user, const char *addr) const char *lang_str(const char *str, const snac *user) /* returns a translated string */ { - if (user && xs_is_string(str) && xs_is_dict(srv_langs)) { - /* get user preference */ - const char *lang = xs_dict_get(user->config, "lang"); + const char *n_str = str; - if (xs_is_string(lang)) { - const xs_dict *strs = xs_dict_get(srv_langs, lang); + if (user && xs_is_dict(user->lang) && xs_is_string(str)) { + n_str = xs_dict_get(user->lang, str); - if (xs_is_dict(strs)) { - const char *n_str = xs_dict_get(strs, str); - - if (xs_is_string(n_str)) - str = n_str; - } - } + if (xs_is_null(n_str) || *n_str == '\0') + n_str = str; } - return str; + return n_str; } diff --git a/html.c b/html.c index e11abd1..5068b49 100644 --- a/html.c +++ b/html.c @@ -3326,6 +3326,17 @@ xs_str *html_notifications(snac *user, int skip, int show) } +void set_user_lang(snac *user) +/* sets the language dict according to user configuration */ +{ + user->lang = NULL; + const char *lang = xs_dict_get(user->config, "lang"); + + if (xs_is_string(lang)) + user->lang = xs_dict_get(srv_langs, lang); +} + + int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag, xs_str **last_modified) @@ -3399,7 +3410,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, return HTTP_STATUS_NOT_FOUND; } - user = &snac; + user = &snac; /* for L() */ + set_user_lang(&snac); if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) proxy = 1; @@ -4048,7 +4060,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, return HTTP_STATUS_UNAUTHORIZED; } - user = &snac; + user = &snac; /* for L() */ + set_user_lang(&snac); p_vars = xs_dict_get(req, "p_vars"); diff --git a/snac.h b/snac.h index e2b6b5f..ac1abcd 100644 --- a/snac.h +++ b/snac.h @@ -60,6 +60,7 @@ typedef struct { xs_dict *links; /* validated links */ xs_str *actor; /* actor url */ xs_str *md5; /* actor url md5 */ + const xs_dict *lang;/* string translation dict */ } snac; typedef struct { -- cgit From 91d2beb267020fa632cace5b0b475a9febfa0439 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 15 Feb 2025 06:11:47 +0100 Subject: srv_open() now loads all .po files into srv_langs. --- data.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'data.c') diff --git a/data.c b/data.c index 4308aa5..e0b4936 100644 --- a/data.c +++ b/data.c @@ -149,6 +149,28 @@ int srv_open(const char *basedir, int auto_upgrade) mkdirx(expdir); } + /* languages */ + srv_langs = xs_dict_new(); + + xs *l_dir = xs_fmt("%s/lang/", srv_basedir); + mkdirx(l_dir); + + l_dir = xs_str_cat(l_dir, "*.po"); + xs *pos = xs_glob(l_dir, 0, 0); + const char *po; + + xs_list_foreach(pos, po) { + xs *d = xs_po_to_dict(po); + + if (xs_is_dict(d)) { + xs *l = xs_split(po, "/"); + xs *id = xs_dup(xs_list_get(l, -1)); + id = xs_replace_i(id, ".po", ""); + + srv_langs = xs_dict_set(srv_langs, id, d); + } + } + return ret; } -- cgit From 1554a207e653d626c194b7d8113a55358c0dcdf7 Mon Sep 17 00:00:00 2001 From: default Date: Sat, 15 Feb 2025 06:35:47 +0100 Subject: The interface language preference can now be set. --- data.c | 1 + html.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'data.c') diff --git a/data.c b/data.c index e0b4936..e9837bb 100644 --- a/data.c +++ b/data.c @@ -151,6 +151,7 @@ int srv_open(const char *basedir, int auto_upgrade) /* languages */ srv_langs = xs_dict_new(); + srv_langs = xs_dict_set(srv_langs, "en", xs_stock(XSTYPE_NULL)); xs *l_dir = xs_fmt("%s/lang/", srv_basedir); mkdirx(l_dir); diff --git a/html.c b/html.c index 5068b49..6573630 100644 --- a/html.c +++ b/html.c @@ -1243,6 +1243,28 @@ xs_html *html_top_controls(snac *user) else metadata = xs_str_new(NULL); + /* ui language */ + xs_html *lang_select = xs_html_tag("select", + xs_html_attr("name", "web_ui_lang")); + + const char *u_lang = xs_dict_get_def(user->config, "lang", "en"); + const char *lang; + const xs_dict *langs; + + xs_dict_foreach(srv_langs, lang, langs) { + if (strcmp(u_lang, lang) == 0) + xs_html_add(lang_select, + xs_html_tag("option", + xs_html_text(lang), + xs_html_attr("value", lang), + xs_html_attr("selected", "selected"))); + else + xs_html_add(lang_select, + xs_html_tag("option", + xs_html_text(lang), + xs_html_attr("value", lang))); + } + xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); xs_html_add(top_controls, @@ -1433,6 +1455,11 @@ xs_html *html_top_controls(snac *user) "/example.com/my-blog\nGPG Key=1FA54\n..."), xs_html_text(metadata))), + xs_html_tag("p", + xs_html_text(L("Web interface language:")), + xs_html_sctag("br", NULL), + lang_select), + xs_html_tag("p", xs_html_text(L("New password:")), xs_html_sctag("br", NULL), @@ -4505,6 +4532,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); else snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); + if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) + snac.config = xs_dict_set(snac.config, "lang", v); snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); -- cgit From c12a7e38adbce6058ee7d9d4113cce2649c87977 Mon Sep 17 00:00:00 2001 From: default Date: Mon, 17 Feb 2025 07:49:11 +0100 Subject: Return ASAP if srv_open() fails. --- data.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'data.c') diff --git a/data.c b/data.c index e9837bb..ce040dd 100644 --- a/data.c +++ b/data.c @@ -99,6 +99,9 @@ int srv_open(const char *basedir, int auto_upgrade) if (error != NULL) srv_log(error); + if (!ret) + return ret; + /* create the queue/ subdir, just in case */ xs *qdir = xs_fmt("%s/queue", srv_basedir); mkdirx(qdir); -- cgit