Skip to content

Commit 51d557e

Browse files
Keonik1missytake
authored andcommitted
www: add markdown language switcher
1 parent 0ed7c36 commit 51d557e

File tree

9 files changed

+147
-352
lines changed

9 files changed

+147
-352
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
- Increase maxproc for reinjecting ports from 10 to 100
1515
([#646](https://github.com/chatmail/relay/pull/646))
1616

17+
- Add markdown tabs blocks for rendering multilingual pages.
18+
Add russian language support to `index.md`, `privacy.md`, and `info.md`.
19+
([#658](https://github.com/chatmail/relay/pull/658))
20+
1721
- Allow ports 143 and 993 to be used by `dovecot` process
1822
([#639](https://github.com/chatmail/relay/pull/639))
1923

chatmaild/src/chatmaild/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, inipath, params):
3333
self.password_min_length = int(params["password_min_length"])
3434
self.passthrough_senders = params["passthrough_senders"].split()
3535
self.passthrough_recipients = params["passthrough_recipients"].split()
36+
self.languages = (params.get("languages", "EN").split())
3637
self.www_folder = params.get("www_folder", "")
3738
self.filtermail_smtp_port = int(params["filtermail_smtp_port"])
3839
self.filtermail_smtp_port_incoming = int(

chatmaild/src/chatmaild/ini/chatmail.ini.f

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
# Deployment Details
5050
#
5151

52+
# A space-separated list of languages to be displayed on the site.
53+
# Now available languages: EN RU
54+
# You can also use the keyword "ALL"
55+
# NOTE: The order of languages affects their order on the page
56+
languages = EN
57+
5258
# SMTP outgoing filtermail and reinjection
5359
filtermail_smtp_port = 10080
5460
postfix_reinject_port = 10025

cmdeploy/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies = [
2020
"pytest-xdist",
2121
"execnet",
2222
"imap_tools",
23+
"pymdown-extensions",
2324
]
2425

2526
[project.scripts]

cmdeploy/src/cmdeploy/www.py

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111

1212
from .genqr import gen_qr_png_data
1313

14+
LANGUAGE_NAMES = {
15+
"EN": " 🇬🇧 English",
16+
"RU": " 🇷🇺 Русский",
17+
# "UA": "Українська",
18+
# "FR": "Français",
19+
# "DE": "Deutsch",
20+
}
1421

1522
def snapshot_dir_stats(somedir):
1623
d = {}
@@ -22,12 +29,59 @@ def snapshot_dir_stats(somedir):
2229
return d
2330

2431

25-
def prepare_template(source):
26-
assert source.exists(), source
27-
render_vars = {}
28-
render_vars["pagename"] = "home" if source.stem == "index" else source.stem
29-
render_vars["markdown_html"] = markdown.markdown(source.read_text())
30-
page_layout = source.with_name("page-layout.html").read_text()
32+
def prepare_template(source, locales_dir, languages=["EN"]):
33+
assert source.exists(), f"Template {source} not found."
34+
assert locales_dir.exists(), f"Locales directory {locales_dir} not found."
35+
base_name = source.stem
36+
render_vars = {
37+
"pagename": "home" if base_name == "index" else base_name
38+
}
39+
40+
selected_langs = (
41+
sorted([d.name.upper() for d in locales_dir.iterdir() if d.is_dir()])
42+
if "ALL" in [l.upper() for l in languages]
43+
else [l.upper() for l in languages]
44+
)
45+
46+
markdown_blocks = []
47+
48+
tabs_enabled = False
49+
if len(selected_langs) > 1:
50+
tabs_enabled = True
51+
52+
for lang_code in selected_langs:
53+
lang_folder = locales_dir / lang_code
54+
lang_file = lang_folder / f"{base_name}.md"
55+
lang_name = LANGUAGE_NAMES.get(lang_code, lang_code)
56+
57+
if lang_file.exists():
58+
content = lang_file.read_text().strip()
59+
else:
60+
print(f"[WARNING]: Missing file {lang_file}. Inserting fallback message.")
61+
content = "Content for this language is not available, please contact your server administrator."
62+
63+
if tabs_enabled:
64+
markdown_blocks.append(f"/// tab | {lang_name}\n{content}\n///")
65+
continue
66+
67+
markdown_blocks.append(content)
68+
69+
if not markdown_blocks:
70+
print("[WARNING] No valid language content found. Skipping file.")
71+
return None, None
72+
73+
original_markdown = source.read_text()
74+
combined_markdown = original_markdown.replace("%content placeholder%", "\n\n".join(markdown_blocks))
75+
76+
render_vars["markdown_html"] = markdown.markdown(
77+
combined_markdown,
78+
extensions=["pymdownx.blocks.tab"]
79+
)
80+
81+
page_layout_path = source.with_name("page-layout.html")
82+
assert page_layout_path.exists(), f"Missing template: {page_layout_path}"
83+
page_layout = page_layout_path.read_text()
84+
3185
return render_vars, page_layout
3286

3387

@@ -80,25 +134,27 @@ def int_to_english(number):
80134

81135
def _build_webpages(src_dir, build_dir, config):
82136
mail_domain = config.mail_domain
137+
languages = config.languages
83138
assert src_dir.exists(), src_dir
84139
if not build_dir.exists():
85140
build_dir.mkdir()
86141

87142
qr_path = build_dir.joinpath(f"qr-chatmail-invite-{mail_domain}.png")
88143
qr_path.write_bytes(gen_qr_png_data(mail_domain).read())
89144

145+
locales_dir = src_dir / "locales"
146+
90147
for path in src_dir.iterdir():
91148
if path.suffix == ".md":
92-
render_vars, content = prepare_template(path)
93-
render_vars["username_min_length"] = int_to_english(
94-
config.username_min_length
95-
)
96-
render_vars["username_max_length"] = int_to_english(
97-
config.username_max_length
98-
)
99-
render_vars["password_min_length"] = int_to_english(
100-
config.password_min_length
101-
)
149+
render_vars, content = prepare_template(path, locales_dir, languages)
150+
151+
if render_vars is None:
152+
continue
153+
154+
render_vars["username_min_length"] = int_to_english(config.username_min_length)
155+
render_vars["username_max_length"] = int_to_english(config.username_max_length)
156+
render_vars["password_min_length"] = int_to_english(config.password_min_length)
157+
102158
target = build_dir.joinpath(path.stem + ".html")
103159

104160
# recursive jinja2 rendering
@@ -110,9 +166,11 @@ def _build_webpages(src_dir, build_dir, config):
110166

111167
with target.open("w") as f:
112168
f.write(content)
113-
elif path.name != "page-layout.html":
169+
170+
elif path.name != "page-layout.html" and path.name != "locales":
114171
target = build_dir.joinpath(path.name)
115172
target.write_bytes(path.read_bytes())
173+
116174
return build_dir
117175

118176

www/src/index.md

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,8 @@
11

22
<img class="banner" src="collage-top.png"/>
33

4-
## Dear [Delta Chat](https://get.delta.chat) users and newcomers ...
4+
%content placeholder%
55

6-
{% if config.mail_domain != "nine.testrun.org" %}
7-
Welcome to instant, interoperable and [privacy-preserving](privacy.html) messaging :)
8-
{% else %}
9-
Welcome to the default onboarding server ({{ config.mail_domain }})
10-
for Delta Chat users. For details how it avoids storing personal information
11-
please see our [privacy policy](privacy.html).
12-
{% endif %}
13-
14-
<a class="cta-button" href="DCACCOUNT:https://{{ config.mail_domain }}/new">Get a {{config.mail_domain}} chat profile</a>
15-
16-
If you are viewing this page on a different device
17-
without a Delta Chat app,
18-
you can also **scan this QR code** with Delta Chat:
19-
20-
<a href="DCACCOUNT:https://{{ config.mail_domain }}/new">
21-
<img width=300 style="float: none;" src="qr-chatmail-invite-{{config.mail_domain}}.png" /></a>
22-
23-
🐣 **Choose** your Avatar and Name
24-
25-
💬 **Start** chatting with any Delta Chat contacts using [QR invite codes](https://delta.chat/en/help#howtoe2ee)
26-
27-
{% if config.mail_domain != "nine.testrun.org" %}
6+
{% if config.is_development_instance == True %}
287
<div class="experimental">Note: this is only a temporary development chatmail service</div>
298
{% endif %}

www/src/info.md

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,3 @@
1+
<img class="banner" src="collage-info.png"/>
12

2-
## More information
3-
4-
{{ config.mail_domain }} provides a low-maintenance, resource efficient and
5-
interoperable e-mail service for everyone. What's behind a `chatmail` is
6-
effectively a normal e-mail address just like any other but optimized
7-
for the usage in chats, especially DeltaChat.
8-
9-
10-
### Rate and storage limits
11-
12-
- Un-encrypted messages are blocked to recipients outside
13-
{{config.mail_domain}} but setting up contact via [QR invite codes](https://delta.chat/en/help#howtoe2ee)
14-
allows your messages to pass freely to any outside recipients.
15-
16-
- You may send up to {{ config.max_user_send_per_minute }} messages per minute.
17-
18-
- You can store up to [{{ config.max_mailbox_size }} messages on the server](https://delta.chat/en/help#what-happens-if-i-turn-on-delete-old-messages-from-server).
19-
20-
- Messages are unconditionally removed latest {{ config.delete_mails_after }} days after arriving on the server.
21-
Earlier, if storage may exceed otherwise.
22-
23-
24-
### <a name="account-deletion"></a> Account deletion
25-
26-
If you remove a {{ config.mail_domain }} profile from within the Delta Chat app,
27-
then the according account on the server, along with all associated data,
28-
is automatically deleted {{ config.delete_inactive_users_after }} days afterwards.
29-
30-
If you use multiple devices
31-
then you need to remove the according chat profile from each device
32-
in order for all account data to be removed on the server side.
33-
34-
If you have any further questions or requests regarding account deletion
35-
please send a message from your account to {{ config.privacy_mail }}.
36-
37-
38-
### Who are the operators? Which software is running?
39-
40-
This chatmail provider is run by a small voluntary group of devs and sysadmins,
41-
who [publically develop chatmail provider setups](https://github.com/deltachat/chatmail).
42-
Chatmail setups aim to be very low-maintenance, resource efficient and
43-
interoperable with any other standards-compliant e-mail service.
3+
%content placeholder%

www/src/main.css

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,57 @@ code {
8484
color: white !important;
8585
font-weight: bold;
8686
}
87+
88+
.tabbed-set {
89+
position: relative;
90+
display: flex;
91+
flex-wrap: wrap;
92+
margin: 1em 0;
93+
border-radius: 0.1rem;
94+
}
95+
96+
.tabbed-set > input {
97+
display: none;
98+
}
99+
100+
.tabbed-set label {
101+
width: auto;
102+
padding: 0.9375em 1.25em 0.78125em;
103+
font-weight: 700;
104+
font-size: 0.84em;
105+
white-space: nowrap;
106+
border-bottom: 0.15rem solid transparent;
107+
border-top-left-radius: 0.1rem;
108+
border-top-right-radius: 0.1rem;
109+
cursor: pointer;
110+
transition: background-color 250ms, color 250ms;
111+
}
112+
113+
.tabbed-set .tabbed-content {
114+
width: 100%;
115+
display: none;
116+
box-shadow: 0 -.05rem #ddd;
117+
}
118+
119+
.tabbed-set input {
120+
position: absolute;
121+
opacity: 0;
122+
}
123+
124+
.tabbed-set input:checked:nth-child(n+1) + label {
125+
color: red;
126+
border-color: red;
127+
}
128+
129+
@media screen {
130+
.tabbed-set input:nth-child(n+1):checked + label + .tabbed-content {
131+
order: 99;
132+
display: block;
133+
}
134+
}
135+
136+
@media print {
137+
.tabbed-content {
138+
display: contents;
139+
}
140+
}

0 commit comments

Comments
 (0)