From 7105c3ce59c421a6e104e4e36067463b7fd63cb0 Mon Sep 17 00:00:00 2001 From: Burak Yavuz Date: Tue, 22 Oct 2024 12:15:32 +0000 Subject: [PATCH 01/32] Translated using Weblate (Turkish) Currently translated at 100.0% (1770 of 1770 strings) --- plinth/locale/tr/LC_MESSAGES/django.po | 330 +++++++++---------------- 1 file changed, 118 insertions(+), 212 deletions(-) diff --git a/plinth/locale/tr/LC_MESSAGES/django.po b/plinth/locale/tr/LC_MESSAGES/django.po index 2abd8dd07..00b563cd6 100644 --- a/plinth/locale/tr/LC_MESSAGES/django.po +++ b/plinth/locale/tr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-09 04:16+0000\n" +"PO-Revision-Date: 2024-10-23 05:15+0000\n" "Last-Translator: Burak Yavuz \n" "Language-Team: Turkish \n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 5.8-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -856,14 +856,12 @@ msgstr "" #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Dosya ve Kod Parçacığı Paylaşımı" +msgstr "Dosya paylaşımı" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1098,19 +1096,15 @@ msgstr "Bu ada sahip bir kütüphane zaten var." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "E-kitap" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "E-kitap Kütüphanesi" +msgstr "Kütüphane" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "E-kitap Kütüphanesi" +msgstr "E-kitap okuyucu" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1364,15 +1358,15 @@ msgstr "STUN/TURN Sunucu URI'lerinin geçersiz listesi" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Görüntülü konferans" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1461,17 +1455,13 @@ msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Web istemcisini başlat" +msgstr "Web istemcisi" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -2030,25 +2020,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Şifreleme" +msgstr "Şifreli mesajlaşma" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Sesli sohbet" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Video Odası" +msgstr "Görüntülü sohbet" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 #, python-format @@ -2189,18 +2175,16 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" -msgstr "E-posta Sunucusu" +msgstr "E-posta sunucusu" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" -msgstr "" +msgstr "İstenmeyen ileti denetimi" #: modules/email/templates/email-aliases.html:13 #: modules/email/templates/email.html:15 @@ -2358,26 +2342,22 @@ msgstr "Viki" #: modules/featherwiki/manifest.py:18 modules/infinoted/manifest.py:46 #: modules/tiddlywiki/manifest.py:20 msgid "Note taking" -msgstr "" +msgstr "Not alma" #: modules/featherwiki/manifest.py:18 modules/ikiwiki/manifest.py:15 #: modules/mediawiki/manifest.py:25 modules/tiddlywiki/manifest.py:21 #: modules/wordpress/manifest.py:26 -#, fuzzy -#| msgid "Website Security" msgid "Website" -msgstr "Web Sitesi Güvenliği" +msgstr "Web sitesi" #: modules/featherwiki/manifest.py:18 modules/tiddlywiki/manifest.py:25 msgid "Quine" -msgstr "" +msgstr "Quine" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Debian:" +msgstr "Debian olmayan" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 @@ -2590,10 +2570,8 @@ msgstr "" "sağlanır." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Kurulum Tamamlandı!" +msgstr "Kurulum tamamlandı! Sonraki adımlar:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2601,14 +2579,16 @@ msgid "" "Initial setup has been completed. Perform the next steps to make your " "{box_name} operational." msgstr "" +"İlk kurulum tamamlandı. {box_name} cihazınızı çalışır duruma getirmek için " +"sonraki adımları uygulayın." #: modules/first_boot/__init__.py:66 msgid "Next steps" -msgstr "" +msgstr "Sonraki adımlar" #: modules/first_boot/__init__.py:73 msgid "See next steps" -msgstr "" +msgstr "Sonraki adımları görün" #: modules/first_boot/forms.py:14 #, python-brace-format @@ -2626,10 +2606,8 @@ msgid "Firstboot Wizard Secret" msgstr "Firstboot Sihirbazı Gizli Anahtarı" #: modules/first_boot/templates/firstboot_complete.html:11 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Kurulum Tamamlandı!" +msgstr "Kurulum Tamamlandı! Sonraki Adımlar:" #: modules/first_boot/templates/firstboot_complete.html:18 #, python-format @@ -2637,6 +2615,9 @@ msgid "" "Automatic software update " "runs daily by default. For the first time, manually run it now." msgstr "" +"Otomatik yazılım " +"güncellemesi varsayılan olarak günlük çalışır. Şimdi ilk kez el ile " +"çalıştırın." #: modules/first_boot/templates/firstboot_complete.html:27 #: modules/upgrades/templates/upgrades_configure.html:108 @@ -2648,6 +2629,8 @@ msgstr "Şimdi güncelle" msgid "" "Review privacy options." msgstr "" +"Gizlilik seçeneklerini " +"inceleyin." #: modules/first_boot/templates/firstboot_complete.html:46 #, python-format @@ -2655,12 +2638,17 @@ msgid "" "Review and setup network " "connections. Change the default Wi-Fi password, if applicable." msgstr "" +"Ağ bağlantılarını " +"inceleyin ve ayarlayın. Varsa, varsayılan kablosuz (Wi-Fi) şifresini " +"değiştirin." #: modules/first_boot/templates/firstboot_complete.html:57 #, python-format msgid "" "Configure a domain name." msgstr "" +"Bir etki alanı adı " +"yapılandırın." #: modules/first_boot/templates/firstboot_complete.html:67 #, python-format @@ -2668,6 +2656,8 @@ msgid "" "Configure and schedule remote backups." msgstr "" +"Uzak yedeklemeleri " +"yapılandırın ve zamanlayın." #: modules/first_boot/templates/firstboot_complete.html:78 #, python-format @@ -2675,6 +2665,8 @@ msgid "" "Put %(box_name)s to use by installing apps." msgstr "" +"Uygulamaları yükleyerek " +"%(box_name)s cihazını kullanmaya başlayın." #: modules/first_boot/templates/firstboot_welcome.html:29 msgid "Start Setup" @@ -2781,18 +2773,16 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Basit Git Barındırma" +msgstr "Git barındırma" #: modules/gitweb/manifest.py:37 msgid "Version control" -msgstr "" +msgstr "Sürüm denetimi" #: modules/gitweb/manifest.py:37 msgid "Developer tool" -msgstr "" +msgstr "Geliştirici aracı" #: modules/gitweb/templates/gitweb_configure.html:13 msgid "Manage Repositories" @@ -3263,16 +3253,14 @@ msgstr "I2P Vekil Sunucusu" #: modules/i2p/manifest.py:43 modules/tor/manifest.py:59 #: modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" -msgstr "İsim Gizliliği Ağı" +msgstr "İsim gizliliği ağı" #: modules/i2p/manifest.py:43 modules/kiwix/manifest.py:25 #: modules/shadowsocks/manifest.py:19 modules/shadowsocksserver/manifest.py:18 #: modules/tor/manifest.py:60 modules/torproxy/manifest.py:57 msgid "Censorship resistance" -msgstr "" +msgstr "Sansüre dayanıklılık" #: modules/i2p/templates/i2p.html:12 msgid "I2P Proxies and Tunnels" @@ -3358,7 +3346,7 @@ msgstr "Yönetici Hesap Parolası" #: modules/ikiwiki/manifest.py:15 modules/wordpress/manifest.py:26 msgid "Blog" -msgstr "" +msgstr "Blog" #: modules/ikiwiki/templates/ikiwiki_configure.html:12 msgid "Manage Wikis and Blogs" @@ -3477,7 +3465,7 @@ msgstr "" #: modules/infinoted/manifest.py:46 msgid "Collaborative editing" -msgstr "" +msgstr "İşbirliğine dayalı düzenleme" #: modules/janus/__init__.py:23 msgid "Janus is a lightweight WebRTC server." @@ -3505,14 +3493,12 @@ msgid "Janus Video Room" msgstr "Janus Video Odası" #: modules/janus/manifest.py:16 -#, fuzzy -#| msgid "WebRTC server" msgid "WebRTC" -msgstr "WebRTC sunucusu" +msgstr "WebRTC" #: modules/janus/manifest.py:16 msgid "Web conference" -msgstr "" +msgstr "Web görüşme" #: modules/janus/templates/janus_video_room.html:205 #: modules/jsxc/templates/jsxc_launch.html:117 templates/base.html:254 @@ -3536,16 +3522,12 @@ msgid "Chat Client" msgstr "Sohbet İstemcisi" #: modules/jsxc/manifest.py:16 -#, fuzzy -#| msgid "Web Search" msgid "Web chat" -msgstr "Web Arama" +msgstr "Web sohbeti" #: modules/jsxc/manifest.py:16 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Client" msgid "Client" -msgstr "IRC İstemcisi" +msgstr "İstemci" #: modules/kiwix/__init__.py:21 msgid "" @@ -3621,22 +3603,16 @@ msgstr "" "alanından tasarruf etmek için dosya hemen silinecektir." #: modules/kiwix/manifest.py:23 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Offline reader" -msgstr "Çevrimdışı Vikipedi" +msgstr "Çevrimdışı okuyucu" #: modules/kiwix/manifest.py:24 -#, fuzzy -#| msgid "Kite name" msgid "Archival" -msgstr "Kite ismi" +msgstr "Arşiv" #: modules/kiwix/manifest.py:26 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Wikipedia" -msgstr "Çevrimdışı Vikipedi" +msgstr "Vikipedi" #: modules/kiwix/templates/kiwix-add-package.html:24 #, python-format @@ -3916,16 +3892,12 @@ msgid "FluffyChat" msgstr "FluffyChat" #: modules/matrixsynapse/manifest.py:101 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Chatroom" msgid "Chat room" -msgstr "IRC Sohbet Odası" +msgstr "Sohbet odası" #: modules/matrixsynapse/manifest.py:105 -#, fuzzy -#| msgid "Media streaming server" msgid "Matrix server" -msgstr "Ortam akış sunucusu" +msgstr "Matrix sunucusu" #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 #: modules/miniflux/templates/miniflux.html:12 @@ -4265,20 +4237,16 @@ msgstr "" "Etkisizleştirildiğinde, oyuncular ölemez veya hiçbir şekilde hasar alamazlar." #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Updated server." msgid "Game server" -msgstr "Sunucu güncellendi." +msgstr "Oyun sunucusu" #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Block Sandbox" msgid "Block sandbox" -msgstr "Blok Kum Havuzu" +msgstr "Blok sanal alan" #: modules/minetest/manifest.py:49 msgid "Platform" -msgstr "" +msgstr "Platform" #: modules/minetest/templates/minetest.html:17 modules/networks/forms.py:105 #: modules/networks/forms.py:145 @@ -4340,24 +4308,20 @@ msgid "totem" msgstr "totem" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "Simple Media Server" msgid "Media server" -msgstr "Basit Ortam Sunucusu" +msgstr "Ortam sunucusu" #: modules/minidlna/manifest.py:116 msgid "Television" -msgstr "" +msgstr "Televizyon" #: modules/minidlna/manifest.py:116 msgid "UPnP" -msgstr "" +msgstr "UPnP" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "MiniDLNA" msgid "DLNA" -msgstr "MiniDLNA" +msgstr "DLNA" #: modules/minidlna/views.py:33 msgid "Updated media directory" @@ -4452,26 +4416,22 @@ msgid "RSS Guard" msgstr "RSS Guard" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "News Feed Reader" msgid "Feed reader" -msgstr "Haber Bildirim Okuyucusu" +msgstr "Bildirim okuyucu" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 msgid "News aggregation" -msgstr "" +msgstr "Haber toplama" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "SSH" msgid "RSS" -msgstr "SSH" +msgstr "RSS" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: modules/miniflux/templates/miniflux.html:14 msgid "" @@ -4591,7 +4551,7 @@ msgstr "Mumla" #: modules/mumble/manifest.py:67 msgid "Group conference" -msgstr "" +msgstr "Grup görüşme" #: modules/mumble/manifest.py:67 modules/radicale/manifest.py:91 #: modules/shadowsocks/forms.py:24 @@ -6078,20 +6038,16 @@ msgstr "" "varsayılan telefon bölgesi gereklidir." #: modules/nextcloud/manifest.py:56 modules/syncthing/manifest.py:58 -#, fuzzy -#| msgid "System" msgid "File sync" -msgstr "Sistem" +msgstr "Dosya eşitleme" #: modules/nextcloud/manifest.py:56 modules/sharing/__init__.py:34 msgid "Sharing" msgstr "Paylaşım" #: modules/nextcloud/manifest.py:56 -#, fuzzy -#| msgid "Group Share" msgid "Groupware" -msgstr "Grup Paylaşımı" +msgstr "Grup yazılımı" #: modules/nextcloud/views.py:53 msgid "Password update failed. Please choose a stronger password." @@ -6137,16 +6093,12 @@ msgid "Tunnelblick" msgstr "Tunnelblick" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS server" msgid "VPN server" -msgstr "DNS sunucusu" +msgstr "VPN sunucusu" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "Name Services" msgid "Remote access" -msgstr "İsim Servisleri" +msgstr "Uzaktan erişim" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6575,20 +6527,16 @@ msgstr "Tcp{kind} üzerinde {proxy} vekil sunucusu ile {url} adresine erişin" #: modules/privoxy/manifest.py:10 msgid "Ad blocker" -msgstr "" +msgstr "Reklam engelleyici" #: modules/privoxy/manifest.py:10 modules/shadowsocks/manifest.py:18 #: modules/torproxy/manifest.py:55 -#, fuzzy -#| msgid "Gobby Server" msgid "Proxy server" -msgstr "Gobby Sunucusu" +msgstr "Vekil sunucusu" #: modules/privoxy/manifest.py:10 modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Yerel Ağ Etki Alanı" +msgstr "Yerel ağ" #: modules/quassel/__init__.py:24 #, python-brace-format @@ -6633,7 +6581,7 @@ msgstr "Quasseldroid" #: modules/quassel/manifest.py:54 msgid "IRC" -msgstr "" +msgstr "IRC" #: modules/radicale/__init__.py:25 #, python-brace-format @@ -6741,22 +6689,20 @@ msgstr "" "defterlerini listeleyecektir." #: modules/radicale/manifest.py:91 -#, fuzzy -#| msgid "GNOME Calendar" msgid "Calendar" -msgstr "GNOME Takvim" +msgstr "Takvim" #: modules/radicale/manifest.py:91 modules/roundcube/manifest.py:23 msgid "Contacts" -msgstr "" +msgstr "Kişiler" #: modules/radicale/manifest.py:91 msgid "CalDAV" -msgstr "" +msgstr "CalDAV" #: modules/radicale/manifest.py:91 msgid "CardDAV" -msgstr "" +msgstr "CardDAV" #: modules/radicale/views.py:32 msgid "Access rights configuration updated" @@ -6827,10 +6773,8 @@ msgstr "" "eklenir." #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "E-posta" #: modules/rssbridge/__init__.py:21 msgid "" @@ -6883,16 +6827,12 @@ msgstr "" "Bu uygulamanın, ona ulaşabilen herkes tarafından kullanılmasına izin verin." #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "RSS Bildirim Oluşturucu" +msgstr "Bildirim oluşturucu" #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "FluxNews" msgid "News" -msgstr "FluxNews" +msgstr "Haberler" #: modules/samba/__init__.py:23 msgid "" @@ -6973,22 +6913,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Ağ Arayüzü" +msgstr "Ağ sürücüsü" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Media streaming server" msgid "Media storage" -msgstr "Ortam akış sunucusu" +msgstr "Ortam depolama" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Yedeklemeler" +msgstr "Yedekleme depolaması" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -7128,14 +7062,12 @@ msgid "Strict" msgstr "Sıkı" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Search" msgid "Web search" -msgstr "Web Arama" +msgstr "Web arama" #: modules/searx/manifest.py:17 msgid "Metasearch Engine" -msgstr "" +msgstr "Üst Arama Motoru" #: modules/security/forms.py:13 msgid "Fail2Ban (recommended)" @@ -7292,16 +7224,12 @@ msgid "Shaarlier" msgstr "Shaarlier" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "Link" msgid "Link blog" -msgstr "Bağlantı" +msgstr "Bağlantı blogu" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "in use" msgid "Single user" -msgstr "kullanımda" +msgstr "Tek kullanıcı" #: modules/shadowsocks/__init__.py:18 modules/shadowsocksserver/__init__.py:18 msgid "" @@ -7372,22 +7300,16 @@ msgid "Encryption method. Must match setting on server." msgstr "Şifreleme yöntemi. Sunucudaki ayarla eşleşmek zorundadır." #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Şifreleme" +msgstr "Şifreli tünel" #: modules/shadowsocks/manifest.py:21 -#, fuzzy -#| msgid "Endpoint" msgid "Entry point" -msgstr "Uç nokta" +msgstr "Giriş noktası" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 -#, fuzzy -#| msgid "Shadowsocks Client" msgid "Shadowsocks" -msgstr "Shadowsocks İstemcisi" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7423,10 +7345,8 @@ msgid "Encryption method. Clients must use the same setting." msgstr "Şifreleme yöntemi. İstemciler aynı ayarı kullanmak zorundadır." #: modules/shadowsocksserver/manifest.py:20 -#, fuzzy -#| msgid "Endpoint" msgid "Exit point" -msgstr "Uç nokta" +msgstr "Çıkış noktası" #: modules/sharing/__init__.py:17 #, python-brace-format @@ -7486,10 +7406,8 @@ msgid "Shares should be either public or shared with at least one group" msgstr "Paylaşımlar herkese açık olmalı veya en az bir grupla paylaşılmalıdır" #: modules/sharing/manifest.py:19 modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Sharing" msgid "Web sharing" -msgstr "Paylaşım" +msgstr "Web paylaşımı" #: modules/sharing/templates/sharing.html:18 #: modules/sharing/templates/sharing.html:21 @@ -8227,15 +8145,15 @@ msgstr "Bu bilgisayardan varolan bir TiddlyWiki dosyası yükleyin." #: modules/tiddlywiki/manifest.py:22 msgid "Journal" -msgstr "" +msgstr "Dergi" #: modules/tiddlywiki/manifest.py:23 msgid "Digital garden" -msgstr "" +msgstr "Dijital bahçe" #: modules/tiddlywiki/manifest.py:24 msgid "Zettelkasten" -msgstr "" +msgstr "Zettelkasten" #: modules/tiddlywiki/templates/tiddlywiki_delete.html:18 msgid "" @@ -8401,10 +8319,8 @@ msgid "Orbot: Proxy with Tor" msgstr "Orbot: Tor ile Vekil Sunucu" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" -msgstr "Onion Hizmeti" +msgstr "Onion hizmetleri" #: modules/tor/manifest.py:58 msgid "Relay" @@ -8616,16 +8532,16 @@ msgid "FreedomBox Updated" msgstr "FreedomBox Güncellendi" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Yazılım Güncellemesi" +msgstr "Yazılım güncellemesini el ile çalıştır" #: modules/upgrades/__init__.py:140 msgid "" "Automatic software update runs daily by default. For the first time, " "manually run it now." msgstr "" +"Otomatik yazılım güncellemesi varsayılan olarak günlük çalışır. Şimdi ilk " +"kez el ile çalıştırın." #: modules/upgrades/__init__.py:242 msgid "Could not start distribution update" @@ -9230,10 +9146,8 @@ msgid "Typically checked for a VPN service through which all traffic is sent." msgstr "Genellikle tüm trafiğin gönderildiği bir VPN hizmeti için denetlenir." #: modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "IRC Client" msgid "VPN client" -msgstr "IRC İstemcisi" +msgstr "VPN istemcisi" #: modules/wireguard/templates/wireguard.html:10 msgid "As a Server" @@ -9518,7 +9432,7 @@ msgstr "" #: modules/wordpress/manifest.py:26 msgid "Content management system" -msgstr "" +msgstr "İçerik yönetim sistemi" #: modules/zoph/__init__.py:24 #, python-brace-format @@ -9575,13 +9489,11 @@ msgstr "" #: modules/zoph/manifest.py:26 msgid "Photo" -msgstr "" +msgstr "Fotoğraf" #: modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Photo Organizer" msgid "Organizer" -msgstr "Fotoğraf Düzenleyici" +msgstr "Düzenleyici" #: modules/zoph/templates/zoph-pre-setup.html:15 #: modules/zoph/templates/zoph-pre-setup.html:28 @@ -9602,10 +9514,9 @@ msgid "Generic" msgstr "Genel" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Hata: {name}: {exception_message}" +msgstr "Hata: {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -9657,22 +9568,19 @@ msgid "Updating app" msgstr "Uygulama güncelleniyor" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Uygulama yüklenirken hata oldu: {error}" +msgstr "Uygulama yüklenirken hata oldu: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Uygulama onarılırken hata oldu: {error}" +msgstr "Uygulama onarılırken hata oldu: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Uygulama güncellenirken hata oldu: {error}" +msgstr "Uygulama güncellenirken hata oldu: {exception}" #: setup.py:85 msgid "App installed." @@ -9791,10 +9699,8 @@ msgid "Service %(service_name)s is not running." msgstr "%(service_name)s hizmeti çalışmıyor." #: templates/apps.html:29 -#, fuzzy -#| msgid "Search the web" msgid "Search with tags" -msgstr "Web'de ara" +msgstr "Etiketler ile ara" #: templates/base.html:31 msgid "" From 7e92d0fdba7c144d5623cd611cca23f182657393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Wed, 23 Oct 2024 00:37:51 +0000 Subject: [PATCH 02/32] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 63.8% (1130 of 1770 strings) --- plinth/locale/zh_Hans/LC_MESSAGES/django.po | 178 +++++--------------- 1 file changed, 46 insertions(+), 132 deletions(-) diff --git a/plinth/locale/zh_Hans/LC_MESSAGES/django.po b/plinth/locale/zh_Hans/LC_MESSAGES/django.po index ee8f4d09a..a312b366c 100644 --- a/plinth/locale/zh_Hans/LC_MESSAGES/django.po +++ b/plinth/locale/zh_Hans/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Plinth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-09 04:16+0000\n" +"PO-Revision-Date: 2024-10-23 05:15+0000\n" "Last-Translator: 大王叫我来巡山 \n" "Language-Team: Chinese (Simplified Han script) Date: Tue, 22 Oct 2024 04:26:55 +0000 Subject: [PATCH 03/32] Translated using Weblate (Bulgarian) Currently translated at 46.4% (823 of 1770 strings) --- plinth/locale/bg/LC_MESSAGES/django.po | 158 ++++++++----------------- 1 file changed, 47 insertions(+), 111 deletions(-) diff --git a/plinth/locale/bg/LC_MESSAGES/django.po b/plinth/locale/bg/LC_MESSAGES/django.po index 01b81e412..e3ab77a47 100644 --- a/plinth/locale/bg/LC_MESSAGES/django.po +++ b/plinth/locale/bg/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-17 13:15+0000\n" +"PO-Revision-Date: 2024-10-23 05:15+0000\n" "Last-Translator: 109247019824 \n" "Language-Team: Bulgarian \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.8-rc\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -1081,11 +1081,11 @@ msgstr "" #: modules/calibre/manifest.py:20 msgid "Library" -msgstr "" +msgstr "Библиотека" #: modules/calibre/manifest.py:20 msgid "Ebook reader" -msgstr "" +msgstr "Четене на електронни книги" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1418,17 +1418,13 @@ msgstr "" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Пускане на клиента за уеб" +msgstr "Клиент за уеб" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -1932,23 +1928,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Шифроване" +msgstr "Шифровани съобщения" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Аудио разговори" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 msgid "Video chat" -msgstr "" +msgstr "Видео разговори" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 #, python-format @@ -2066,8 +2060,6 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" msgstr "Пощенски сървър" @@ -2234,10 +2226,8 @@ msgstr "" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Дебиан:" +msgstr "Не е от Дебиан" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 @@ -2447,10 +2437,8 @@ msgstr "" "firewall\">Cockpit." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Настройката е завършена!" +msgstr "Настройката е завършена! Следващи стъпи:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2483,10 +2471,8 @@ msgid "Firstboot Wizard Secret" msgstr "Помощник за тайния ключ при първо зареждане" #: modules/first_boot/templates/firstboot_complete.html:11 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Настройката е завършена!" +msgstr "Настройката е завършена! Следващи стъпки:" #: modules/first_boot/templates/firstboot_complete.html:18 #, python-format @@ -2627,10 +2613,8 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Обикновен хостинг на Git" +msgstr "Хостинг на Git" #: modules/gitweb/manifest.py:37 msgid "Version control" @@ -3037,8 +3021,6 @@ msgstr "I2P Proxy" #: modules/i2p/manifest.py:43 modules/tor/manifest.py:59 #: modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" msgstr "Анонимна мрежа" @@ -3253,10 +3235,8 @@ msgid "Janus Video Room" msgstr "" #: modules/janus/manifest.py:16 -#, fuzzy -#| msgid "WebRTC server" msgid "WebRTC" -msgstr "Услуга на WebRTC" +msgstr "WebRTC" #: modules/janus/manifest.py:16 msgid "Web conference" @@ -3286,10 +3266,8 @@ msgid "Web chat" msgstr "" #: modules/jsxc/manifest.py:16 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "Client Apps" msgid "Client" -msgstr "Клиентски приложения" +msgstr "Клиент" #: modules/kiwix/__init__.py:21 msgid "" @@ -3365,20 +3343,16 @@ msgstr "" "премахнат, за да бъде освободено мястото на диска." #: modules/kiwix/manifest.py:23 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Offline reader" -msgstr "Уикипедия извън мрежата" +msgstr "Четене извън мрежата" #: modules/kiwix/manifest.py:24 msgid "Archival" msgstr "" #: modules/kiwix/manifest.py:26 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Wikipedia" -msgstr "Уикипедия извън мрежата" +msgstr "Уикипедия" #: modules/kiwix/templates/kiwix-add-package.html:24 #, python-format @@ -3622,16 +3596,12 @@ msgid "FluffyChat" msgstr "FluffyChat" #: modules/matrixsynapse/manifest.py:101 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Chatroom" msgid "Chat room" -msgstr "Канал в IRC" +msgstr "Стая за разговори" #: modules/matrixsynapse/manifest.py:105 -#, fuzzy -#| msgid "Matrix Synapse" msgid "Matrix server" -msgstr "Matrix Synapse" +msgstr "Сървър на Matrix" #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 #: modules/miniflux/templates/miniflux.html:12 @@ -3919,14 +3889,10 @@ msgstr "" "характер." #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Updated server." msgid "Game server" -msgstr "Настройките на сървъра са променени." +msgstr "Сървър за игри" #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Block Sandbox" msgid "Block sandbox" msgstr "Пясъчник с блокове" @@ -3985,10 +3951,8 @@ msgid "totem" msgstr "" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "Updated server." msgid "Media server" -msgstr "Настройките на сървъра са променени." +msgstr "Сървър за медия" #: modules/minidlna/manifest.py:116 msgid "Television" @@ -3999,10 +3963,8 @@ msgid "UPnP" msgstr "" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "MiniDLNA" msgid "DLNA" -msgstr "MiniDLNA" +msgstr "DLNA" #: modules/minidlna/views.py:33 msgid "Updated media directory" @@ -4087,8 +4049,6 @@ msgid "RSS Guard" msgstr "" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "News Feed Reader" msgid "Feed reader" msgstr "Четец на абонаменти за новини" @@ -5655,14 +5615,12 @@ msgid "Tunnelblick" msgstr "" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS Server" msgid "VPN server" -msgstr "Сървър на DNS" +msgstr "Сървър за ВЧМ" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 msgid "Remote access" -msgstr "" +msgstr "Отдалечен достъп" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6050,10 +6008,8 @@ msgid "Proxy server" msgstr "" #: modules/privoxy/manifest.py:10 modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Домейн в местната мрежа" +msgstr "Местна мрежа" #: modules/quassel/__init__.py:24 #, python-brace-format @@ -6235,10 +6191,8 @@ msgid "" msgstr "" #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "Адрес на ел. поща" #: modules/rssbridge/__init__.py:21 msgid "" @@ -6289,14 +6243,12 @@ msgid "Allow this application to be used by anyone who can reach it." msgstr "" #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "Създател на емисии на RSS" +msgstr "Създаване на емисии" #: modules/rssbridge/manifest.py:16 msgid "News" -msgstr "" +msgstr "Новини" #: modules/samba/__init__.py:23 msgid "" @@ -6366,22 +6318,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Мрежов интерфейс" +msgstr "Мрежов диск" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Restore" msgid "Media storage" -msgstr "Възстановяване" +msgstr "Хранилище за медиа" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Резервни копия" +msgstr "Хранилище за резервни копия" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -6508,10 +6454,8 @@ msgid "Strict" msgstr "" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Server" msgid "Web search" -msgstr "Уеб сървър" +msgstr "Търсене в интернет" #: modules/searx/manifest.py:17 msgid "Metasearch Engine" @@ -6730,18 +6674,16 @@ msgid "Encryption method. Must match setting on server." msgstr "" #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Шифроване" +msgstr "Шифрован тунел" #: modules/shadowsocks/manifest.py:21 msgid "Entry point" -msgstr "" +msgstr "Входна точка" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 msgid "Shadowsocks" -msgstr "" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7670,8 +7612,6 @@ msgid "Orbot: Proxy with Tor" msgstr "Орбот: Прокси с Тор" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" msgstr "Услуга на Onion" @@ -7864,16 +7804,16 @@ msgid "FreedomBox Updated" msgstr "FreedomBox е обновен" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Обновяване на софтуера" +msgstr "Ръчно обновяване на софтуера" #: modules/upgrades/__init__.py:140 msgid "" "Automatic software update runs daily by default. For the first time, " "manually run it now." msgstr "" +"Автоматичното обновяване по подразбиране се извършва всеки ден. Извършете го " +"за първи път ръчно сега." #: modules/upgrades/__init__.py:242 msgid "Could not start distribution update" @@ -8775,10 +8715,9 @@ msgid "Generic" msgstr "" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Грешка {name}: {exception_message}" +msgstr "Грешка {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -8830,22 +8769,19 @@ msgid "Updating app" msgstr "Обновяване на приложение" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Грешка при инсталиране на приложението: {error}" +msgstr "Грешка при инсталиране на приложението: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Грешка при поправка на приложението: {error}" +msgstr "Грешка при поправка на приложението: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Грешка при обновяване на приложението: {error}" +msgstr "Грешка при обновяване на приложението: {exception}" #: setup.py:85 msgid "App installed." From 0eac9c326035af4ba8abd406de204ff2892eb599 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 22 Oct 2024 10:01:28 +0000 Subject: [PATCH 04/32] Translated using Weblate (Albanian) Currently translated at 99.6% (1764 of 1770 strings) --- plinth/locale/sq/LC_MESSAGES/django.po | 325 +++++++++---------------- 1 file changed, 118 insertions(+), 207 deletions(-) diff --git a/plinth/locale/sq/LC_MESSAGES/django.po b/plinth/locale/sq/LC_MESSAGES/django.po index 7b2253631..2906c5ef9 100644 --- a/plinth/locale/sq/LC_MESSAGES/django.po +++ b/plinth/locale/sq/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-10 05:16+0000\n" +"PO-Revision-Date: 2024-10-23 05:15+0000\n" "Last-Translator: Besnik Bleta \n" "Language-Team: Albanian \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.8-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -864,14 +864,12 @@ msgstr "" #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Dhënie Kartelash & Copëzash" +msgstr "Dhënie kartelash" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1108,19 +1106,15 @@ msgstr "Ka tashmë një bibliotekë me atë emër." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "E-libër" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "Bibliotekë E-librash" +msgstr "Bibliotekë" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "Bibliotekë E-librash" +msgstr "Lexues E-librash" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1376,15 +1370,15 @@ msgstr "Listë e pavlefshme URI-sh Shërbyesi STUN/TURN" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Konferencë me video" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1469,24 +1463,18 @@ msgid "Bittorrent client written in Python/PyGTK" msgstr "Klient Bittorrent i shkruar në Python/PyGTK" #: modules/deluge/manifest.py:21 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "BitTorrent Web Client" msgid "BitTorrent" -msgstr "Klient Web BitTorrent" +msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Nis klientin web" +msgstr "Klient web" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -2046,25 +2034,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Fshehtëzim" +msgstr "Shkëmbim mesazhesh i fshehtëzuar" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Fjalosje me zë" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Dhomë Me Video" +msgstr "Fjalosje me video" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 #, python-format @@ -2208,18 +2192,16 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" -msgstr "Shërbyes Email-i" +msgstr "Shërbyes email-esh" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" -msgstr "" +msgstr "Kontroll të padëshiruarish" #: modules/email/templates/email-aliases.html:13 #: modules/email/templates/email.html:15 @@ -2378,26 +2360,22 @@ msgstr "Wiki" #: modules/featherwiki/manifest.py:18 modules/infinoted/manifest.py:46 #: modules/tiddlywiki/manifest.py:20 msgid "Note taking" -msgstr "" +msgstr "Mbajtje shënimesh" #: modules/featherwiki/manifest.py:18 modules/ikiwiki/manifest.py:15 #: modules/mediawiki/manifest.py:25 modules/tiddlywiki/manifest.py:21 #: modules/wordpress/manifest.py:26 -#, fuzzy -#| msgid "Website Security" msgid "Website" -msgstr "Siguri Sajti" +msgstr "Sajt" #: modules/featherwiki/manifest.py:18 modules/tiddlywiki/manifest.py:25 msgid "Quine" -msgstr "" +msgstr "Quine" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Debian:" +msgstr "Jo-Debian" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 @@ -2609,10 +2587,8 @@ msgstr "" "a>." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Ujdisje e Plotësuar!" +msgstr "Ujdisje e plotësuar! Hapat vijues:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2620,14 +2596,16 @@ msgid "" "Initial setup has been completed. Perform the next steps to make your " "{box_name} operational." msgstr "" +"Ujdisja fillestare është plotësuar. Që ta bëni funksional {box_name} tuaj, " +"kryeni hapat vijues." #: modules/first_boot/__init__.py:66 msgid "Next steps" -msgstr "" +msgstr "Hapat vijues" #: modules/first_boot/__init__.py:73 msgid "See next steps" -msgstr "" +msgstr "Shihni hapat vijues" #: modules/first_boot/forms.py:14 #, python-brace-format @@ -2645,10 +2623,8 @@ msgid "Firstboot Wizard Secret" msgstr "E fshehtë Për Skenën e Parë të Ndihmësit" #: modules/first_boot/templates/firstboot_complete.html:11 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Ujdisje e Plotësuar!" +msgstr "Ujdisje e Plotësuar! Hapat Vijues:" #: modules/first_boot/templates/firstboot_complete.html:18 #, python-format @@ -2656,6 +2632,9 @@ msgid "" "Automatic software update " "runs daily by default. For the first time, manually run it now." msgstr "" +"Përditësimi i automatizuar i " +"software-it, si parazgjedhje, kryhet një herë në ditë. Për herën e parë, " +"kryejeni dorazi tani." #: modules/first_boot/templates/firstboot_complete.html:27 #: modules/upgrades/templates/upgrades_configure.html:108 @@ -2667,6 +2646,8 @@ msgstr "Përditësoje tani" msgid "" "Review privacy options." msgstr "" +"Shqyrtoni mundësi " +"privatësie." #: modules/first_boot/templates/firstboot_complete.html:46 #, python-format @@ -2674,12 +2655,16 @@ msgid "" "Review and setup network " "connections. Change the default Wi-Fi password, if applicable." msgstr "" +"Shqyrtoni dhe ujdisni lidhje " +"në rrjet. Nëse ka vend, ndryshoni fjalëkalimin parazgjedhje për Wi-Fi." #: modules/first_boot/templates/firstboot_complete.html:57 #, python-format msgid "" "Configure a domain name." msgstr "" +"Formësoni një emër " +"përkatësie." #: modules/first_boot/templates/firstboot_complete.html:67 #, python-format @@ -2687,6 +2672,8 @@ msgid "" "Configure and schedule remote backups." msgstr "" +"Formësoni dhe vini në plan kopjeruajtje të largëta." #: modules/first_boot/templates/firstboot_complete.html:78 #, python-format @@ -2694,6 +2681,8 @@ msgid "" "Put %(box_name)s to use by installing apps." msgstr "" +"Bëjeni të dobishëm %(box_name)s duke instaluar aplikacione." #: modules/first_boot/templates/firstboot_welcome.html:29 msgid "Start Setup" @@ -2798,18 +2787,16 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Strehim i Thjeshtë Git" +msgstr "Strehim Git" #: modules/gitweb/manifest.py:37 msgid "Version control" -msgstr "" +msgstr "Kontroll versionesh" #: modules/gitweb/manifest.py:37 msgid "Developer tool" -msgstr "" +msgstr "Mjete zhvilluesish" #: modules/gitweb/templates/gitweb_configure.html:13 msgid "Manage Repositories" @@ -3286,16 +3273,14 @@ msgstr "Ndërmjetës I2P" #: modules/i2p/manifest.py:43 modules/tor/manifest.py:59 #: modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" -msgstr "Rrjet Anonimiteti" +msgstr "Rrjet anonimiteti" #: modules/i2p/manifest.py:43 modules/kiwix/manifest.py:25 #: modules/shadowsocks/manifest.py:19 modules/shadowsocksserver/manifest.py:18 #: modules/tor/manifest.py:60 modules/torproxy/manifest.py:57 msgid "Censorship resistance" -msgstr "" +msgstr "Rezistencë ndaj censurës" #: modules/i2p/templates/i2p.html:12 msgid "I2P Proxies and Tunnels" @@ -3381,7 +3366,7 @@ msgstr "Fjalëkalim Llogarie Përgjegjësi" #: modules/ikiwiki/manifest.py:15 modules/wordpress/manifest.py:26 msgid "Blog" -msgstr "" +msgstr "Blog" #: modules/ikiwiki/templates/ikiwiki_configure.html:12 msgid "Manage Wikis and Blogs" @@ -3500,7 +3485,7 @@ msgstr "" #: modules/infinoted/manifest.py:46 msgid "Collaborative editing" -msgstr "" +msgstr "Përpunim në bashkëpunim" #: modules/janus/__init__.py:23 msgid "Janus is a lightweight WebRTC server." @@ -3530,14 +3515,12 @@ msgid "Janus Video Room" msgstr "Janus Video Room" #: modules/janus/manifest.py:16 -#, fuzzy -#| msgid "Web Server" msgid "WebRTC" -msgstr "Shërbyes" +msgstr "WebRTC" #: modules/janus/manifest.py:16 msgid "Web conference" -msgstr "" +msgstr "Konferencë Web" #: modules/janus/templates/janus_video_room.html:205 #: modules/jsxc/templates/jsxc_launch.html:117 templates/base.html:254 @@ -3561,16 +3544,12 @@ msgid "Chat Client" msgstr "Klient Fjalosjesh" #: modules/jsxc/manifest.py:16 -#, fuzzy -#| msgid "Web Search" msgid "Web chat" -msgstr "Kërkim në Web" +msgstr "Fjalosje në Web" #: modules/jsxc/manifest.py:16 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Client" msgid "Client" -msgstr "Klient IRC" +msgstr "Klient" #: modules/kiwix/__init__.py:21 msgid "" @@ -3645,20 +3624,16 @@ msgstr "" "menjëherë, për të kursyer hapësirë disku." #: modules/kiwix/manifest.py:23 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Offline reader" -msgstr "Wikipedia Jo në linjë" +msgstr "Lexues jashtë linje" #: modules/kiwix/manifest.py:24 msgid "Archival" -msgstr "" +msgstr "Arkivore" #: modules/kiwix/manifest.py:26 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Wikipedia" -msgstr "Wikipedia Jo në linjë" +msgstr "Wikipedia" #: modules/kiwix/templates/kiwix-add-package.html:24 #, python-format @@ -3938,16 +3913,12 @@ msgid "FluffyChat" msgstr "FluffyChat" #: modules/matrixsynapse/manifest.py:101 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Chatroom" msgid "Chat room" -msgstr "Dhomë IRC" +msgstr "Dhomë fjalosjeje" #: modules/matrixsynapse/manifest.py:105 -#, fuzzy -#| msgid "Media streaming server" msgid "Matrix server" -msgstr "Shërbyes transmetimi mediash" +msgstr "Shërbyes Matrix" #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 #: modules/miniflux/templates/miniflux.html:12 @@ -4294,20 +4265,16 @@ msgstr "" "dëmtimi." #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Updated server." msgid "Game server" -msgstr "Shërbyesi u përditësua." +msgstr "Shërbyes lojërash" #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Block Sandbox" msgid "Block sandbox" -msgstr "Bankëprovë Blloqesh" +msgstr "Bankëprovë blloqesh" #: modules/minetest/manifest.py:49 msgid "Platform" -msgstr "" +msgstr "Platformë" #: modules/minetest/templates/minetest.html:17 modules/networks/forms.py:105 #: modules/networks/forms.py:145 @@ -4369,24 +4336,20 @@ msgid "totem" msgstr "totem" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "Simple Media Server" msgid "Media server" -msgstr "Shërbyes i Thjeshtë Mediash" +msgstr "Shërbyes mediash" #: modules/minidlna/manifest.py:116 msgid "Television" -msgstr "" +msgstr "Televizion" #: modules/minidlna/manifest.py:116 msgid "UPnP" -msgstr "" +msgstr "UPnP" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "MiniDLNA" msgid "DLNA" -msgstr "MiniDLNA" +msgstr "DLNA" #: modules/minidlna/views.py:33 msgid "Updated media directory" @@ -4481,24 +4444,22 @@ msgid "RSS Guard" msgstr "RSS Guard" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "News Feed Reader" msgid "Feed reader" -msgstr "Lexues Prurjesh Lajmesh" +msgstr "Lexues prurjesh" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 msgid "News aggregation" -msgstr "" +msgstr "Grumbullim lajmesh" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "RSS" -msgstr "" +msgstr "RSS" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: modules/miniflux/templates/miniflux.html:14 msgid "" @@ -4769,7 +4730,7 @@ msgstr "" #: modules/names/templates/names.html:66 msgid "Global" -msgstr "" +msgstr "Globale" #: modules/names/templates/names.html:68 msgid "Link" @@ -6113,17 +6074,15 @@ msgstr "" #: modules/nextcloud/manifest.py:56 modules/syncthing/manifest.py:58 msgid "File sync" -msgstr "" +msgstr "Njëkohësim kartelash" #: modules/nextcloud/manifest.py:56 modules/sharing/__init__.py:34 msgid "Sharing" msgstr "Dhënie" #: modules/nextcloud/manifest.py:56 -#, fuzzy -#| msgid "Group Share" msgid "Groupware" -msgstr "Pjesë Grupi" +msgstr "" #: modules/nextcloud/views.py:53 msgid "Password update failed. Please choose a stronger password." @@ -6172,14 +6131,12 @@ msgid "Tunnelblick" msgstr "Tunnelblick" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS server" msgid "VPN server" -msgstr "Shërbyes DNS" +msgstr "Shërbyes VPN" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 msgid "Remote access" -msgstr "" +msgstr "Hyrje së largëti" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6613,20 +6570,16 @@ msgstr "Hapni {url} me ndërmjetësin {proxy} në tcp{kind}" #: modules/privoxy/manifest.py:10 msgid "Ad blocker" -msgstr "" +msgstr "Bllokues reklamash" #: modules/privoxy/manifest.py:10 modules/shadowsocks/manifest.py:18 #: modules/torproxy/manifest.py:55 -#, fuzzy -#| msgid "Gobby Server" msgid "Proxy server" -msgstr "Shërbyes Gobby" +msgstr "Shërbyes ndërmjetës" #: modules/privoxy/manifest.py:10 modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Përkatësi Rrjeti Vendor" +msgstr "Rrjet vendor" #: modules/quassel/__init__.py:24 #, python-brace-format @@ -6671,7 +6624,7 @@ msgstr "Quasseldroid" #: modules/quassel/manifest.py:54 msgid "IRC" -msgstr "" +msgstr "IRC" #: modules/radicale/__init__.py:25 #, python-brace-format @@ -6778,22 +6731,20 @@ msgstr "" "e kërkimit do të shfaqë kalendarët dhe librat ekzistues të adresave." #: modules/radicale/manifest.py:91 -#, fuzzy -#| msgid "GNOME Calendar" msgid "Calendar" -msgstr "Kalendar Gnome" +msgstr "Kalendar" #: modules/radicale/manifest.py:91 modules/roundcube/manifest.py:23 msgid "Contacts" -msgstr "" +msgstr "Kontakte" #: modules/radicale/manifest.py:91 msgid "CalDAV" -msgstr "" +msgstr "CalDAV" #: modules/radicale/manifest.py:91 msgid "CardDAV" -msgstr "" +msgstr "CardDAV" #: modules/radicale/views.py:32 msgid "Access rights configuration updated" @@ -6867,10 +6818,8 @@ msgstr "" "lidhen." #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "Email" #: modules/rssbridge/__init__.py:21 msgid "" @@ -6922,16 +6871,12 @@ msgid "Allow this application to be used by anyone who can reach it." msgstr "Të lejohet ky aplikacion të përdoret nga cilido që mund ta kapë." #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "Prodhues Prurjesh RSS" +msgstr "Prodhues prurjesh" #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "FluxNews" msgid "News" -msgstr "FluxNews" +msgstr "Lajme" #: modules/samba/__init__.py:23 msgid "" @@ -7012,22 +6957,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Ndërfaqe Rrjeti" +msgstr "Disk rrjeti" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Media streaming server" msgid "Media storage" -msgstr "Shërbyes transmetimi mediash" +msgstr "Depozitim mediash" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Kopjeruajtje" +msgstr "Depozitim kopjeruajtjesh" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -7170,14 +7109,12 @@ msgid "Strict" msgstr "Strikt" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Search" msgid "Web search" msgstr "Kërkim në Web" #: modules/searx/manifest.py:17 msgid "Metasearch Engine" -msgstr "" +msgstr "Motor Tejkërkimesh" #: modules/security/forms.py:13 msgid "Fail2Ban (recommended)" @@ -7334,16 +7271,12 @@ msgid "Shaarlier" msgstr "Shaarlier" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "Link" msgid "Link blog" -msgstr "Lidhje" +msgstr "" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "in use" msgid "Single user" -msgstr "në përdorim" +msgstr "Një përdorues" #: modules/shadowsocks/__init__.py:18 modules/shadowsocksserver/__init__.py:18 msgid "" @@ -7414,22 +7347,16 @@ msgid "Encryption method. Must match setting on server." msgstr "Metodë fshehtëzimi. Duhet të përputhet me atë të caktuar te shërbyesi." #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Fshehtëzim" +msgstr "Tunel i fshehtëzuar" #: modules/shadowsocks/manifest.py:21 -#, fuzzy -#| msgid "Endpoint" msgid "Entry point" -msgstr "Pikëmbarim" +msgstr "Pikë hyrjeje" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 -#, fuzzy -#| msgid "Shadowsocks Client" msgid "Shadowsocks" -msgstr "Klient për Shadowsocks" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7465,10 +7392,8 @@ msgid "Encryption method. Clients must use the same setting." msgstr "Metodë fshehtëzimi. Klientët duhet të përdorin të njëjtin rregullim." #: modules/shadowsocksserver/manifest.py:20 -#, fuzzy -#| msgid "Endpoint" msgid "Exit point" -msgstr "Pikëmbarim" +msgstr "Pikë daljeje" #: modules/sharing/__init__.py:17 #, python-brace-format @@ -7530,10 +7455,8 @@ msgid "Shares should be either public or shared with at least one group" msgstr "Pjesët duhet të jenë ose publike, ose të ndara me të paktën një grup" #: modules/sharing/manifest.py:19 modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Sharing" msgid "Web sharing" -msgstr "Dhënie" +msgstr "Dhënie në Web" #: modules/sharing/templates/sharing.html:18 #: modules/sharing/templates/sharing.html:21 @@ -8269,15 +8192,15 @@ msgstr "Ngarkoni nga ky kompjuter një kartelë ekzistuese TiddlyWiki." #: modules/tiddlywiki/manifest.py:22 msgid "Journal" -msgstr "" +msgstr "Ditar" #: modules/tiddlywiki/manifest.py:23 msgid "Digital garden" -msgstr "" +msgstr "Kopsht dixhital" #: modules/tiddlywiki/manifest.py:24 msgid "Zettelkasten" -msgstr "" +msgstr "Zettelkasten" #: modules/tiddlywiki/templates/tiddlywiki_delete.html:18 msgid "" @@ -8446,10 +8369,8 @@ msgid "Orbot: Proxy with Tor" msgstr "Orbot: Ndërmjetës me Tor" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" -msgstr "Shërbim Onion" +msgstr "Shërbime Onion" #: modules/tor/manifest.py:58 msgid "Relay" @@ -8628,7 +8549,7 @@ msgstr "Tiny Tiny RSS" #: modules/ttrss/manifest.py:10 msgid "TTRSS-Reader" -msgstr "" +msgstr "Lexues TTRSS" #: modules/ttrss/manifest.py:25 msgid "Geekttrss" @@ -8662,16 +8583,16 @@ msgid "FreedomBox Updated" msgstr "FreedomBox-i u Përditësua" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Përditësim Software-i" +msgstr "Xhirojeni dorazi përditësimin e software-it" #: modules/upgrades/__init__.py:140 msgid "" "Automatic software update runs daily by default. For the first time, " "manually run it now." msgstr "" +"Përditësimi i automatizuar i software-it, si parazgjedhje, kryhet një herë " +"në ditë. Për herën e parë, kryejeni dorazi tani." #: modules/upgrades/__init__.py:242 msgid "Could not start distribution update" @@ -9286,10 +9207,8 @@ msgstr "" "trafiku." #: modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "IRC Client" msgid "VPN client" -msgstr "Klient IRC" +msgstr "Klient VPN" #: modules/wireguard/templates/wireguard.html:10 msgid "As a Server" @@ -9574,7 +9493,7 @@ msgstr "" #: modules/wordpress/manifest.py:26 msgid "Content management system" -msgstr "" +msgstr "Sistem administrimi lënde" #: modules/zoph/__init__.py:24 #, python-brace-format @@ -9631,13 +9550,11 @@ msgstr "" #: modules/zoph/manifest.py:26 msgid "Photo" -msgstr "" +msgstr "Foto" #: modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Photo Organizer" msgid "Organizer" -msgstr "Sistemues Fotografish" +msgstr "Sistemues" #: modules/zoph/templates/zoph-pre-setup.html:15 #: modules/zoph/templates/zoph-pre-setup.html:28 @@ -9658,10 +9575,9 @@ msgid "Generic" msgstr "Elementar" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Gabim: {name}: {exception_message}" +msgstr "Gabim: {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -9714,22 +9630,19 @@ msgid "Updating app" msgstr "Po përditësohet aplikacioni" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Gabim në instalimin e aplikacionit: {error}" +msgstr "Gabim në instalimin e aplikacionit: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Gabim në riparimin e aplikacionit: {error}" +msgstr "Gabim në riparimin e aplikacionit: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Gabim në përditësimin e aplikacionit: {error}" +msgstr "Gabim në përditësimin e aplikacionit: {exception}" #: setup.py:85 msgid "App installed." @@ -9847,10 +9760,8 @@ msgid "Service %(service_name)s is not running." msgstr "Shërbimi %(service_name)s s’po xhiron." #: templates/apps.html:29 -#, fuzzy -#| msgid "Search the web" msgid "Search with tags" -msgstr "Kërkoni në internet" +msgstr "Kërkoni me etiketa" #: templates/base.html:31 msgid "" From f12e634bc9054d844f294b5adbb08c10a7619d8d Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Thu, 17 Oct 2024 17:36:14 +0300 Subject: [PATCH 05/32] users: Delete or move home folder when user is deleted or renamed On user deletion, user's home folder is also deleted. Admins have an option to avoid deleting user's home by inactivating the user instead. This commit also removes user deletion buttons from the user's list page and adds this option to the user edit page. The user's edit form asks for a confirmation if the user deletion is requested. This change also means that the confirmation password is now required to delete a user. Also: - Add a simple username validation to the privileged actions. - Functional tests: Create a fixture to login as an admin before every test. - Functional tests: Add a test to check that SSH passwordless login works after user is renamed to validate correct SSH related path permissions. - Privileged tests: Add `test_` prefix to the generated random string which makes easier to check and cleanup created home folders. - Minor quote fixes. Tests performed in stable and testing containers: - Run all the users module tests twice, no failures in tests. - When user is the last admin, both "Active" and "Delete user" checkboxes are disabled. Closes #2451. [sunil] - Refactor the JS code: - Ensure that DOM elements are lookup after DOM content is loaded. - Styling changes. Reduce the number of globals, name the global names somewhat more unique. - Click the button instead of submitting the form to disable the button. - Template changes: - Add a body for the confirmation dialog to talk about disabling the user and deleting the home directory. - Change the label of the confirm button to make it more explicit (recommendation from many UX guides). - Styling. - Functional tests: - Fix visibility checking of an element to use the correct splinter API. - Simplify clicking the edit user link. - Minor update to form checkbox help text. Signed-off-by: Veiko Aasa Signed-off-by: Sunil Mohan Adapa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/users/forms.py | 24 +++++- plinth/modules/users/privileged.py | 85 ++++++++++++++++--- plinth/modules/users/static/users.js | 60 +++++++++++++ .../modules/users/templates/users_delete.html | 26 ------ .../modules/users/templates/users_list.html | 13 +-- .../modules/users/templates/users_update.html | 38 +++++++++ plinth/modules/users/tests/test_functional.py | 52 ++++++++---- plinth/modules/users/tests/test_privileged.py | 47 +++++++--- plinth/modules/users/tests/test_views.py | 16 ++-- plinth/modules/users/urls.py | 2 - plinth/modules/users/views.py | 71 ++-------------- plinth/tests/functional/__init__.py | 18 ++-- 12 files changed, 299 insertions(+), 153 deletions(-) create mode 100644 plinth/modules/users/static/users.js delete mode 100644 plinth/modules/users/templates/users_delete.html diff --git a/plinth/modules/users/forms.py b/plinth/modules/users/forms.py index e4297a662..40419dda4 100644 --- a/plinth/modules/users/forms.py +++ b/plinth/modules/users/forms.py @@ -19,6 +19,7 @@ import plinth.forms import plinth.modules.ssh.privileged as ssh_privileged from plinth.modules import first_boot from plinth.utils import is_user_admin +from plinth.views import messages_error from . import get_last_admin_user, privileged from .components import UsersAndGroups @@ -247,11 +248,18 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, PasswordConfirmForm, language = plinth.forms.LanguageSelectionFormMixin.language + delete = forms.BooleanField( + label=gettext_lazy('Delete user'), required=False, + help_text=gettext_lazy( + 'Deleting the user account will also remove all the files ' + 'related to the user. Deleting files can be avoided by ' + 'setting the user account as inactive.')) + class Meta: """Metadata to control automatic form building.""" fields = ('username', 'email', 'groups', 'ssh_keys', 'language', - 'is_active', 'confirm_password') + 'is_active', 'delete', 'confirm_password') model = User widgets = { 'groups': plinth.forms.CheckboxSelectMultipleWithReadOnly(), @@ -274,6 +282,7 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, PasswordConfirmForm, if self.is_last_admin_user: self.fields['is_active'].disabled = True + self.fields['delete'].disabled = True def save(self, commit=True): """Update LDAP user name and groups after saving user model.""" @@ -287,6 +296,19 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, PasswordConfirmForm, user.save() self.save_m2m() + if self.cleaned_data.get('delete'): + try: + # Remove system user + privileged.remove_user(user.get_username(), auth_username, + confirm_password) + except Exception as error: + messages_error(self.request, _('Failed to delete user.'), + error) + else: + # Remove Django user + user.delete() + return user + old_groups = privileged.get_user_groups(self.username) old_groups = [group for group in old_groups if group] diff --git a/plinth/modules/users/privileged.py b/plinth/modules/users/privileged.py index 23598b6b2..f672b8282 100644 --- a/plinth/modules/users/privileged.py +++ b/plinth/modules/users/privileged.py @@ -3,6 +3,7 @@ import logging import os +import pathlib import re import shutil import subprocess @@ -45,6 +46,12 @@ def _validate_password(username, password): raise PermissionError('Invalid credentials') +def _validate_username(username): + """Validate username.""" + if pathlib.Path(username).parts[-1] != username: + raise ValueError('Invalid username') + + @privileged def first_setup(): """Perform initial setup of LDAP.""" @@ -277,8 +284,7 @@ def _configure_ldapscripts(): def _lock_ldap_user(username: str): """Lock user.""" - if not _get_user_ids(username): - # User not found + if not _user_exists(username): return None # Replace command adds the attribute if it doesn't exist. @@ -286,13 +292,12 @@ def _lock_ldap_user(username: str): replace: pwdAccountLockedTime pwdAccountLockedTime: 000001010000Z ''' - _run(["ldapmodifyuser", username], input=input.encode()) + _run(['ldapmodifyuser', username], input=input.encode()) def _unlock_ldap_user(username: str): """Unlock user.""" - if not _get_user_ids(username): - # User not found + if not _user_exists(username): return None # Replace command without providing a value will remove the attribute @@ -344,41 +349,83 @@ def _disconnect_samba_user(username): raise +def _get_user_home(username): + """Return the user home directory.""" + output = subprocess.check_output(['getent', 'passwd', username], text=True) + return pathlib.Path(output.split(':')[5]) + + @privileged def create_user(username: str, password: secret_str, auth_user: str | None = None, auth_password: secret_str | None = None): """Create an LDAP user, set password and flush cache.""" + _validate_username(username) _validate_user(auth_user, auth_password) _run(['ldapadduser', username, 'users']) + _set_user_password(username, password) _flush_cache() _set_samba_user(username, password) @privileged -def remove_user(username: str, password: secret_str | None = None): +def remove_user(username: str, auth_user: str, auth_password: secret_str): """Remove an LDAP user.""" + _validate_username(username) + _validate_user(auth_user, auth_password) groups = _get_user_groups(username) - # require authentication if the user is last admin user - if _get_group_users('admin') == [username]: - _validate_password(username, password) - _delete_samba_user(username) for group in groups: _remove_user_from_group(username, group) - _run(['ldapdeleteuser', username]) + if _user_exists(username): + # remove the home folder if it's owned by the user + home_folder = _get_user_home(username) + if home_folder.is_dir(): + try: + owner = home_folder.owner() + except KeyError: # owner not found + pass + else: + if owner == username: + shutil.rmtree(home_folder, ignore_errors=True) + + _run(['ldapdeleteuser', username]) _flush_cache() +def _rename_ldap_user(old_username: str, new_username: str, + new_home: pathlib.Path | None): + """Rename LDAP user and user parameters.""" + _run(['ldaprenameuser', old_username, new_username]) + + input = f'''changetype: modify +replace: cn +cn: {new_username} +- +replace: gecos +gecos: {new_username} +''' + + if new_home: + input += f'''- +replace: homeDirectory +homeDirectory: {str(new_home)} +''' + + _run(['ldapmodifyuser', new_username], input=input.encode()) + + @privileged def rename_user(old_username: str, new_username: str): """Rename an LDAP user.""" + _validate_username(old_username) + _validate_username(new_username) groups = _get_user_groups(old_username) _delete_samba_user(old_username) @@ -386,7 +433,16 @@ def rename_user(old_username: str, new_username: str): for group in groups: _remove_user_from_group(old_username, group) - _run(['ldaprenameuser', old_username, new_username]) + old_home = _get_user_home(old_username) + new_home = old_home.with_name(new_username) + + if new_home.exists(): + new_home = None # Do not rename home + else: + if old_home.is_dir(): + old_home.rename(new_home) + + _rename_ldap_user(old_username, new_username, new_home) for group in groups: _add_user_to_group(new_username, group) @@ -465,6 +521,11 @@ def _get_user_ids(username: str) -> str | None: return process.stdout.decode().strip() +def _user_exists(username): + """Return whether the user exists.""" + return _get_user_ids(username) is not None + + def _get_group_users(groupname): """Return list of members in the group.""" try: diff --git a/plinth/modules/users/static/users.js b/plinth/modules/users/static/users.js new file mode 100644 index 000000000..1b62a65bb --- /dev/null +++ b/plinth/modules/users/static/users.js @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +/** + * @licstart The following is the entire license notice for the JavaScript + * code in this page. + * + * This file is part of FreedomBox. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * @licend The above is the entire license notice for the JavaScript code + * in this page. + */ + +var deleteConfirmed = false; + +document.addEventListener('DOMContentLoaded', (event) => { + const form = document.querySelector('form.form-update'); + form.addEventListener('submit', onUserUpdateSubmit); + + const confirmDeleteButton = document.querySelector( + '#user-delete-confirm-dialog button.confirm'); + confirmDeleteButton.addEventListener('click', () => { + onUserDeleteConfirmed(form); + }); +}); + +// Show the confirmation dialog if the delete checkbox is selected +function onUserUpdateSubmit(event) { + const deleteUserCheckbox = document.getElementById('id_delete'); + if (!deleteUserCheckbox.checked) { + return; + } + + if (deleteConfirmed) { // Deletion is already confirmed + deleteConfirmed = false; + return; + } + + event.preventDefault(); + $("#user-delete-confirm-dialog").modal('show'); +}; + +// Submit the user edit form +function onUserDeleteConfirmed(form) { + deleteConfirmed = true; + $('#user-delete-confirm-dialog').modal('hide'); + // Click instead of submit to disable the submission button + form.querySelector('input[type=submit]').click(); +}; diff --git a/plinth/modules/users/templates/users_delete.html b/plinth/modules/users/templates/users_delete.html deleted file mode 100644 index 14b40f648..000000000 --- a/plinth/modules/users/templates/users_delete.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "base.html" %} -{% comment %} -# SPDX-License-Identifier: AGPL-3.0-or-later -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block content %} - -

{% trans "Delete User" %}

- -

- {% blocktrans trimmed with username=object.username %} - Delete user {{ username }} permanently? - {% endblocktrans %} -

- -
- {% csrf_token %} - - -
- -{% endblock %} diff --git a/plinth/modules/users/templates/users_list.html b/plinth/modules/users/templates/users_list.html index edf6ee28f..a06ea9021 100644 --- a/plinth/modules/users/templates/users_list.html +++ b/plinth/modules/users/templates/users_list.html @@ -19,8 +19,8 @@
-
-
+
+
{% for user in object_list %} {% endfor %}
diff --git a/plinth/modules/users/templates/users_update.html b/plinth/modules/users/templates/users_update.html index 35e678bbd..a0de5fd60 100644 --- a/plinth/modules/users/templates/users_update.html +++ b/plinth/modules/users/templates/users_update.html @@ -5,6 +5,7 @@ {% load bootstrap %} {% load i18n %} +{% load static %} {% block content %}

@@ -30,5 +31,42 @@ + + {% endblock %} +{% block page_js %} + +{% endblock %} diff --git a/plinth/modules/users/tests/test_functional.py b/plinth/modules/users/tests/test_functional.py index 4400833d3..1d16e18ce 100644 --- a/plinth/modules/users/tests/test_functional.py +++ b/plinth/modules/users/tests/test_functional.py @@ -54,16 +54,21 @@ _config_page_title_language_map = { @pytest.fixture(scope='module', autouse=True) def fixture_background(session_browser): + """Unset language.""" + yield + functional.login(session_browser) + functional.user_set_language(session_browser, _language_codes['None']) + + +@pytest.fixture(scope='function', autouse=True) +def fixture_login(session_browser): """Login.""" functional.login(session_browser) - yield - functional.user_set_language(session_browser, _language_codes['None']) def test_create_user(session_browser): """Test creating a user.""" - if functional.user_exists(session_browser, 'alice'): - functional.delete_user(session_browser, 'alice') + _delete_user(session_browser, 'alice') functional.create_user(session_browser, 'alice', email='alice@example.com') assert functional.user_exists(session_browser, 'alice') @@ -73,8 +78,7 @@ def test_create_user(session_browser): def test_rename_user(session_browser): """Test renaming a user.""" _non_admin_user_exists(session_browser, 'alice') - if functional.user_exists(session_browser, 'bob'): - functional.delete_user(session_browser, 'bob') + _delete_user(session_browser, 'bob') _rename_user(session_browser, 'alice', 'bob') assert not functional.user_exists(session_browser, 'alice') @@ -94,7 +98,6 @@ def test_non_admin_users_can_change_own_ssh_keys(session_browser): 'alice') _set_ssh_keys(session_browser, 'somekey456') assert _get_ssh_keys(session_browser) == 'somekey456' - functional.login(session_browser) def test_admin_users_can_change_other_users_ssh_keys(session_browser): @@ -121,6 +124,26 @@ def test_users_can_connect_passwordless_over_ssh(session_browser, _should_connect_passwordless_over_ssh(session_browser, tmp_path_factory) +def test_ssh_passwordless_after_user_rename(session_browser, tmp_path_factory): + """Test that users can connect passwordless after user is renamed.""" + username_old = 'bob' + username_new = 'bob2' + functional.app_enable(session_browser, 'ssh') + _non_admin_user_exists(session_browser, username_old, + groups=['freedombox-ssh']) + _delete_user(session_browser, username_new) + _configure_ssh_keys(session_browser, tmp_path_factory, + username=username_old) + _should_connect_passwordless_over_ssh(session_browser, tmp_path_factory, + username=username_old) + + _rename_user(session_browser, username_old, username_new) + + assert functional.user_exists(session_browser, username_new) + _should_connect_passwordless_over_ssh(session_browser, tmp_path_factory, + username=username_new) + + def test_users_cannot_connect_passwordless_over_ssh(session_browser, tmp_path_factory): """Test that users cannot connect passwordless over ssh if the keys aren't @@ -173,8 +196,6 @@ def test_user_states(session_browser, tmp_path_factory): _should_connect_passwordless_over_ssh(session_browser, tmp_path_factory, username=username) - functional.login(session_browser) - def test_admin_users_can_change_own_password(session_browser): """Test that admin users can change their own password.""" @@ -183,7 +204,6 @@ def test_admin_users_can_change_own_password(session_browser): 'testadmin') _change_password(session_browser, 'newpassword456') _can_log_in_with_password(session_browser, 'testadmin', 'newpassword456') - functional.login(session_browser) def test_admin_users_can_change_others_password(session_browser): @@ -191,7 +211,6 @@ def test_admin_users_can_change_others_password(session_browser): _non_admin_user_exists(session_browser, 'alice') _change_password(session_browser, 'secretsecret567', username='alice') _can_log_in_with_password(session_browser, 'alice', 'secretsecret567') - functional.login(session_browser) def test_non_admin_users_can_change_own_password(session_browser): @@ -201,7 +220,6 @@ def test_non_admin_users_can_change_own_password(session_browser): 'alice') _change_password(session_browser, 'newpassword123') _can_log_in_with_password(session_browser, 'alice', 'newpassword123') - functional.login(session_browser) def test_delete_user(session_browser): @@ -211,15 +229,19 @@ def test_delete_user(session_browser): assert not functional.user_exists(session_browser, 'alice') -def _admin_user_exists(session_browser, name): +def _delete_user(session_browser, name): + """Delete a user.""" if functional.user_exists(session_browser, name): functional.delete_user(session_browser, name) + + +def _admin_user_exists(session_browser, name): + _delete_user(session_browser, name) functional.create_user(session_browser, name, groups=['admin']) def _non_admin_user_exists(session_browser, name, groups=[]): - if functional.user_exists(session_browser, name): - functional.delete_user(session_browser, name) + _delete_user(session_browser, name) functional.create_user(session_browser, name, groups=groups) diff --git a/plinth/modules/users/tests/test_privileged.py b/plinth/modules/users/tests/test_privileged.py index 356c6c10c..907b1f96f 100644 --- a/plinth/modules/users/tests/test_privileged.py +++ b/plinth/modules/users/tests/test_privileged.py @@ -36,17 +36,17 @@ def _is_ldap_set_up(): pytestmark: list[pytest.MarkDecorator] = [ pytest.mark.usefixtures('needs_root', 'load_cfg', 'mock_privileged'), - pytest.mark.skipif(not _is_ldap_set_up(), reason="LDAP is not configured") + pytest.mark.skipif(not _is_ldap_set_up(), reason='LDAP is not configured') ] privileged_modules_to_mock = [ 'plinth.modules.users.privileged', 'plinth.modules.security.privileged' ] -def _random_string(length=8): +def _random_string(): """Return a random string created from lower case ascii.""" - return ''.join( - [random.choice(string.ascii_lowercase) for _ in range(length)]) + random_chars = [random.choice(string.ascii_lowercase) for _ in range(8)] + return 'test_' + ''.join(random_chars) def _get_password_hash(username): @@ -135,11 +135,8 @@ def _create_user(username=None, groups=None): def _delete_user(username): """Utility to delete an LDAP and Samba user""" - admin_password = None - if privileged.get_group_users('admin') == [username]: - _, admin_password = _get_admin_user_password() - - privileged.remove_user(username, admin_password) + admin_user, admin_password = _get_admin_user_password() + privileged.remove_user(username, admin_user, admin_password) def _create_admin_if_does_not_exist(): @@ -195,6 +192,13 @@ def test_create_user(): _create_user(username) +def test_create_invalid_user(): + """Test invalid username validation.""" + username = 'invalid/user' + with pytest.raises(ValueError): + _create_user(username) + + def test_change_user_password(): """Test changing user password.""" _create_admin_if_does_not_exist() @@ -293,6 +297,18 @@ def test_rename_user(): _rename_user(existing_user, new_username=new_username) +def test_rename_invalid_user(): + """Test rename invalid username""" + invalid_username = 'invalid/user' + valid_username = _random_string() + + with pytest.raises(ValueError): + _rename_user(invalid_username, new_username=valid_username) + + with pytest.raises(ValueError): + _rename_user(valid_username, new_username=invalid_username) + + def test_delete_user(): """Test to check whether LDAP users can be deleted""" _create_admin_if_does_not_exist() @@ -313,10 +329,17 @@ def test_delete_user(): def test_delete_non_existent_user(): - """Deleting a non-existent user should fail.""" + """Deleting a non-existent user doesn't fail.""" non_existent_user = _random_string() - with pytest.raises(subprocess.CalledProcessError): - privileged.remove_user(non_existent_user) + _delete_user(non_existent_user) + + +def test_delete_invalid_user(): + """Deleting invalid username should fail.""" + invalid_username = 'invalid/user' + + with pytest.raises(ValueError): + _delete_user(invalid_username) def test_groups(): diff --git a/plinth/modules/users/tests/test_views.py b/plinth/modules/users/tests/test_views.py index c0672b053..fdbd6de30 100644 --- a/plinth/modules/users/tests/test_views.py +++ b/plinth/modules/users/tests/test_views.py @@ -84,15 +84,14 @@ def make_request(request, view, as_admin=True, **kwargs): def test_users_list_view(rf): - """Test that users list view has correct view data.""" + """Test users list view has correct view data.""" with (patch('plinth.views.AppView.get_context_data', return_value={'is_enabled': True}), patch('plinth.views.AppView.app', return_value=None)): view = views.UserList.as_view() response, messages = make_request(rf.get('/'), view) - assert response.context_data['last_admin_user'] == 'admin' - assert response.status_code == 200 + assert response.status_code == 200 @pytest.mark.parametrize('username', ['test-new', 'test-neW@2_']) @@ -268,10 +267,15 @@ def test_update_user_without_permissions_view(rf): def test_delete_user_view(rf): """Test that user deletion succeeds.""" user = 'tester' + form_data = { + 'username': user, + 'delete': True, + 'confirm_password': 'adminpassword', + } - url = urls.reverse('users:delete', kwargs={'slug': user}) - request = rf.post(url) - view = views.UserDelete.as_view() + url = urls.reverse('users:edit', kwargs={'slug': user}) + request = rf.post(url, data=form_data) + view = views.UserUpdate.as_view() response, messages = make_request(request, view, as_admin=True, slug=user) assert list(messages)[0].message == 'User {} deleted.'.format(user) diff --git a/plinth/modules/users/urls.py b/plinth/modules/users/urls.py index 2e721b5d5..8123cd21d 100644 --- a/plinth/modules/users/urls.py +++ b/plinth/modules/users/urls.py @@ -16,8 +16,6 @@ urlpatterns = [ re_path(r'^sys/users/create/$', views.UserCreate.as_view(), name='create'), re_path(r'^sys/users/(?P[\w.@+-]+)/edit/$', non_admin_view(views.UserUpdate.as_view()), name='edit'), - re_path(r'^sys/users/(?P[\w.@+-]+)/delete/$', - views.UserDelete.as_view(), name='delete'), re_path(r'^sys/users/(?P[\w.@+-]+)/change_password/$', non_admin_view(views.UserChangePassword.as_view()), name='change_password'), diff --git a/plinth/modules/users/views.py b/plinth/modules/users/views.py index 93a849ab3..357bf1820 100644 --- a/plinth/modules/users/views.py +++ b/plinth/modules/users/views.py @@ -2,17 +2,14 @@ """Django views for user app.""" import django.views.generic -from django.contrib import messages from django.contrib.auth import update_session_auth_hash from django.contrib.auth.models import User from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy -from django.utils.translation import gettext as _ from django.utils.translation import gettext_lazy -from django.views.generic.edit import (CreateView, DeleteView, FormView, - UpdateView) +from django.views.generic.edit import (CreateView, FormView, UpdateView) import plinth.modules.ssh.privileged as ssh_privileged from plinth import translation @@ -20,7 +17,7 @@ from plinth.modules import first_boot from plinth.utils import is_user_admin from plinth.views import AppView -from . import get_last_admin_user, privileged +from . import privileged from .forms import (CreateUserForm, FirstBootForm, UserChangePasswordForm, UserUpdateForm) @@ -64,11 +61,6 @@ class UserList(AppView, ContextMixin, django.views.generic.ListView): title = gettext_lazy('Users') app_id = 'users' - def get_context_data(self, *args, **kwargs): - context = super().get_context_data(*args, **kwargs) - context['last_admin_user'] = get_last_admin_user() - return context - class UserUpdate(ContextMixin, SuccessMessageMixin, UpdateView): """View to update a user's details.""" @@ -113,7 +105,13 @@ class UserUpdate(ContextMixin, SuccessMessageMixin, UpdateView): def form_valid(self, form): """Set the user language if necessary.""" + + is_user_deleted = form.cleaned_data.get('delete') + if is_user_deleted: + self.success_message = gettext_lazy('User %(username)s deleted.') response = super().form_valid(form) + if is_user_deleted: + return HttpResponseRedirect(reverse_lazy('users:index')) # If user is updating their own profile then set the language for # current session too. @@ -124,59 +122,6 @@ class UserUpdate(ContextMixin, SuccessMessageMixin, UpdateView): return response -class UserDelete(ContextMixin, DeleteView): - """Handle deleting users, showing a confirmation dialog first. - - On GET, display a confirmation page. - On POST, delete the user. - """ - - template_name = 'users_delete.html' - model = User - slug_field = 'username' - success_url = reverse_lazy('users:index') - title = gettext_lazy('Delete User') - - def __init__(self, *args, **kwargs): - """Initialize view, override delete method.""" - super().__init__(*args, **kwargs) - - # Avoid a warning with Django 4.0 that delete member should not be - # overridden. Remove this line and _delete() after Django 4.0 reaches - # Debian Stable. - self.delete = self._delete - - def _delete_from_ldap(self): - """Remove user from LDAP and show a success/error message.""" - message = _('User {user} deleted.').format(user=self.kwargs['slug']) - messages.success(self.request, message) - - try: - privileged.remove_user(self.kwargs['slug']) - except Exception: - messages.error(self.request, _('Deleting LDAP user failed.')) - - def _delete(self, *args, **kwargs): - """Set the success message of deleting the user. - - The SuccessMessageMixin doesn't work with the DeleteView on Django1.7, - so set the success message manually here. - """ - output = super().delete(*args, **kwargs) - self._delete_from_ldap() - return output - - def form_valid(self, form): - """Perform additional operations after delete. - - Since Django 4.0, DeleteView inherits form_view and a call to delete() - is not made. - """ - response = super().form_valid(form) # NOQA, pylint: disable=no-member - self._delete_from_ldap() - return response - - class UserChangePassword(ContextMixin, SuccessMessageMixin, FormView): """View to change user password.""" diff --git a/plinth/tests/functional/__init__.py b/plinth/tests/functional/__init__.py index 3f771404f..b1060575f 100644 --- a/plinth/tests/functional/__init__.py +++ b/plinth/tests/functional/__init__.py @@ -664,12 +664,20 @@ def create_user(browser, name, password=None, groups=[], email=None): def delete_user(browser, name): """Delete a user.""" nav_to_module(browser, 'users') - delete_link = browser.links.find_by_href( - f'/plinth/sys/users/{name}/delete/') + browser.links.find_by_href(f'/plinth/sys/users/{name}/edit/').first.click() - with wait_for_page_update(browser): - delete_link.first.click() - submit(browser, form_class='form-delete') + browser.find_by_id('id_delete').check() + browser.find_by_id('id_confirm_password').fill( + config['DEFAULT']['password']) + + browser.find_by_css('.form-update input[type=submit]').first.click() + + confirm_button = browser.find_by_css( + '#user-delete-confirm-dialog button.confirm').first + eventually(lambda: confirm_button.visible) + assert confirm_button.visible + with wait_for_page_update(browser, expected_url='/plinth/sys/users/'): + confirm_button.click() def user_exists(browser, name): From b67ce15f8993ad23e54d7145bc51bce03261dd2a Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Wed, 2 Oct 2024 17:27:53 +0300 Subject: [PATCH 06/32] functional tests: Add pytest testinfra plugin Adds ability to run local commands with functional tests. By default, commands are run locally. It is possible to set remote host connection parameters from pytest command line, for example: `--hosts 'fbx@IP' --ssh-identity-file '.container/ssh/id_ed25519'` For more options, see documentation https://testinfra.readthedocs.io/en/latest/backends.html#ssh. Includes a fixture `host_sudo` to run commands as sudo. Relates to https://salsa.debian.org/freedombox-team/freedombox/-/issues/2451#note_530752. Signed-off-by: Veiko Aasa Reviewed-by: Sunil Mohan Adapa --- plinth/conftest.py | 9 ++++++++- plinth/tests/functional/install.sh | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plinth/conftest.py b/plinth/conftest.py index 9236f7fa6..6ab851b0c 100644 --- a/plinth/conftest.py +++ b/plinth/conftest.py @@ -223,7 +223,7 @@ def fixture_fix_session_browser_screenshots(request): continue value = fixture_def.cached_result[0] - should_take_screenshot = (hasattr(value, "__splinter_browser__") + should_take_screenshot = (hasattr(value, '__splinter_browser__') and splinter_make_screenshot_on_failure and getattr(request.node, 'splinter_failure', True)) @@ -250,3 +250,10 @@ def fixture_fix_session_browser_screenshots(request): } plugin._take_screenshot(**kwargs) + + +@pytest.fixture(name='host_sudo') +def fixture_host_sudo(host): + """Pytest fixture to run commands with sudo.""" + with host.sudo(): + yield host diff --git a/plinth/tests/functional/install.sh b/plinth/tests/functional/install.sh index a51cfbb1c..94ff71e82 100755 --- a/plinth/tests/functional/install.sh +++ b/plinth/tests/functional/install.sh @@ -5,7 +5,8 @@ IFS=$'\n\t' echo "Installing requirements" sudo apt-get install -yq --no-install-recommends \ python3-pytest python3-pytest-django python3-pytest-instafail \ - python3-pytest-xdist python3-pip python3-wheel firefox-esr git smbclient + python3-pytest-xdist python3-pip python3-wheel firefox-esr git smbclient \ + python3-testinfra # Use compatible versions of Splinter and Selenium PIP_VERSION=$(dpkg-query -W -f '${Version}' python3-pip) From df52acc329dc817b8612f70edf2431f1bd3cb4e9 Mon Sep 17 00:00:00 2001 From: Veiko Aasa Date: Fri, 18 Oct 2024 11:44:29 +0300 Subject: [PATCH 07/32] users: tests: functional: Check LDAP information is correct after renaming user Tests performed in stable and testing containers: - All the users module tests pass. Signed-off-by: Veiko Aasa Reviewed-by: Sunil Mohan Adapa --- plinth/modules/users/tests/test_functional.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plinth/modules/users/tests/test_functional.py b/plinth/modules/users/tests/test_functional.py index 1d16e18ce..be92241f5 100644 --- a/plinth/modules/users/tests/test_functional.py +++ b/plinth/modules/users/tests/test_functional.py @@ -75,7 +75,7 @@ def test_create_user(session_browser): assert _get_email(session_browser, 'alice') == 'alice@example.com' -def test_rename_user(session_browser): +def test_rename_user(session_browser, host_sudo): """Test renaming a user.""" _non_admin_user_exists(session_browser, 'alice') _delete_user(session_browser, 'bob') @@ -84,6 +84,10 @@ def test_rename_user(session_browser): assert not functional.user_exists(session_browser, 'alice') assert functional.user_exists(session_browser, 'bob') + assert 'cn: bob' in host_sudo.check_output('ldapfinger bob') + assert host_sudo.user('bob').gecos == 'bob' + assert host_sudo.user('bob').home == '/home/bob' + def test_admin_users_can_change_own_ssh_keys(session_browser): """Test that admin users can change their own ssh keys.""" From e80bd01de774e5ef2c845dd26ca06e6320faa5a7 Mon Sep 17 00:00:00 2001 From: Ettore Atalan Date: Thu, 24 Oct 2024 03:46:25 +0000 Subject: [PATCH 08/32] Translated using Weblate (German) Currently translated at 90.4% (1601 of 1770 strings) --- plinth/locale/de/LC_MESSAGES/django.po | 159 ++++++++----------------- 1 file changed, 51 insertions(+), 108 deletions(-) diff --git a/plinth/locale/de/LC_MESSAGES/django.po b/plinth/locale/de/LC_MESSAGES/django.po index 261d635bb..239fbd350 100644 --- a/plinth/locale/de/LC_MESSAGES/django.po +++ b/plinth/locale/de/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-09-02 10:09+0000\n" +"PO-Revision-Date: 2024-10-24 15:15+0000\n" "Last-Translator: Ettore Atalan \n" "Language-Team: German \n" @@ -19,7 +19,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.8-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -882,10 +882,8 @@ msgstr "" #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Datei- und Snippet-Freigabe" +msgstr "Dateiaustausch" #: modules/bepasty/manifest.py:23 msgid "Pastebin" @@ -1125,19 +1123,15 @@ msgstr "Eine Bibliothek mit diesem Namen ist bereits vorhanden." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "E-Book" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "E-Book-Bibliothek" +msgstr "Bibliothek" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "E-Book-Bibliothek" +msgstr "E-Book-Leser" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1500,17 +1494,13 @@ msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Webclient starten" +msgstr "Webclient" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -2073,25 +2063,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Verschlüsselung" +msgstr "Verschlüsselter Nachrichtenaustausch" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Audiochat" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Videoraum" +msgstr "Videochat" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 #, fuzzy, python-format @@ -2245,14 +2231,12 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" -msgstr "E-Mail Server" +msgstr "E-Mail-Server" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" @@ -2353,10 +2337,8 @@ msgid "View and edit wiki applications" msgstr "Wiki-Anwendungen ansehen und bearbeiten" #: modules/featherwiki/__init__.py:59 modules/featherwiki/manifest.py:9 -#, fuzzy -#| msgid "Create Wiki/Blog" msgid "Feather Wiki" -msgstr "Wiki/Blog anlegen" +msgstr "Feather-Wiki" #: modules/featherwiki/__init__.py:61 msgid "Personal Notebooks" @@ -2401,15 +2383,13 @@ msgstr "Wiki" #: modules/featherwiki/manifest.py:18 modules/infinoted/manifest.py:46 #: modules/tiddlywiki/manifest.py:20 msgid "Note taking" -msgstr "" +msgstr "Notizen machen" #: modules/featherwiki/manifest.py:18 modules/ikiwiki/manifest.py:15 #: modules/mediawiki/manifest.py:25 modules/tiddlywiki/manifest.py:21 #: modules/wordpress/manifest.py:26 -#, fuzzy -#| msgid "Website Security" msgid "Website" -msgstr "Webseiten-Sicherheit" +msgstr "Website" #: modules/featherwiki/manifest.py:18 modules/tiddlywiki/manifest.py:25 msgid "Quine" @@ -2417,17 +2397,13 @@ msgstr "" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Debian:" +msgstr "Nicht-Debian" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 -#, fuzzy -#| msgid "Manage Libraries" msgid "Manage Wikis" -msgstr "Bibliotheken verwalten" +msgstr "Wikis verwalten" #: modules/featherwiki/templates/featherwiki_configure.html:16 #: modules/featherwiki/templates/featherwiki_configure.html:18 @@ -2435,54 +2411,44 @@ msgstr "Bibliotheken verwalten" #: modules/tiddlywiki/templates/tiddlywiki_configure.html:16 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:18 #: modules/tiddlywiki/views.py:47 -#, fuzzy -#| msgid "Create Wiki/Blog" msgid "Create Wiki" -msgstr "Wiki/Blog anlegen" +msgstr "Wiki erstellen" #: modules/featherwiki/templates/featherwiki_configure.html:21 #: modules/featherwiki/templates/featherwiki_configure.html:23 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:21 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:23 -#, fuzzy -#| msgid "Upload File" msgid "Upload Wiki" -msgstr "Datei hochladen" +msgstr "Wiki hochladen" #: modules/featherwiki/templates/featherwiki_configure.html:30 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:30 -#, fuzzy -#| msgid "No libraries available." msgid "No wikis available." -msgstr "Keine Bibliotheken verfügbar." +msgstr "Keine Wikis verfügbar." #: modules/featherwiki/templates/featherwiki_configure.html:36 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:36 -#, fuzzy, python-format -#| msgid "Go to site %(site)s" +#, python-format msgid "Go to wiki %(wiki)s" -msgstr "Gehe zu Seite %(site)s" +msgstr "Zu Wiki %(wiki)s gehen" #: modules/featherwiki/templates/featherwiki_configure.html:43 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:43 -#, fuzzy, python-format -#| msgid "Enable ikiwiki" +#, python-format msgid "Rename wiki %(wiki)s" -msgstr "ikiwiki einschalten" +msgstr "Wiki %(wiki)s umbenennen" #: modules/featherwiki/templates/featherwiki_configure.html:50 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:50 -#, fuzzy, python-format -#| msgid "Delete site %(site)s" +#, python-format msgid "Delete wiki %(wiki)s" -msgstr "Seite %(site)s löschen" +msgstr "Wiki %(wiki)s löschen" #: modules/featherwiki/templates/featherwiki_delete.html:12 #: modules/tiddlywiki/templates/tiddlywiki_delete.html:12 -#, fuzzy, python-format -#| msgid "Delete Wiki or Blog %(name)s" +#, python-format msgid "Delete wiki %(name)s" -msgstr "Wiki oder Blog %(name)s löschen" +msgstr "Wiki %(name)s löschen" #: modules/featherwiki/templates/featherwiki_delete.html:18 msgid "" @@ -2492,10 +2458,8 @@ msgstr "" #: modules/featherwiki/templates/featherwiki_delete.html:25 #: modules/tiddlywiki/templates/tiddlywiki_delete.html:25 -#, fuzzy -#| msgid "Delete this archive permanently?" msgid "Delete this wiki file permanently?" -msgstr "Dieses Archiv endgültig löschen?" +msgstr "Diese Wiki-Datei endgültig löschen?" #: modules/featherwiki/templates/featherwiki_upload_file.html:20 #: modules/tiddlywiki/templates/tiddlywiki_upload_file.html:20 @@ -2503,60 +2467,45 @@ msgid "Upload" msgstr "Hochladen" #: modules/featherwiki/views.py:20 modules/tiddlywiki/views.py:20 -#, fuzzy -#| msgid "A share with this name already exists." msgid "A wiki file with the given name already exists." -msgstr "Eine Freigabe mit diesem Namen existiert bereits." +msgstr "Eine Wiki-Datei mit dem angegebenen Namen existiert bereits." #: modules/featherwiki/views.py:54 modules/tiddlywiki/views.py:54 -#, fuzzy -#| msgid "Archive created." msgid "Wiki created." -msgstr "Archiv angelegt." +msgstr "Wiki erstellt." #: modules/featherwiki/views.py:59 modules/tiddlywiki/views.py:59 -#, fuzzy -#| msgid "An error occurred while creating the library." msgid "An error occurred while creating the wiki." -msgstr "Beim Erstellen der Bibliothek ist ein Fehler aufgetreten." +msgstr "Beim Erstellen des Wikis ist ein Fehler aufgetreten." #: modules/featherwiki/views.py:76 modules/tiddlywiki/views.py:76 -#, fuzzy -#| msgid "MediaWiki" msgid "Rename Wiki" -msgstr "MediaWiki" +msgstr "Wiki umbenennen" #: modules/featherwiki/views.py:84 modules/tiddlywiki/views.py:84 msgid "Wiki renamed." -msgstr "" +msgstr "Wiki umbenannt." #: modules/featherwiki/views.py:89 modules/tiddlywiki/views.py:89 -#, fuzzy -#| msgid "An error occurred while creating the library." msgid "An error occurred while renaming the wiki." -msgstr "Beim Erstellen der Bibliothek ist ein Fehler aufgetreten." +msgstr "Beim Umbenennen des Wikis ist ein Fehler aufgetreten." #: modules/featherwiki/views.py:106 modules/tiddlywiki/views.py:106 -#, fuzzy -#| msgid "Upload File" msgid "Upload Wiki File" -msgstr "Datei hochladen" +msgstr "Wiki-Datei hochladen" #: modules/featherwiki/views.py:115 modules/tiddlywiki/views.py:116 msgid "Wiki file added." -msgstr "" +msgstr "Wiki-Datei hochgeladen." #: modules/featherwiki/views.py:119 modules/tiddlywiki/views.py:120 -#, fuzzy -#| msgid "Failed to add content package." msgid "Failed to add wiki file." -msgstr "Hinzufügen eines Inhaltspakets ist fehlgeschlagen." +msgstr "Fehler beim Hinzufügen der Wiki-Datei." #: modules/featherwiki/views.py:138 modules/tiddlywiki/views.py:139 -#, fuzzy, python-brace-format -#| msgid "Could not delete {name}: {error}" +#, python-brace-format msgid "Could not delete {name}" -msgstr "{name} konnte nicht gelöscht werden: {error}" +msgstr "{name} konnte nicht gelöscht werden" #: modules/firewall/__init__.py:25 #, python-brace-format @@ -2659,10 +2608,8 @@ msgstr "" "bereitgestellt." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Installation abgeschlossen!" +msgstr "Einrichtung abgeschlossen! Nächste Schritte:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2670,14 +2617,16 @@ msgid "" "Initial setup has been completed. Perform the next steps to make your " "{box_name} operational." msgstr "" +"Die Ersteinrichtung ist abgeschlossen. Führen Sie die nächsten Schritte aus, " +"um Ihre {box_name} betriebsbereit zu machen." #: modules/first_boot/__init__.py:66 msgid "Next steps" -msgstr "" +msgstr "Nächste Schritte" #: modules/first_boot/__init__.py:73 msgid "See next steps" -msgstr "" +msgstr "Siehe nächste Schritte" #: modules/first_boot/forms.py:14 #, python-brace-format @@ -2696,10 +2645,8 @@ msgid "Firstboot Wizard Secret" msgstr "Assistent beim ersten Start" #: modules/first_boot/templates/firstboot_complete.html:11 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Installation abgeschlossen!" +msgstr "Einrichtung abgeschlossen! Nächste Schritte:" #: modules/first_boot/templates/firstboot_complete.html:18 #, python-format @@ -2854,18 +2801,16 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Einfaches Git Hosting" +msgstr "Git-Hosting" #: modules/gitweb/manifest.py:37 msgid "Version control" -msgstr "" +msgstr "Versionskontrolle" #: modules/gitweb/manifest.py:37 msgid "Developer tool" -msgstr "" +msgstr "Entwicklerwerkzeug" #: modules/gitweb/templates/gitweb_configure.html:13 msgid "Manage Repositories" @@ -3347,8 +3292,6 @@ msgstr "I2P Proxy" #: modules/i2p/manifest.py:43 modules/tor/manifest.py:59 #: modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" msgstr "Anonymisierungsnetzwerk" From a87c3b102e6e9155dabcc5cb07b11227874d8191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Podhoreck=C3=BD?= Date: Thu, 24 Oct 2024 07:20:35 +0000 Subject: [PATCH 09/32] Translated using Weblate (Czech) Currently translated at 100.0% (1770 of 1770 strings) --- plinth/locale/cs/LC_MESSAGES/django.po | 326 +++++++++---------------- 1 file changed, 115 insertions(+), 211 deletions(-) diff --git a/plinth/locale/cs/LC_MESSAGES/django.po b/plinth/locale/cs/LC_MESSAGES/django.po index 4c3c53bdf..95dd1f75a 100644 --- a/plinth/locale/cs/LC_MESSAGES/django.po +++ b/plinth/locale/cs/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-14 21:15+0000\n" +"PO-Revision-Date: 2024-10-24 15:15+0000\n" "Last-Translator: Jiří Podhorecký \n" "Language-Team: Czech \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" -"X-Generator: Weblate 5.8-rc\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -855,14 +855,12 @@ msgstr "Jakýkoli komentář, který vám pomůže zapamatovat si účel tohoto #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Sdílení souborů a útržků" +msgstr "Sdílení souborů" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1095,19 +1093,15 @@ msgstr "Knihovna s tímto názvem již existuje." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "E-book" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "Knihovna e-knih" +msgstr "Knihovna" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "Knihovna e-knih" +msgstr "čtečka e-knih" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1360,15 +1354,15 @@ msgstr "Neplatný seznam URI serverů STUN/TURN" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Videokonference" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1458,17 +1452,13 @@ msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Spustit webového klienta" +msgstr "Webový klient" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -2021,25 +2011,21 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Šifrování" +msgstr "Šifrované zasílání zpráv" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Audio chat" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Video Room" +msgstr "Video chat" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 #, python-format @@ -2182,18 +2168,16 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" -msgstr "E-mailový Server" +msgstr "E-mailový server" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" -msgstr "" +msgstr "Řízení spamu" #: modules/email/templates/email-aliases.html:13 #: modules/email/templates/email.html:15 @@ -2351,26 +2335,22 @@ msgstr "Wiki" #: modules/featherwiki/manifest.py:18 modules/infinoted/manifest.py:46 #: modules/tiddlywiki/manifest.py:20 msgid "Note taking" -msgstr "" +msgstr "Přijímání poznámek" #: modules/featherwiki/manifest.py:18 modules/ikiwiki/manifest.py:15 #: modules/mediawiki/manifest.py:25 modules/tiddlywiki/manifest.py:21 #: modules/wordpress/manifest.py:26 -#, fuzzy -#| msgid "Website Security" msgid "Website" -msgstr "Zabezpečení webové stránky" +msgstr "Webové stránky" #: modules/featherwiki/manifest.py:18 modules/tiddlywiki/manifest.py:25 msgid "Quine" -msgstr "" +msgstr "Quine" #: modules/featherwiki/manifest.py:18 modules/nextcloud/manifest.py:56 #: modules/tiddlywiki/manifest.py:26 -#, fuzzy -#| msgid "Debian:" msgid "Non-Debian" -msgstr "Debian:" +msgstr "Non-Debian" #: modules/featherwiki/templates/featherwiki_configure.html:12 #: modules/tiddlywiki/templates/tiddlywiki_configure.html:12 @@ -2581,10 +2561,8 @@ msgstr "" "zajišťuje aplikace Cockpit." #: modules/first_boot/__init__.py:61 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup complete! Next steps:" -msgstr "Nastavení dokončeno!" +msgstr "Nastavení dokončeno! Další kroky:" #: modules/first_boot/__init__.py:63 #, python-brace-format @@ -2592,14 +2570,16 @@ msgid "" "Initial setup has been completed. Perform the next steps to make your " "{box_name} operational." msgstr "" +"Počáteční nastavení bylo dokončeno. Proveďte další kroky pro zprovoznění " +"vaší {box_name}." #: modules/first_boot/__init__.py:66 msgid "Next steps" -msgstr "" +msgstr "Další kroky" #: modules/first_boot/__init__.py:73 msgid "See next steps" -msgstr "" +msgstr "Viz další kroky" #: modules/first_boot/forms.py:14 #, python-brace-format @@ -2617,10 +2597,8 @@ msgid "Firstboot Wizard Secret" msgstr "Tajemství průvodce Firstboot" #: modules/first_boot/templates/firstboot_complete.html:11 -#, fuzzy -#| msgid "Setup Complete!" msgid "Setup Complete! Next Steps:" -msgstr "Nastavení dokončeno!" +msgstr "Nastavení dokončeno! Další kroky:" #: modules/first_boot/templates/firstboot_complete.html:18 #, python-format @@ -2628,6 +2606,9 @@ msgid "" "Automatic software update " "runs daily by default. For the first time, manually run it now." msgstr "" +"Automatická aktualizace " +"softwaru probíhá ve výchozím nastavení každý den. Poprvé ji nyní spusťte " +"ručně." #: modules/first_boot/templates/firstboot_complete.html:27 #: modules/upgrades/templates/upgrades_configure.html:108 @@ -2639,6 +2620,8 @@ msgstr "Aktualizovat nyní" msgid "" "Review privacy options." msgstr "" +"Zkontrolujte možnosti ochrany " +"osobních údajů." #: modules/first_boot/templates/firstboot_complete.html:46 #, python-format @@ -2646,12 +2629,15 @@ msgid "" "Review and setup network " "connections. Change the default Wi-Fi password, if applicable." msgstr "" +"Zkontrolujte a nastavte síťová připojení. V případě potřeby změňte výchozí heslo Wi-Fi." #: modules/first_boot/templates/firstboot_complete.html:57 #, python-format msgid "" "Configure a domain name." msgstr "" +"Nakonfigurujte název domény." #: modules/first_boot/templates/firstboot_complete.html:67 #, python-format @@ -2659,6 +2645,8 @@ msgid "" "Configure and schedule remote backups." msgstr "" +"Konfigurace a plánování vzdálených záloh." #: modules/first_boot/templates/firstboot_complete.html:78 #, python-format @@ -2666,6 +2654,8 @@ msgid "" "Put %(box_name)s to use by installing apps." msgstr "" +"Zprovozněte %(box_name)s instalací aplikací." #: modules/first_boot/templates/firstboot_welcome.html:29 msgid "Start Setup" @@ -2770,18 +2760,16 @@ msgid "Git" msgstr "Git" #: modules/gitweb/manifest.py:37 -#, fuzzy -#| msgid "Simple Git Hosting" msgid "Git hosting" -msgstr "Jednoduché hostování Git" +msgstr "hostování Git" #: modules/gitweb/manifest.py:37 msgid "Version control" -msgstr "" +msgstr "Správa verzí" #: modules/gitweb/manifest.py:37 msgid "Developer tool" -msgstr "" +msgstr "Nástroj pro vývojáře" #: modules/gitweb/templates/gitweb_configure.html:13 msgid "Manage Repositories" @@ -3250,8 +3238,6 @@ msgstr "I2P proxy" #: modules/i2p/manifest.py:43 modules/tor/manifest.py:59 #: modules/torproxy/manifest.py:56 -#, fuzzy -#| msgid "Anonymity Network" msgid "Anonymity network" msgstr "Anonymní síť" @@ -3259,7 +3245,7 @@ msgstr "Anonymní síť" #: modules/shadowsocks/manifest.py:19 modules/shadowsocksserver/manifest.py:18 #: modules/tor/manifest.py:60 modules/torproxy/manifest.py:57 msgid "Censorship resistance" -msgstr "" +msgstr "Rezistence proti cenzuře" #: modules/i2p/templates/i2p.html:12 msgid "I2P Proxies and Tunnels" @@ -3343,7 +3329,7 @@ msgstr "Heslo k účtu správce" #: modules/ikiwiki/manifest.py:15 modules/wordpress/manifest.py:26 msgid "Blog" -msgstr "" +msgstr "Blog" #: modules/ikiwiki/templates/ikiwiki_configure.html:12 msgid "Manage Wikis and Blogs" @@ -3461,7 +3447,7 @@ msgstr "" #: modules/infinoted/manifest.py:46 msgid "Collaborative editing" -msgstr "" +msgstr "Společná editace" #: modules/janus/__init__.py:23 msgid "Janus is a lightweight WebRTC server." @@ -3490,14 +3476,12 @@ msgid "Janus Video Room" msgstr "Janus Video Room" #: modules/janus/manifest.py:16 -#, fuzzy -#| msgid "WebRTC server" msgid "WebRTC" -msgstr "WebRTC server" +msgstr "WebRTC" #: modules/janus/manifest.py:16 msgid "Web conference" -msgstr "" +msgstr "Webové konference" #: modules/janus/templates/janus_video_room.html:205 #: modules/jsxc/templates/jsxc_launch.html:117 templates/base.html:254 @@ -3521,16 +3505,12 @@ msgid "Chat Client" msgstr "Chatovací klient" #: modules/jsxc/manifest.py:16 -#, fuzzy -#| msgid "Web Search" msgid "Web chat" -msgstr "Vyhledávání na webu" +msgstr "Webový chat" #: modules/jsxc/manifest.py:16 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Client" msgid "Client" -msgstr "IRC klient" +msgstr "Klient" #: modules/kiwix/__init__.py:21 msgid "" @@ -3605,22 +3585,16 @@ msgstr "" "aby se ušetřilo místo na disku." #: modules/kiwix/manifest.py:23 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Offline reader" -msgstr "Offline Wikipedia" +msgstr "Offline čtečka" #: modules/kiwix/manifest.py:24 -#, fuzzy -#| msgid "Archive name" msgid "Archival" -msgstr "Název archivu" +msgstr "Archivace" #: modules/kiwix/manifest.py:26 -#, fuzzy -#| msgid "Offline Wikipedia" msgid "Wikipedia" -msgstr "Offline Wikipedia" +msgstr "Wikipedie" #: modules/kiwix/templates/kiwix-add-package.html:24 #, python-format @@ -3899,16 +3873,12 @@ msgid "FluffyChat" msgstr "FluffyChat" #: modules/matrixsynapse/manifest.py:101 modules/quassel/manifest.py:54 -#, fuzzy -#| msgid "IRC Chatroom" msgid "Chat room" -msgstr "Chatovací místnost na IRC" +msgstr "Chatovací místnost" #: modules/matrixsynapse/manifest.py:105 -#, fuzzy -#| msgid "Media streaming server" msgid "Matrix server" -msgstr "Server pro streamování médií" +msgstr "Server Matrix" #: modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:15 #: modules/miniflux/templates/miniflux.html:12 @@ -4240,20 +4210,16 @@ msgid "When disabled, players cannot die or receive damage of any kind." msgstr "Pokud je vypnuto, postavy hráčů nemohou zemřít nebo se zranit." #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Updated server." msgid "Game server" -msgstr "Aktualizovaný server." +msgstr "Herní server" #: modules/minetest/manifest.py:49 -#, fuzzy -#| msgid "Block Sandbox" msgid "Block sandbox" -msgstr "Pískoviště s kostkami" +msgstr "Blokové pískoviště" #: modules/minetest/manifest.py:49 msgid "Platform" -msgstr "" +msgstr "Platforma" #: modules/minetest/templates/minetest.html:17 modules/networks/forms.py:105 #: modules/networks/forms.py:145 @@ -4315,24 +4281,20 @@ msgid "totem" msgstr "totem" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "Simple Media Server" msgid "Media server" -msgstr "Simple Media Server" +msgstr "Mediální server" #: modules/minidlna/manifest.py:116 msgid "Television" -msgstr "" +msgstr "Televize" #: modules/minidlna/manifest.py:116 msgid "UPnP" -msgstr "" +msgstr "UPnP" #: modules/minidlna/manifest.py:116 -#, fuzzy -#| msgid "MiniDLNA" msgid "DLNA" -msgstr "MiniDLNA" +msgstr "DLNA" #: modules/minidlna/views.py:33 msgid "Updated media directory" @@ -4427,26 +4389,22 @@ msgid "RSS Guard" msgstr "RSS Guard" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "News Feed Reader" msgid "Feed reader" -msgstr "Čtečka novinek" +msgstr "Čtečka kanálů" #: modules/miniflux/manifest.py:138 modules/ttrss/manifest.py:55 msgid "News aggregation" -msgstr "" +msgstr "Agregace zpráv" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 -#, fuzzy -#| msgid "SSH" msgid "RSS" -msgstr "SSH" +msgstr "RSS" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: modules/miniflux/templates/miniflux.html:14 msgid "" @@ -4566,7 +4524,7 @@ msgstr "Mumla" #: modules/mumble/manifest.py:67 msgid "Group conference" -msgstr "" +msgstr "Skupinová konference" #: modules/mumble/manifest.py:67 modules/radicale/manifest.py:91 #: modules/shadowsocks/forms.py:24 @@ -6052,20 +6010,16 @@ msgstr "" "nastavení profilu bez kódu země." #: modules/nextcloud/manifest.py:56 modules/syncthing/manifest.py:58 -#, fuzzy -#| msgid "Filesystem" msgid "File sync" -msgstr "Souborový systém" +msgstr "Synchronizace souborů" #: modules/nextcloud/manifest.py:56 modules/sharing/__init__.py:34 msgid "Sharing" msgstr "Sdílení" #: modules/nextcloud/manifest.py:56 -#, fuzzy -#| msgid "Group Share" msgid "Groupware" -msgstr "Skupinové sdílení" +msgstr "Groupware" #: modules/nextcloud/views.py:53 msgid "Password update failed. Please choose a stronger password." @@ -6112,16 +6066,12 @@ msgid "Tunnelblick" msgstr "Tunnelblick" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS server" msgid "VPN server" -msgstr "DNS server" +msgstr "VPN server" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "Removable Devices" msgid "Remote access" -msgstr "Vyjímatelná zařízení" +msgstr "Vzdálený přístup" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6546,20 +6496,16 @@ msgstr "Přistupte {url} s proxy {proxy} na tcp{kind}" #: modules/privoxy/manifest.py:10 msgid "Ad blocker" -msgstr "" +msgstr "Blokování reklam" #: modules/privoxy/manifest.py:10 modules/shadowsocks/manifest.py:18 #: modules/torproxy/manifest.py:55 -#, fuzzy -#| msgid "Gobby Server" msgid "Proxy server" -msgstr "Gobby server" +msgstr "Proxy server" #: modules/privoxy/manifest.py:10 modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Doména místní sítě" +msgstr "Místní síť" #: modules/quassel/__init__.py:24 #, python-brace-format @@ -6604,7 +6550,7 @@ msgstr "Quasseldroid" #: modules/quassel/manifest.py:54 msgid "IRC" -msgstr "" +msgstr "IRC" #: modules/radicale/__init__.py:25 #, python-brace-format @@ -6710,22 +6656,20 @@ msgstr "" "existujících kalendářů a adresářů." #: modules/radicale/manifest.py:91 -#, fuzzy -#| msgid "GNOME Calendar" msgid "Calendar" -msgstr "Kalendář GNOME" +msgstr "Kalendář" #: modules/radicale/manifest.py:91 modules/roundcube/manifest.py:23 msgid "Contacts" -msgstr "" +msgstr "Kontakty" #: modules/radicale/manifest.py:91 msgid "CalDAV" -msgstr "" +msgstr "CalDAV" #: modules/radicale/manifest.py:91 msgid "CardDAV" -msgstr "" +msgstr "CardDAV" #: modules/radicale/views.py:32 msgid "Access rights configuration updated" @@ -6797,10 +6741,8 @@ msgstr "" "textu, aby uživatel mohl určit, ke kterému účtu se chce připojit." #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "E-mail" #: modules/rssbridge/__init__.py:21 msgid "" @@ -6852,16 +6794,12 @@ msgstr "" "Umožnit, aby tato aplikace byla používána kýmkoli, kdo se k ní může dostat." #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "Generátor kanálů RSS" +msgstr "Generátor kanálů" #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "FluxNews" msgid "News" -msgstr "FluxNews" +msgstr "Zpravodajství" #: modules/samba/__init__.py:23 msgid "" @@ -6941,22 +6879,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Síťové rozhraní" +msgstr "Síťový disk" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Media streaming server" msgid "Media storage" -msgstr "Server pro streamování médií" +msgstr "Úložiště médií" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Zálohy" +msgstr "Zálohovací úložiště" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -7098,14 +7030,12 @@ msgid "Strict" msgstr "Přísný" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Search" msgid "Web search" -msgstr "Vyhledávání na webu" +msgstr "Webový vyhledávač" #: modules/searx/manifest.py:17 msgid "Metasearch Engine" -msgstr "" +msgstr "Metavyhledávač" #: modules/security/forms.py:13 msgid "Fail2Ban (recommended)" @@ -7259,16 +7189,12 @@ msgid "Shaarlier" msgstr "Shaarlier" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "Link" msgid "Link blog" -msgstr "Odkaz" +msgstr "Odkaz na blog" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "in use" msgid "Single user" -msgstr "v použití" +msgstr "Jeden uživatel" #: modules/shadowsocks/__init__.py:18 modules/shadowsocksserver/__init__.py:18 msgid "" @@ -7339,22 +7265,16 @@ msgstr "" "Metoda šifrování. Je třeba, aby byla stejná, jaká je nastavená na serveru." #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Šifrování" +msgstr "Šifrovaný tunel" #: modules/shadowsocks/manifest.py:21 -#, fuzzy -#| msgid "Endpoint" msgid "Entry point" -msgstr "Koncový bod" +msgstr "Vstupní bod" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 -#, fuzzy -#| msgid "Shadowsocks Client" msgid "Shadowsocks" -msgstr "Shadowsocks klient" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7388,10 +7308,8 @@ msgid "Encryption method. Clients must use the same setting." msgstr "Metoda šifrování. Klienti musí používat stejné nastavení." #: modules/shadowsocksserver/manifest.py:20 -#, fuzzy -#| msgid "Endpoint" msgid "Exit point" -msgstr "Koncový bod" +msgstr "Výstupní bod" #: modules/sharing/__init__.py:17 #, python-brace-format @@ -7454,10 +7372,8 @@ msgstr "" "Sdílení by mělo být buď veřejné, nebo sdílené alespoň s jednou skupinou." #: modules/sharing/manifest.py:19 modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Sharing" msgid "Web sharing" -msgstr "Sdílení" +msgstr "Webové sdílení" #: modules/sharing/templates/sharing.html:18 #: modules/sharing/templates/sharing.html:21 @@ -8186,15 +8102,15 @@ msgstr "Nahrát existující soubor TiddlyWiki z tohoto počítače." #: modules/tiddlywiki/manifest.py:22 msgid "Journal" -msgstr "" +msgstr "Deník" #: modules/tiddlywiki/manifest.py:23 msgid "Digital garden" -msgstr "" +msgstr "Digitální zahrada" #: modules/tiddlywiki/manifest.py:24 msgid "Zettelkasten" -msgstr "" +msgstr "Kartotéka" #: modules/tiddlywiki/templates/tiddlywiki_delete.html:18 msgid "" @@ -8358,10 +8274,8 @@ msgid "Orbot: Proxy with Tor" msgstr "Orbot: proxy s Tor" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" -msgstr "Onion služba" +msgstr "Onion služby" #: modules/tor/manifest.py:58 msgid "Relay" @@ -8572,16 +8486,16 @@ msgid "FreedomBox Updated" msgstr "FreedomBox aktualizován" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Aktualizace software" +msgstr "Ruční spuštění aktualizace softwaru" #: modules/upgrades/__init__.py:140 msgid "" "Automatic software update runs daily by default. For the first time, " "manually run it now." msgstr "" +"Automatická aktualizace softwaru probíhá ve výchozím nastavení denně. Poprvé " +"ji nyní spusťte ručně." #: modules/upgrades/__init__.py:242 msgid "Could not start distribution update" @@ -9177,10 +9091,8 @@ msgstr "" "Obvykle se kontroluje u služby VPN, přes kterou se odesílá veškerý provoz." #: modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "IRC Client" msgid "VPN client" -msgstr "IRC klient" +msgstr "VPN klient" #: modules/wireguard/templates/wireguard.html:10 msgid "As a Server" @@ -9464,7 +9376,7 @@ msgstr "" #: modules/wordpress/manifest.py:26 msgid "Content management system" -msgstr "" +msgstr "Systém správy obsahu" #: modules/zoph/__init__.py:24 #, python-brace-format @@ -9521,13 +9433,11 @@ msgstr "" #: modules/zoph/manifest.py:26 msgid "Photo" -msgstr "" +msgstr "Fotky" #: modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Photo Organizer" msgid "Organizer" -msgstr "Organizér fotografií" +msgstr "Organizér" #: modules/zoph/templates/zoph-pre-setup.html:15 #: modules/zoph/templates/zoph-pre-setup.html:28 @@ -9548,10 +9458,9 @@ msgid "Generic" msgstr "Obecné" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Chyba: {name}: {exception_message}" +msgstr "Chyba: {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -9603,22 +9512,19 @@ msgid "Updating app" msgstr "Aktualizace aplikací" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Chyba při instalaci aplikace: {error}" +msgstr "Chyba při instalaci aplikace: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Chyba při opravě aplikace: {error}" +msgstr "Chyba při opravě aplikace: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Chyba při aktualizaci aplikace: {error}" +msgstr "Chyba při aktualizaci aplikace: {exception}" #: setup.py:85 msgid "App installed." @@ -9735,10 +9641,8 @@ msgid "Service %(service_name)s is not running." msgstr "Služba %(service_name)s není spuštěná." #: templates/apps.html:29 -#, fuzzy -#| msgid "Search the web" msgid "Search with tags" -msgstr "Hledat na webu" +msgstr "Hledání pomocí štítků" #: templates/base.html:31 msgid "" From 33b41a66c3c3908800a613637e6750218e4d0808 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sat, 12 Oct 2024 07:24:34 -0400 Subject: [PATCH 10/32] ejabberd: Set mod_mam default to always This helps various clients to use MAM. Fixes: #2338 Tests: - Functional tests for ejabberd pass. - Install ejabberd and enable MAM. Check that default is set to always in the configuration. - Without this change, install ejabberd and enable MAM. Then restart plinth with this change. The configuration is changed from never to always. After several minutes, ejabberd is running again. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/ejabberd/__init__.py | 6 +++++- plinth/modules/ejabberd/privileged.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plinth/modules/ejabberd/__init__.py b/plinth/modules/ejabberd/__init__.py index 20e2ee88d..d4409d16b 100644 --- a/plinth/modules/ejabberd/__init__.py +++ b/plinth/modules/ejabberd/__init__.py @@ -50,7 +50,7 @@ class EjabberdApp(app_module.App): app_id = 'ejabberd' - _version = 8 + _version = 9 def __init__(self) -> None: """Create components for the app.""" @@ -159,6 +159,10 @@ class EjabberdApp(app_module.App): 'turn-ejabberd').get_configuration() update_turn_configuration(configuration, force=True) + if old_version and old_version < 9 and privileged.mam('status'): + # Re-enable to change configuration + privileged.mam('enable') + class EjabberdTurnConsumer(TurnConsumer): """Component to manage Coturn configuration for ejabberd.""" diff --git a/plinth/modules/ejabberd/privileged.py b/plinth/modules/ejabberd/privileged.py index 6f177c4c6..a682a073e 100644 --- a/plinth/modules/ejabberd/privileged.py +++ b/plinth/modules/ejabberd/privileged.py @@ -261,7 +261,7 @@ def mam(command: str) -> bool | None: 'mod_mam': { 'db_type': 'mnesia', # default is 'mnesia' (w/o set default_db) - 'default': 'never', # policy, default 'never' + 'default': 'always', # helps various clients to use mam 'request_activates_archiving': False, # default False 'assume_mam_usage': False, # for non-ack'd msgs, default False 'cache_size': 1000, # default is 1000 items From e4a2a6b9a6a4bf60d7443a254cd2507ca32c13dd Mon Sep 17 00:00:00 2001 From: Ettore Atalan Date: Fri, 25 Oct 2024 23:39:54 +0000 Subject: [PATCH 11/32] Translated using Weblate (German) Currently translated at 94.7% (1677 of 1770 strings) --- plinth/locale/de/LC_MESSAGES/django.po | 264 ++++++++----------------- 1 file changed, 85 insertions(+), 179 deletions(-) diff --git a/plinth/locale/de/LC_MESSAGES/django.po b/plinth/locale/de/LC_MESSAGES/django.po index 239fbd350..eeef81c40 100644 --- a/plinth/locale/de/LC_MESSAGES/django.po +++ b/plinth/locale/de/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-24 15:15+0000\n" +"PO-Revision-Date: 2024-10-26 23:15+0000\n" "Last-Translator: Ettore Atalan \n" "Language-Team: German \n" @@ -887,7 +887,7 @@ msgstr "Dateiaustausch" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1395,15 +1395,15 @@ msgstr "Ungültige Liste von STUN/TURN-Server-URIs" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Videokonferenz" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1675,7 +1675,7 @@ msgid "Diagnostic Test" msgstr "Diagnose" #: modules/diagnostics/views.py:155 -#, fuzzy, python-brace-format +#, python-brace-format msgid "App {app_id} is not installed, cannot repair" msgstr "App {app_id} ist nicht installiert, kann nicht reparieren" @@ -4518,7 +4518,7 @@ msgstr "SSH" #: modules/miniflux/manifest.py:138 modules/rssbridge/manifest.py:16 #: modules/ttrss/manifest.py:55 msgid "ATOM" -msgstr "" +msgstr "ATOM" #: modules/miniflux/templates/miniflux.html:14 msgid "" @@ -4528,53 +4528,41 @@ msgstr "" #: modules/miniflux/templates/miniflux.html:22 #: modules/miniflux/templates/miniflux.html:24 -#, fuzzy -#| msgid "Create User" msgid "Create admin user" -msgstr "Benutzer anlegen" +msgstr "Admin-Benutzer erstellen" #: modules/miniflux/templates/miniflux.html:27 #: modules/miniflux/templates/miniflux.html:29 -#, fuzzy -#| msgid "Set SuperUser Password" msgid "Reset user password" -msgstr "SuperUser-Kennwort festlegen" +msgstr "Benutzerpasswort zurücksetzen" #: modules/miniflux/views.py:38 -#, fuzzy -#| msgid "Create User" msgid "Create Admin User" -msgstr "Benutzer anlegen" +msgstr "Admin-Benutzer erstellen" #: modules/miniflux/views.py:48 -#, fuzzy, python-brace-format -#| msgid "Invalid username: {username}" +#, python-brace-format msgid "Created admin user: {username}" -msgstr "Ungültiger Nutzername: {username}" +msgstr "Admin-Benutzer erstellt: {username}" #: modules/miniflux/views.py:53 -#, fuzzy, python-brace-format -#| msgid "An error occurred while creating the repository." +#, python-brace-format msgid "An error occurred while creating the user: {error}." -msgstr "Beim Erstellen des Repository ist ein Fehler aufgetreten." +msgstr "Beim Erstellen des Benutzers ist ein Fehler aufgetreten: {error}." #: modules/miniflux/views.py:70 -#, fuzzy -#| msgid "Set SuperUser Password" msgid "Reset User Password" -msgstr "SuperUser-Kennwort festlegen" +msgstr "Benutzerpasswort zurücksetzen" #: modules/miniflux/views.py:80 -#, fuzzy, python-brace-format -#| msgid "Invalid username: {username}" +#, python-brace-format msgid "Password reset for user: {username}" -msgstr "Ungültiger Nutzername: {username}" +msgstr "Passwort zurücksetzen für Benutzer: {username}" #: modules/miniflux/views.py:85 -#, fuzzy, python-brace-format -#| msgid "An error occurred during configuration." +#, python-brace-format msgid "An error occurred during password reset: {error}." -msgstr "Ein Fehler ist bei der Konfiguration aufgetreten." +msgstr "Beim Zurücksetzen des Passworts ist ein Fehler aufgetreten: {error}." #: modules/mumble/__init__.py:25 msgid "" @@ -4649,7 +4637,7 @@ msgstr "Mumla" #: modules/mumble/manifest.py:67 msgid "Group conference" -msgstr "" +msgstr "Gruppenkonferenz" #: modules/mumble/manifest.py:67 modules/radicale/manifest.py:91 #: modules/shadowsocks/forms.py:24 @@ -4658,7 +4646,7 @@ msgstr "Server" #: modules/mumble/views.py:43 msgid "SuperUser password successfully updated." -msgstr "SuperUser-Kennwort wurde erfolgreich aktualisiert." +msgstr "SuperUser-Passwort erfolgreich aktualisiert." #: modules/mumble/views.py:48 msgid "Join password changed" @@ -4687,12 +4675,12 @@ msgstr "Namen-Dienste" #: modules/names/__init__.py:171 msgid "Package systemd-resolved is installed" -msgstr "" +msgstr "Paket systemd-resolved ist installiert" #: modules/names/__init__.py:195 #, python-brace-format msgid "Resolve domain name: {domain}" -msgstr "" +msgstr "Domainname auflösen: {domain}" #: modules/names/components.py:14 msgid "All" @@ -4712,7 +4700,7 @@ msgstr "" #: modules/names/forms.py:49 msgid "Use DNSSEC when resolving domains (global preference)" -msgstr "" +msgstr "DNSSEC bei der Auflösung von Domains verwenden (globale Einstellung)" #: modules/names/forms.py:84 msgid "Hostname" @@ -4763,28 +4751,24 @@ msgstr "ja" #: modules/names/resolved.py:92 modules/networks/forms.py:29 #: modules/networks/views.py:124 msgid "opportunistic" -msgstr "" +msgstr "opportunistisch" #: modules/names/resolved.py:93 modules/names/resolved.py:103 #: modules/networks/forms.py:30 modules/networks/views.py:123 -#, fuzzy -#| msgid "Dino" msgid "no" -msgstr "Dino" +msgstr "nein" #: modules/names/resolved.py:102 msgid "allow-downgrade" msgstr "" #: modules/names/resolved.py:110 -#, fuzzy -#| msgid "Get Support" msgid "supported" -msgstr "Unterstützung erhalten" +msgstr "unterstützt" #: modules/names/resolved.py:110 msgid "unsupported" -msgstr "" +msgstr "nicht unterstützt" #: modules/names/templates/names.html:12 msgid "Domains" @@ -4800,11 +4784,11 @@ msgstr "" #: modules/names/templates/names.html:66 msgid "Global" -msgstr "" +msgstr "Global" #: modules/names/templates/names.html:68 msgid "Link" -msgstr "" +msgstr "Link" #: modules/names/templates/names.html:73 #: modules/networks/templates/connection_show.html:264 @@ -4812,50 +4796,40 @@ msgid "DNS-over-TLS" msgstr "" #: modules/names/templates/names.html:77 -#, fuzzy -#| msgid "Enable DNSSEC" msgid "DNSSEC" -msgstr "DNSSEC einschalten" +msgstr "DNSSEC" #: modules/names/templates/names.html:82 -#, fuzzy -#| msgid "Second DNS Server" msgid "Current DNS Server" -msgstr "Zweiter DNS-Server" +msgstr "Aktueller DNS-Server" #: modules/names/templates/names.html:88 -#, fuzzy -#| msgid "DNS Server" msgid "DNS Servers" msgstr "DNS-Server" #: modules/names/templates/names.html:98 -#, fuzzy -#| msgid "DNS Server" msgid "Fallback DNS Servers" -msgstr "DNS-Server" +msgstr "Ausweich-DNS-Server" #: modules/names/templates/names.html:112 msgid "" "systemd-resolved package is not installed. Install it for additional " "functionality." msgstr "" +"Paket systemd-resolved ist nicht installiert. Installieren Sie es für " +"zusätzliche Funktionen." #: modules/names/templates/names.html:121 templates/setup.html:66 msgid "Install" msgstr "Installieren" #: modules/names/templates/names.html:126 -#, fuzzy -#| msgid "Error during installation" msgid "Error retrieving status:" -msgstr "Fehler bei der Installation" +msgstr "Fehler beim Abrufen des Status:" #: modules/names/views.py:81 -#, fuzzy -#| msgid "Hostname" msgid "Set Hostname" -msgstr "Hostname" +msgstr "Hostname festlegen" #: modules/names/views.py:99 #, python-brace-format @@ -4863,10 +4837,8 @@ msgid "Error setting hostname: {exception}" msgstr "Fehler beim Setzen des Hostnamens: {exception}" #: modules/names/views.py:115 -#, fuzzy -#| msgid "Domain Name" msgid "Set Domain Name" -msgstr "Domain-Name" +msgstr "Domainname festlegen" #: modules/names/views.py:133 #, python-brace-format @@ -5032,16 +5004,10 @@ msgstr "" "diesem Netzwerk verwenden" #: modules/networks/forms.py:136 -#, fuzzy -#| msgid "" -#| "Automatic: Configure automatically, use Internet connection from this " -#| "network" msgid "" "Link-local: Configure automatically to use an address that is only relevant " "to this network." msgstr "" -"Automatisch: Automatisch konfigurieren, Internetverbindung aus diesem " -"Netzwerk verwenden" #: modules/networks/forms.py:141 msgid "Ignore: Ignore this addressing method" @@ -5049,7 +5015,7 @@ msgstr "Ignorieren: Diese Adressierungsmethode ignorieren" #: modules/networks/forms.py:142 msgid "Disabled: Disable IPv6 for this connection" -msgstr "" +msgstr "Deaktiviert: IPv6 für diese Verbindung deaktivieren" #: modules/networks/forms.py:147 msgid "Prefix" @@ -5593,8 +5559,6 @@ msgid "Edit Connection" msgstr "Verbindung bearbeiten" #: modules/networks/templates/connections_fields.html:24 -#, fuzzy -#| msgid "Generic" msgid "General" msgstr "Allgemein" @@ -5859,10 +5823,8 @@ msgid "dhcp" msgstr "" #: modules/networks/views.py:33 -#, fuzzy -#| msgid "Ignore" msgid "ignore" -msgstr "Ignorieren" +msgstr "ignorieren" #: modules/networks/views.py:40 msgid "unmanaged" @@ -6014,8 +5976,6 @@ msgid "mesh point" msgstr "Mesh-Point" #: modules/networks/views.py:122 -#, fuzzy -#| msgid "Default" msgid "default" msgstr "Standard" @@ -6181,20 +6141,16 @@ msgstr "" "Profileinstellungen ohne Ländervorwahl zu akzeptieren." #: modules/nextcloud/manifest.py:56 modules/syncthing/manifest.py:58 -#, fuzzy -#| msgid "Filesystem" msgid "File sync" -msgstr "Dateisystem" +msgstr "Dateisynchronisierung" #: modules/nextcloud/manifest.py:56 modules/sharing/__init__.py:34 msgid "Sharing" msgstr "Sharing" #: modules/nextcloud/manifest.py:56 -#, fuzzy -#| msgid "Group Share" msgid "Groupware" -msgstr "Group Share" +msgstr "Groupware" #: modules/nextcloud/views.py:53 msgid "Password update failed. Please choose a stronger password." @@ -6243,16 +6199,12 @@ msgid "Tunnelblick" msgstr "Tunnelblick" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "DNS server" msgid "VPN server" -msgstr "DNS-Server" +msgstr "VPN-Server" #: modules/openvpn/manifest.py:60 modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "Removable Devices" msgid "Remote access" -msgstr "Wechseldatenträger" +msgstr "Fernzugriff" #: modules/openvpn/templates/openvpn.html:12 msgid "Profile" @@ -6629,7 +6581,7 @@ msgstr "" #: modules/privacy/forms.py:28 msgid "Allow using fallback DNS servers" -msgstr "" +msgstr "Verwendung von Ausweich-DNS-Servern zulassen" #: modules/privacy/forms.py:30 msgid "" @@ -6682,20 +6634,16 @@ msgstr "Zugang auf {url} über Proxy {proxy} auf TCP{kind}" #: modules/privoxy/manifest.py:10 msgid "Ad blocker" -msgstr "" +msgstr "Werbeblocker" #: modules/privoxy/manifest.py:10 modules/shadowsocks/manifest.py:18 #: modules/torproxy/manifest.py:55 -#, fuzzy -#| msgid "Gobby Server" msgid "Proxy server" -msgstr "Gobby-Server" +msgstr "Proxy-Server" #: modules/privoxy/manifest.py:10 modules/samba/manifest.py:90 -#, fuzzy -#| msgid "Local Network Domain" msgid "Local network" -msgstr "Lokale Netzwerkdomäne" +msgstr "Lokales Netzwerk" #: modules/quassel/__init__.py:24 #, python-brace-format @@ -6741,7 +6689,7 @@ msgstr "Quasseldroid" #: modules/quassel/manifest.py:54 msgid "IRC" -msgstr "" +msgstr "IRC" #: modules/radicale/__init__.py:25 #, python-brace-format @@ -6848,22 +6796,20 @@ msgstr "" "drücken, werden die bestehenden Kalender und Adressbücher aufgelistet." #: modules/radicale/manifest.py:91 -#, fuzzy -#| msgid "GNOME Calendar" msgid "Calendar" -msgstr "GNOME Kalender" +msgstr "Kalender" #: modules/radicale/manifest.py:91 modules/roundcube/manifest.py:23 msgid "Contacts" -msgstr "" +msgstr "Kontakte" #: modules/radicale/manifest.py:91 msgid "CalDAV" -msgstr "" +msgstr "CalDAV" #: modules/radicale/manifest.py:91 msgid "CardDAV" -msgstr "" +msgstr "CardDAV" #: modules/radicale/views.py:32 msgid "Access rights configuration updated" @@ -6936,10 +6882,8 @@ msgstr "" "dem der Benutzer angeben kann, mit welchem Konto er sich verbinden möchte." #: modules/roundcube/manifest.py:23 -#, fuzzy -#| msgid "FairEmail" msgid "Email" -msgstr "FairEmail" +msgstr "E-Mail" #: modules/rssbridge/__init__.py:21 msgid "" @@ -6993,10 +6937,8 @@ msgstr "" "erreichen kann." #: modules/rssbridge/manifest.py:16 -#, fuzzy -#| msgid "RSS Feed Generator" msgid "Feed generator" -msgstr "RSS Feed Generator" +msgstr "" #: modules/rssbridge/manifest.py:16 msgid "News" @@ -7081,22 +7023,16 @@ msgid "Dolphin" msgstr "Dolphin" #: modules/samba/manifest.py:91 -#, fuzzy -#| msgid "Network Interface" msgid "Network drive" -msgstr "Netzwerk-Schnittstelle" +msgstr "Netzlaufwerk" #: modules/samba/manifest.py:92 -#, fuzzy -#| msgid "Media streaming server" msgid "Media storage" -msgstr "Medien-Streaming-Server" +msgstr "Medienspeicher" #: modules/samba/manifest.py:93 -#, fuzzy -#| msgid "Backups" msgid "Backup storage" -msgstr "Sicherungen" +msgstr "Datensicherungsspeicher" #: modules/samba/templates/samba.html:20 msgid "Shares" @@ -7239,8 +7175,6 @@ msgid "Strict" msgstr "Streng" #: modules/searx/manifest.py:17 -#, fuzzy -#| msgid "Web Search" msgid "Web search" msgstr "Websuche" @@ -7405,10 +7339,8 @@ msgid "Link blog" msgstr "" #: modules/shaarli/manifest.py:34 -#, fuzzy -#| msgid "in use" msgid "Single user" -msgstr "in Benutzung" +msgstr "Einzelner Benutzer" #: modules/shadowsocks/__init__.py:18 modules/shadowsocksserver/__init__.py:18 msgid "" @@ -7481,22 +7413,16 @@ msgstr "" "übereinstimmen." #: modules/shadowsocks/manifest.py:20 modules/shadowsocksserver/manifest.py:19 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted tunnel" -msgstr "Verschlüsselung" +msgstr "Verschlüsselter Tunnel" #: modules/shadowsocks/manifest.py:21 -#, fuzzy -#| msgid "Endpoint" msgid "Entry point" -msgstr "Endpunkt" +msgstr "Einstiegspunkt" #: modules/shadowsocks/manifest.py:22 modules/shadowsocksserver/manifest.py:21 -#, fuzzy -#| msgid "Shadowsocks Client" msgid "Shadowsocks" -msgstr "Shadowsocks-Client" +msgstr "Shadowsocks" #: modules/shadowsocksserver/__init__.py:26 #, python-brace-format @@ -7534,10 +7460,8 @@ msgstr "" "verwenden." #: modules/shadowsocksserver/manifest.py:20 -#, fuzzy -#| msgid "Endpoint" msgid "Exit point" -msgstr "Endpunkt" +msgstr "Ausstiegspunkt" #: modules/sharing/__init__.py:17 #, python-brace-format @@ -7600,10 +7524,8 @@ msgid "Shares should be either public or shared with at least one group" msgstr "Freigaben sollten öffentlich sein oder mindestens einer Gruppe haben" #: modules/sharing/manifest.py:19 modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Sharing" msgid "Web sharing" -msgstr "Sharing" +msgstr "" #: modules/sharing/templates/sharing.html:18 #: modules/sharing/templates/sharing.html:21 @@ -8127,7 +8049,7 @@ msgstr "Springe zur Stromversorgung" #: modules/storage/__init__.py:447 modules/storage/tests/test_storage.py:396 msgid "grub package is configured" -msgstr "" +msgstr "grub-Paket ist konfiguriert" #: modules/storage/forms.py:63 msgid "Invalid directory name." @@ -8320,7 +8242,7 @@ msgstr "" #: modules/tiddlywiki/__init__.py:64 modules/tiddlywiki/manifest.py:9 msgid "TiddlyWiki" -msgstr "" +msgstr "TiddlyWiki" #: modules/tiddlywiki/__init__.py:66 msgid "Non-linear Notebooks" @@ -8340,15 +8262,15 @@ msgstr "" #: modules/tiddlywiki/manifest.py:22 msgid "Journal" -msgstr "" +msgstr "Journal" #: modules/tiddlywiki/manifest.py:23 msgid "Digital garden" -msgstr "" +msgstr "Digitaler Garten" #: modules/tiddlywiki/manifest.py:24 msgid "Zettelkasten" -msgstr "" +msgstr "Zettelkasten" #: modules/tiddlywiki/templates/tiddlywiki_delete.html:18 msgid "" @@ -8516,8 +8438,6 @@ msgid "Orbot: Proxy with Tor" msgstr "Orbot: Proxy mit Tor" #: modules/tor/manifest.py:57 -#, fuzzy -#| msgid "Onion Service" msgid "Onion services" msgstr "Onion-Dienste" @@ -8731,10 +8651,8 @@ msgid "FreedomBox Updated" msgstr "FreedomBox aktualisiert" #: modules/upgrades/__init__.py:138 -#, fuzzy -#| msgid "Software Update" msgid "Run software update manually" -msgstr "Software-Aktualisierung" +msgstr "Software-Aktualisierung manuell ausführen" #: modules/upgrades/__init__.py:140 msgid "" @@ -9359,10 +9277,8 @@ msgstr "" "Datenverkehr gesendet wird." #: modules/wireguard/manifest.py:45 -#, fuzzy -#| msgid "IRC Client" msgid "VPN client" -msgstr "IRC-Client" +msgstr "VPN-Client" #: modules/wireguard/templates/wireguard.html:10 msgid "As a Server" @@ -9652,7 +9568,7 @@ msgstr "" #: modules/wordpress/manifest.py:26 msgid "Content management system" -msgstr "" +msgstr "Inhaltsverwaltungssystem" #: modules/zoph/__init__.py:24 #, python-brace-format @@ -9713,13 +9629,11 @@ msgstr "" #: modules/zoph/manifest.py:26 msgid "Photo" -msgstr "" +msgstr "Foto" #: modules/zoph/manifest.py:26 -#, fuzzy -#| msgid "Photo Organizer" msgid "Organizer" -msgstr "Foto-Manager" +msgstr "" #: modules/zoph/templates/zoph-pre-setup.html:15 #: modules/zoph/templates/zoph-pre-setup.html:28 @@ -9740,10 +9654,9 @@ msgid "Generic" msgstr "Allgemein" #: operation.py:120 -#, fuzzy, python-brace-format -#| msgid "Error: {name}: {exception_message}" +#, python-brace-format msgid "Error: {name}: {exception}" -msgstr "Fehler: {name}: {exception_message}" +msgstr "Fehler: {name}: {exception}" #: operation.py:123 #, python-brace-format @@ -9795,22 +9708,19 @@ msgid "Updating app" msgstr "Aktualisieren der App" #: setup.py:78 -#, fuzzy, python-brace-format -#| msgid "Error installing app: {error}" +#, python-brace-format msgid "Error installing app: {exception}" -msgstr "Fehler bei der Installation der App: {error}" +msgstr "Fehler beim Installieren der App: {exception}" #: setup.py:80 -#, fuzzy, python-brace-format -#| msgid "Error repairing app: {error}" +#, python-brace-format msgid "Error repairing app: {exception}" -msgstr "Fehler beim Reparieren der App: {error}" +msgstr "Fehler beim Reparieren der App: {exception}" #: setup.py:82 -#, fuzzy, python-brace-format -#| msgid "Error updating app: {error}" +#, python-brace-format msgid "Error updating app: {exception}" -msgstr "Fehler beim Aktualisieren der App: {error}" +msgstr "Fehler beim Aktualisieren der App: {exception}" #: setup.py:85 msgid "App installed." @@ -9830,9 +9740,8 @@ msgid "Error running diagnostics: {error}" msgstr "Fehler bei der Diagnose: {error}" #: setup.py:156 -#, fuzzy msgid "Skipping repair, no failed checks" -msgstr "Überspringe Reparatur, keine fehlgeschlagenen Tests" +msgstr "Reparatur wird übersprungen, keine fehlgeschlagenen Tests" #: setup.py:164 #, python-brace-format @@ -9840,9 +9749,8 @@ msgid "Error repairing app: {error}" msgstr "Fehler beim Reparieren der App: {error}" #: setup.py:170 -#, fuzzy msgid "Re-running setup to complete repairs" -msgstr "Wiederholen der Installation, um Reparatur abzuschließen" +msgstr "Einrichtung erneut ausführen, um Reparaturen abzuschließen" #: setup.py:178 msgid "App repaired." @@ -9933,10 +9841,8 @@ msgid "Service %(service_name)s is not running." msgstr "Dienst %(service_name)s läuft nicht." #: templates/apps.html:29 -#, fuzzy -#| msgid "Search the web" msgid "Search with tags" -msgstr "Suche im Web" +msgstr "Suche mit Schlagwörtern" #: templates/base.html:31 msgid "" From 24382c298c6ac34743a220910aa9a1592b898a30 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sun, 27 Oct 2024 23:29:56 +0000 Subject: [PATCH 12/32] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegian?= =?UTF-8?q?=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 64.9% (1150 of 1770 strings) --- plinth/locale/nb/LC_MESSAGES/django.po | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plinth/locale/nb/LC_MESSAGES/django.po b/plinth/locale/nb/LC_MESSAGES/django.po index 430b3f7c0..16344fe37 100644 --- a/plinth/locale/nb/LC_MESSAGES/django.po +++ b/plinth/locale/nb/LC_MESSAGES/django.po @@ -16,8 +16,8 @@ msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-08-15 06:09+0000\n" -"Last-Translator: Petter Reinholdtsen \n" +"PO-Revision-Date: 2024-10-27 23:30+0000\n" +"Last-Translator: Sunil Mohan Adapa \n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" @@ -25,7 +25,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.7-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -5254,6 +5254,7 @@ msgid "Choose your internet connection type" msgstr "Velg din internett-tilkoblingstype" #: modules/networks/forms.py:412 +#, fuzzy msgid "" "I have a public IP address that may change over time

This means that devices on the Internet can reach you when you are " From d85105a428579a12180a4eba6bcbe4143575c72f Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 21 Oct 2024 15:01:42 -0700 Subject: [PATCH 13/32] middleware: tests: Drop some obsolete mock code - setup_helper was removed long ago. Tests: - Re-run unit tests. Signed-off-by: Sunil Mohan Adapa [vexch: Removed unused global variable setup_helper] Signed-off-by: Veiko Aasa Reviewed-by: Veiko Aasa --- plinth/tests/test_middleware.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/plinth/tests/test_middleware.py b/plinth/tests/test_middleware.py index a967eceef..1ba500205 100644 --- a/plinth/tests/test_middleware.py +++ b/plinth/tests/test_middleware.py @@ -17,8 +17,6 @@ from plinth import app as app_module from plinth.middleware import (AdminRequiredMiddleware, CommonErrorMiddleware, SetupMiddleware) -setup_helper = None - @pytest.fixture(name='kwargs') def fixture_kwargs(): @@ -76,14 +74,11 @@ class TestSetupMiddleware: assert response is None @staticmethod - @patch('plinth.tests.test_middleware.setup_helper') @patch('django.urls.resolve') @patch('django.urls.reverse', return_value='users:login') - def test_module_is_up_to_date(_reverse, resolve, setup_helper_, app, - middleware, kwargs): + def test_module_is_up_to_date(_reverse, resolve, app, middleware, kwargs): """Test that none is returned when module is up-to-date.""" resolve.return_value.namespaces = ['mockapp'] - setup_helper_.is_finished = None app.get_setup_state = lambda: app_module.App.SetupState.UP_TO_DATE request = RequestFactory().get('/plinth/mockapp') @@ -92,20 +87,18 @@ class TestSetupMiddleware: assert response is None @staticmethod - @patch('plinth.tests.test_middleware.setup_helper') @patch('plinth.views.SetupView') @patch('django.urls.resolve') @patch('django.urls.reverse', return_value='users:login') @pytest.mark.django_db - def test_module_view(_reverse, resolve, setup_view, setup_helper, app, - middleware, kwargs): + def test_module_view(_reverse, resolve, setup_view, app, middleware, + kwargs): """Test that only registered users can access the setup view.""" resolve.return_value.namespaces = ['mockapp'] view = Mock() setup_view.as_view.return_value = view request = RequestFactory().get('/plinth/mockapp') request.session = MagicMock() - setup_helper.is_finished = None # Verify that anonymous users cannot access the setup page request.user = AnonymousUser() From 15e9c6cad9e423426365e1e408c08743b87029cc Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Fri, 25 Oct 2024 07:47:40 -0700 Subject: [PATCH 14/32] backups: Better explanation for the format of upload file Fixes: #2115. Tests: - Visit the backups upload page. Notice that the new help text is as expected. Signed-off-by: Sunil Mohan Adapa Reviewed-by: Veiko Aasa --- plinth/modules/backups/forms.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 5b0d9c07f..30c5471b4 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -15,6 +15,7 @@ from django.core.validators import (FileExtensionValidator, from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ +from plinth import cfg from plinth.modules.storage import get_mounts from plinth.utils import format_lazy @@ -138,7 +139,11 @@ class UploadForm(forms.Form): label=_('Upload File'), required=True, validators=[ FileExtensionValidator( ['gz'], _('Backup files have to be in .tar.gz format')) - ], help_text=_('Select the backup file you want to upload')) + ], help_text=format_lazy( + _('Select the backup file to upload from the local computer. This ' + 'must be a file previously downloaded from the result of a ' + 'successful backup on a {box_name}. It must have a .tar.gz ' + 'extension.'), box_name=_(cfg.box_name))) def repository_validator(path): From e6fb96b381aa9fd433aae82918215cdea092cdac Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 24 Oct 2024 21:26:42 -0700 Subject: [PATCH 15/32] backups: Sort list of apps in backup, restore, and schedules Fixes: #2364 Tests: - Set language to English. Go to backups -> create. List of apps is sorted alphabetically and case is ignored. Take a backup. - Click on restore for the new backup. The list of apps is again sorted alphabetically and case is ignored. - Click on schedules. List of apps is sorted and alphabetically and case is ignored. - Repeat tests with Spanish locale. Signed-off-by: Sunil Mohan Adapa Reviewed-by: Veiko Aasa --- plinth/modules/backups/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 30c5471b4..c00d32d64 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -29,14 +29,14 @@ def _get_app_choices(components): """Return a list of check box multiple choices from list of components.""" choices = [] for component in components: - name = component.app.info.name + name = str(component.app.info.name) if not component.has_data: name = gettext('{app} (No data to backup)').format( app=component.app.info.name) choices.append((component.app_id, name)) - return choices + return sorted(choices, key=lambda choice: choice[1].lower()) def _get_repository_choices(): From 8a8e5c78e81f697578ac60c14826d9036ed34f68 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Thu, 31 Oct 2024 22:32:26 +0000 Subject: [PATCH 16/32] Translated using Weblate (Spanish) Currently translated at 94.5% (1674 of 1770 strings) --- plinth/locale/es/LC_MESSAGES/django.po | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/plinth/locale/es/LC_MESSAGES/django.po b/plinth/locale/es/LC_MESSAGES/django.po index 99b7c4376..5ad701e4a 100644 --- a/plinth/locale/es/LC_MESSAGES/django.po +++ b/plinth/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-10-09 04:16+0000\n" +"PO-Revision-Date: 2024-11-01 17:00+0000\n" "Last-Translator: gallegonovato \n" "Language-Team: Spanish \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.8-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -873,14 +873,12 @@ msgstr "" #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Compartir archivos y recortes" +msgstr "Intercambio de archivos" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1116,19 +1114,15 @@ msgstr "Ya existe una biblioteca con este nombre." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "Libro electrónico" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "Biblioteca de libros electrónicos" +msgstr "Biblioteca" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "Biblioteca de libros electrónicos" +msgstr "Lector de libros electrónicos" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1386,7 +1380,7 @@ msgstr "Lista de URIS del servidor Stun/Turn no válida" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Videoconferencia" #: modules/coturn/manifest.py:7 msgid "STUN" From 6a53458e109e0d99e09e7f7ac9e916c990e34985 Mon Sep 17 00:00:00 2001 From: Coucouf Date: Thu, 31 Oct 2024 16:07:23 +0000 Subject: [PATCH 17/32] Translated using Weblate (French) Currently translated at 89.3% (1582 of 1770 strings) --- plinth/locale/fr/LC_MESSAGES/django.po | 90 +++++++++----------------- 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/plinth/locale/fr/LC_MESSAGES/django.po b/plinth/locale/fr/LC_MESSAGES/django.po index f1213ab7b..b835d67df 100644 --- a/plinth/locale/fr/LC_MESSAGES/django.po +++ b/plinth/locale/fr/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: FreedomBox UI\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-10-21 20:10-0400\n" -"PO-Revision-Date: 2024-07-03 07:09+0000\n" -"Last-Translator: John Doe \n" +"PO-Revision-Date: 2024-11-01 17:00+0000\n" +"Last-Translator: Coucouf \n" "Language-Team: French \n" "Language: fr\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 5.7-dev\n" +"X-Generator: Weblate 5.8.2-dev\n" #: config.py:103 #, python-brace-format @@ -879,14 +879,12 @@ msgstr "" #: modules/bepasty/manifest.py:23 modules/deluge/manifest.py:21 #: modules/samba/manifest.py:89 modules/sharing/manifest.py:19 #: modules/syncthing/manifest.py:58 modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "File & Snippet Sharing" msgid "File sharing" -msgstr "Partage de fichiers et de bribes de texte" +msgstr "Partage de fichiers" #: modules/bepasty/manifest.py:23 msgid "Pastebin" -msgstr "" +msgstr "Pastebin" #: modules/bepasty/templates/bepasty.html:12 msgid "Manage Passwords" @@ -1125,19 +1123,15 @@ msgstr "Une collection portant ce nom existe déjà." #: modules/calibre/manifest.py:20 msgid "Ebook" -msgstr "" +msgstr "Livre numérique" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Library" -msgstr "Bibliothèque de livres numériques" +msgstr "Bibliothèque" #: modules/calibre/manifest.py:20 -#, fuzzy -#| msgid "E-book Library" msgid "Ebook reader" -msgstr "Bibliothèque de livres numériques" +msgstr "Lecteur de livres numériques" #: modules/calibre/templates/calibre-delete-library.html:11 #, python-format @@ -1253,16 +1247,12 @@ msgid "Server Administration" msgstr "Administration du serveur" #: modules/config/__init__.py:18 -#, fuzzy -#| msgid "" -#| "Here you can set some general configuration options like hostname, domain " -#| "name, webserver home page etc." msgid "" "Here you can set some general configuration options like webserver home page " "etc." msgstr "" -"Cette page vous permet de modifier certains paramètres généraux comme le nom " -"de machine, le nom de domaine, la page d’accueil du serveur web, etc." +"Cette page vous permet de modifier certains paramètres généraux comme la " +"page d’accueil du serveur web, etc…" #: modules/config/__init__.py:40 msgid "General Configuration" @@ -1406,15 +1396,15 @@ msgstr "Liste d’URI STUN/TURN invalide" #: modules/coturn/manifest.py:7 modules/janus/manifest.py:16 msgid "Video conference" -msgstr "" +msgstr "Visioconférence" #: modules/coturn/manifest.py:7 msgid "STUN" -msgstr "" +msgstr "STUN" #: modules/coturn/manifest.py:7 msgid "TURN" -msgstr "" +msgstr "TURN" #: modules/coturn/templates/coturn.html:15 msgid "Use the following URLs to configure your communication server:" @@ -1506,17 +1496,13 @@ msgstr "BitTorrent" #: modules/deluge/manifest.py:21 modules/roundcube/manifest.py:23 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "Launch web client" msgid "Web client" -msgstr "Ouvrir le client web" +msgstr "Client web" #: modules/deluge/manifest.py:21 modules/syncthing/manifest.py:58 #: modules/transmission/manifest.py:39 -#, fuzzy -#| msgid "I2P" msgid "P2P" -msgstr "I2P" +msgstr "P2P" #: modules/diagnostics/__init__.py:29 msgid "" @@ -1533,7 +1519,7 @@ msgstr "Diagnostics" #: modules/diagnostics/__init__.py:99 msgid "skipped" -msgstr "" +msgstr "ignoré" #: modules/diagnostics/__init__.py:100 msgid "passed" @@ -2080,56 +2066,42 @@ msgid "Gajim" msgstr "Gajim" #: modules/ejabberd/manifest.py:124 modules/matrixsynapse/manifest.py:102 -#, fuzzy -#| msgid "Encryption" msgid "Encrypted messaging" -msgstr "Chiffrement" +msgstr "Messagerie chiffrée" #: modules/ejabberd/manifest.py:125 modules/matrixsynapse/manifest.py:103 #: modules/mumble/manifest.py:67 msgid "Audio chat" -msgstr "" +msgstr "Tchat vocal" #: modules/ejabberd/manifest.py:126 modules/matrixsynapse/manifest.py:104 -#, fuzzy -#| msgid "Video Room" msgid "Video chat" -msgstr "Salle de visio" +msgstr "Tchat vidéo" #: modules/ejabberd/manifest.py:127 modules/jsxc/manifest.py:16 msgid "XMPP" -msgstr "" +msgstr "XMPP" #: modules/ejabberd/templates/ejabberd.html:18 -#, fuzzy, python-format -#| msgid "" -#| "Your XMPP server domain is set to %(domainname)s. User IDs will " -#| "look like username@%(domainname)s. You can setup your domain on " -#| "the system Configure page." +#, python-format msgid "" "Your XMPP server domain is set to %(domain_name)s. User IDs will look " "like username@%(domain_name)s. You can setup your domain on the " "system Name Services page." msgstr "" -"Votre serveur XMPP utilise le domaine %(domainname)s. Les " -"identifiants utilisateur seront du type : username@%(domainname)s. " -"Vous pouvez configurer le domaine de votre système sur la page Configurer." +"Votre serveur XMPP utilise le domaine %(domain_name)s. Les " +"identifiants utilisateur seront du type : username@%(domain_name)s. " +"Vous pouvez configurer le domaine de votre système depuis la page Services de nommage." #: modules/ejabberd/templates/ejabberd.html:25 -#, fuzzy, python-format -#| msgid "" -#| "Your XMPP server domain is set to %(domainname)s. User IDs will " -#| "look like username@%(domainname)s. You can setup your domain on " -#| "the system Configure page." +#, python-format msgid "" "Your XMPP server domain is not set. You can setup your domain on the system " "Name Services page." msgstr "" -"Votre serveur XMPP utilise le domaine %(domainname)s. Les " -"identifiants utilisateur seront du type : username@%(domainname)s. " -"Vous pouvez configurer le domaine de votre système sur la page Configurer." +"Le domaine de votre serveur XMPP n’est pas configuré. Vous pouvez le " +"configurer depuis la page Services de nommage." #: modules/email/__init__.py:26 msgid "" @@ -2252,14 +2224,12 @@ msgid "FairEmail" msgstr "FairEmail" #: modules/email/manifest.py:82 -#, fuzzy -#| msgid "Email Server" msgid "Email server" msgstr "Serveur de courriel" #: modules/email/manifest.py:82 msgid "IMAP" -msgstr "" +msgstr "IMAP" #: modules/email/manifest.py:82 msgid "Spam control" @@ -7256,7 +7226,7 @@ msgstr "Disque du système FreedomBox" #: modules/samba/views.py:60 modules/storage/forms.py:140 msgid "Open Share" -msgstr "Ouvrir un partage" +msgstr "Partage ouvert" #: modules/samba/views.py:64 modules/storage/forms.py:138 msgid "Group Share" From cb5435dacd1a31488336e1c8084594250d8b902f Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sat, 19 Oct 2024 09:40:20 -0700 Subject: [PATCH 18/32] ui: Increase the width of app and system listings - Increase the width of the containers that hold home cards, app cards, system cards, and help cards. This helps in: - Showing system page layout better with wider cards for each item. - Showing more apps in the app page. This does not decrease the readability in the same way that increasing the size of the paragraph does beyond a certain point. - Also increase the width of the navbar to make it appear consistent. - Other containers such as content container remain at the same width. Increasing this width would make a reading a paragraph harder. - Behavior is mobile layouts is unchanged. Tests: - Test that apps, system, home and help views show cards in a wider layout. - Success/error messages shown in apps, system, home and help views are narrow and are not effected. - In home and help:index pages (reached when clicking help icon without JS), the icons are wide but the content is narrow. - Individual app pages, confirmation pages, and help pages remain at the older width. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- static/themes/default/css/main.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index 7b175a03a..e14cf6429 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -313,10 +313,12 @@ html { } .container { - max-width: 1000px; + /* Same width as container-xxl in Bootstrap 5 */ + max-width: 1320px; } .content-container { + max-width: 1000px; margin-bottom: 1.25rem; } From 11038b477a8195297aa25fd4a854bdc907ba8466 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sat, 19 Oct 2024 10:46:49 -0700 Subject: [PATCH 19/32] system: Increase the size of items in listing page Fixes: #2179. Tests: - Set language to Magyar (hu). Test that items in the system page are large enough to accommodate most names/descriptions. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- static/themes/default/css/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index e14cf6429..a867119b4 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -603,7 +603,7 @@ a.menu_link_active { /* System page - special card styling */ .system-page .card { text-align: left; - width: 14.375rem; + width: 19.375rem; } .system-page .card .nav-link { From 2d3e6b6cfa2c99c61b54df1a8d82f7095f53eb21 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sat, 19 Oct 2024 10:56:13 -0700 Subject: [PATCH 20/32] ui: Don't bold titles in card lists - Making most of text that is read in a page bold is useless. Emphasis should instead be used for some text that should stand out of the rest of the text. - The need to make the text bold goes away if the text color is not such a lighter shade of gray. Reset this to the regular text color. Tests: - Observe changes in home, apps, system, and help index pages. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- static/themes/default/css/main.css | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index a867119b4..a59e01a22 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -576,11 +576,10 @@ a.menu_link_active { .card .nav-link { display: block; padding: 0.25rem; - color: #646464; + color: #212529; /* Same as Bootstrap default color */ } .card-title { - font-weight: 800; font-size: 1.25rem; padding: 0.75rem 0; margin-bottom: 0; @@ -616,7 +615,6 @@ a.menu_link_active { .system-page .card-title { font-size: 1.125rem; font-style: normal; - font-weight: bold; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; From 6cb51719aab9cffa97a5f7488021cf1c8f4eb265 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 21 Oct 2024 10:17:56 -0700 Subject: [PATCH 21/32] ui: Move app names below app icons Fixes: #2418. - This resolves a issue that icons being misaligned when the name of the app flows into multiple lines. The increase in size of the card is not very bothering as long as icons are not still aligning. If the noise background is removed (to be proposed later), the increased size of the icon is even less bothering. - The other options would have been to reflect the increased size of the tile to neighboring apps in that row (does not seem possible with CSS) or ellipsize the title when it overflows. - Redo all the spacing in spacing inside the card to better match the new layout. The height of the card reduces slightly due this change, which looks better and closer to other icons grids in other UIs. Tests: - In index, apps, and help:index pages, the title has change location. Spacings are as expected and appealing. System page is unaffected. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/templates/card.html | 2 +- plinth/templates/index.html | 6 +++--- static/themes/default/css/main.css | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/plinth/templates/card.html b/plinth/templates/card.html index 40b1b3f42..6466433c3 100644 --- a/plinth/templates/card.html +++ b/plinth/templates/card.html @@ -6,7 +6,6 @@

diff --git a/plinth/templates/index.html b/plinth/templates/index.html index 57bb2d6be..1e12f6d5e 100644 --- a/plinth/templates/index.html +++ b/plinth/templates/index.html @@ -61,9 +61,6 @@ {% else %} {% endif %} -
- {{ shortcut.name }} -
{% if "custom" in shortcut.icon %} @@ -75,6 +72,9 @@ {% endif %} {% endif %}
+
+ {{ shortcut.name }} +
{{ shortcut.short_description|default:'' }}
diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index a59e01a22..4d199d2e9 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -562,7 +562,7 @@ a.menu_link_active { text-align: center; box-shadow: 0 0.1875rem 0.3125rem 0 rgba(0,0,0,0.12); width: 10rem; - padding: 0; + padding: 0.5rem 0.25rem; margin: 0 0.625rem 1.25rem; border: none; border-radius: 0.5rem; @@ -575,21 +575,20 @@ a.menu_link_active { .card .nav-link { display: block; - padding: 0.25rem; + padding: 0rem; color: #212529; /* Same as Bootstrap default color */ } .card-title { font-size: 1.25rem; - padding: 0.75rem 0; + padding: 0.25rem 0; margin-bottom: 0; } .card-description { font-weight: 400; - padding: 0.75rem 0; color: #646464; - font-size: 0.75rem; + font-size: 0.875rem; } .card-icon span, @@ -597,12 +596,14 @@ a.menu_link_active { width: 6.25rem; height: 6.25rem; font-size: 5rem; + margin: 0.5rem 0; } /* System page - special card styling */ .system-page .card { text-align: left; width: 19.375rem; + padding: 0; } .system-page .card .nav-link { From c8c753af29a0902c2da0c751a525916f994d7db8 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 30 Oct 2024 20:42:30 -0700 Subject: [PATCH 22/32] ui: Remove the noise background - Update the mediawiki functional tests which were using this image. - Slightly increase the darkness of the shadow around the cards to compensate for the loss of the contrast with the noise background. Tests: - Re-run mediawiki functional tests. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- .../modules/mediawiki/tests/test_functional.py | 15 +++++++++------ static/themes/default/css/main.css | 15 +-------------- static/themes/default/img/noise.png | Bin 23558 -> 0 bytes 3 files changed, 10 insertions(+), 20 deletions(-) delete mode 100644 static/themes/default/img/noise.png diff --git a/plinth/modules/mediawiki/tests/test_functional.py b/plinth/modules/mediawiki/tests/test_functional.py index a01d0faeb..149c90ce9 100644 --- a/plinth/modules/mediawiki/tests/test_functional.py +++ b/plinth/modules/mediawiki/tests/test_functional.py @@ -70,8 +70,9 @@ class TestMediawikiApp(functional.BaseAppTests): def test_upload_images(self, session_browser, login): """Test uploading an image.""" - _upload_image(session_browser, 'admin', 'whatever123', 'noise.png') - assert _image_exists(session_browser, 'Noise.png') + _upload_image(session_browser, 'admin', 'whatever123', + 'freedombox-logo-250px.png') + assert _image_exists(session_browser, 'Freedombox-logo-250px.png') def test_upload_svg_image(self, session_browser, login): """Test uploading an SVG image.""" @@ -81,20 +82,22 @@ class TestMediawikiApp(functional.BaseAppTests): def test_backup_restore(self, session_browser, login): """Test backup and restore of pages and images.""" - if not _image_exists(session_browser, 'Noise.png'): - _upload_image(session_browser, 'admin', 'whatever123', 'Noise.png') + if not _image_exists(session_browser, 'Freedombox-logo-250px.png'): + _upload_image(session_browser, 'admin', 'whatever123', + 'freedombox-logo-250px.png') functional.backup_create(session_browser, 'mediawiki', 'test_mediawiki') _enable_public_registrations(session_browser) - _delete_image(session_browser, 'admin', 'whatever123', 'Noise.png') + _delete_image(session_browser, 'admin', 'whatever123', + 'Freedombox-logo-250px.png') _delete_main_page(session_browser, 'admin', 'whatever123') functional.backup_restore(session_browser, 'mediawiki', 'test_mediawiki') assert _has_main_page(session_browser) - assert _image_exists(session_browser, 'Noise.png') + assert _image_exists(session_browser, 'Freedombox-logo-250px.png') _verify_create_account_link(session_browser) def test_uninstall(self, session_browser): diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index 4d199d2e9..0b39cd9aa 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -183,19 +183,6 @@ body { padding-top: 6rem; position: relative; font-family: Lato, sans-serif; - background: url('../img/noise.png') var(--background-color); -} - -@media (max-width: 767px) { - body { - background: none; - } - - body.index-page, - body.apps-page, - body.system-page { - background: url('../img/noise.png') var(--background-color); - } } .multiple-checkbox li { @@ -560,7 +547,7 @@ a.menu_link_active { display: block; line-height: 1.42857143; text-align: center; - box-shadow: 0 0.1875rem 0.3125rem 0 rgba(0,0,0,0.12); + box-shadow: 0 0.1875rem 0.3125rem 0 rgba(0,0,0,0.2); width: 10rem; padding: 0.5rem 0.25rem; margin: 0 0.625rem 1.25rem; diff --git a/static/themes/default/img/noise.png b/static/themes/default/img/noise.png deleted file mode 100644 index 4b89438e9af0ae4f1ace9c3b68027b7b68ca2733..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23558 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 RF696K002ovPDHLkV1l2gEl>ae From 5db6c46e8ba4c7d5fe685b1a63a78a3cd00730a3 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 30 Oct 2024 20:46:39 -0700 Subject: [PATCH 23/32] ui: Remove the border around content container - The container border is, strictly speaking, no longer required as the background and content container don't have different styles. - This makes the mobile and desktop interfaces a lot more similar. - Don't change any margins and padding for the content container. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- static/themes/default/css/main.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index 0b39cd9aa..c834bd80c 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -346,9 +346,6 @@ html { @media (min-width: 768px) { .content-container { padding: 1.5rem 3rem 3rem; - background-color: white; - border: 1px solid #ddd; - border-radius: 0.25rem; } } From 62e67c6c99ac0e43f3e0e6bded9f8252871a838e Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 24 Oct 2024 18:15:14 -0700 Subject: [PATCH 24/32] help: Make about page available to unauthenticated users - This page will replace the rather large footer in the front page. Tests: - Log out. Visit the help about page. It is available without redirection to login page. Version related alert is not shown. - Log in. Visit the help about page. It is available. Version related alert is shown. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/help/templates/help_about.html | 34 ++++++++++--------- plinth/modules/help/tests/test_views.py | 12 +++++-- plinth/modules/help/urls.py | 3 +- plinth/modules/help/views.py | 14 ++++---- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/plinth/modules/help/templates/help_about.html b/plinth/modules/help/templates/help_about.html index 5484c85a3..155fef351 100644 --- a/plinth/modules/help/templates/help_about.html +++ b/plinth/modules/help/templates/help_about.html @@ -13,23 +13,25 @@ class="main-graphic" />

-
+ {% endif %}

{% blocktrans trimmed %} diff --git a/plinth/modules/help/tests/test_views.py b/plinth/modules/help/tests/test_views.py index 9839c4e51..6f4b7e620 100644 --- a/plinth/modules/help/tests/test_views.py +++ b/plinth/modules/help/tests/test_views.py @@ -12,7 +12,7 @@ Pending: - status log import json import pathlib import subprocess -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest from django import urls @@ -113,11 +113,19 @@ def test_contribute_page(requests_get, decompress, apt_cache, rf): def test_about(_get_os_release, _is_newer_version_available, rf): """Test some expected items in about view.""" about_url = urls.reverse('help:about') - response = views.about(rf.get(about_url)) + request = rf.get(about_url) + request.user = Mock() + request.user.is_authenticated = True + response = views.about(request) assert _is_page(response) for item in ('version', 'new_version', 'os_release'): assert item in response.context_data + request.user.is_authenticated = False + response = views.about(request) + for item in ('version', 'new_version', 'os_release'): + assert item not in response.context_data + # --------------------------------------------------------------------------- # Tests for serving the offline user guide ( the "manual") diff --git a/plinth/modules/help/urls.py b/plinth/modules/help/urls.py index 188cbf07d..4f91d1d6f 100644 --- a/plinth/modules/help/urls.py +++ b/plinth/modules/help/urls.py @@ -4,6 +4,7 @@ URLs for the Help module """ from django.urls import re_path +from stronghold.decorators import public from plinth.utils import non_admin_view @@ -11,7 +12,7 @@ from . import views urlpatterns = [ re_path(r'^help/$', non_admin_view(views.index), name='index'), - re_path(r'^help/about/$', non_admin_view(views.about), name='about'), + re_path(r'^help/about/$', public(views.about), name='about'), re_path(r'^help/feedback/$', non_admin_view(views.feedback), name='feedback'), re_path(r'^help/support/$', non_admin_view(views.support), name='support'), diff --git a/plinth/modules/help/views.py b/plinth/modules/help/views.py index cabbb0bfc..a5638bc40 100644 --- a/plinth/modules/help/views.py +++ b/plinth/modules/help/views.py @@ -95,12 +95,14 @@ def support(request): def about(request): """Serve the about page""" - context = { - 'title': _('About {box_name}').format(box_name=_(cfg.box_name)), - 'version': __version__, - 'new_version': upgrades_views.is_newer_version_available(), - 'os_release': upgrades_views.get_os_release() - } + context = {'title': _('About {box_name}').format(box_name=_(cfg.box_name))} + if request.user.is_authenticated: + context.update({ + 'version': __version__, + 'new_version': upgrades_views.is_newer_version_available(), + 'os_release': upgrades_views.get_os_release() + }) + return TemplateResponse(request, 'help_about.html', context) From 6084b95a6515d75da56cccf913e082d5ffc539de Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 24 Oct 2024 18:17:53 -0700 Subject: [PATCH 25/32] help: Add all footer links to about page Fixes: #2464. - This page will act as replacement for the footer links in the home page. - Remove link to FreedomBox Foundation and add link to Weblate project. We already have a donate link to the foundation website. Tests: - About page is shown as expected. Old 'Learn more' button is no more. So is the last paragraph. - Styling is as expected. All the section have equal width. - All links work. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/help/templates/help_about.html | 65 +++++++++++++++---- static/themes/default/css/main.css | 15 ++++- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/plinth/modules/help/templates/help_about.html b/plinth/modules/help/templates/help_about.html index 155fef351..371e71842 100644 --- a/plinth/modules/help/templates/help_about.html +++ b/plinth/modules/help/templates/help_about.html @@ -80,16 +80,59 @@ {% endblocktrans %}

-

- {% blocktrans trimmed %} - For more information about the {{ box_name }} project, see the - {{ box_name }} - Wiki. - {% endblocktrans %} -

- -

{% trans "Learn more" %}

+ {% endblock %} diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index c834bd80c..a2496b9a4 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -879,7 +879,10 @@ input[type='submit'].running-status-button { } } -/* Help manual - anchor is below navbar */ +/* + * Help + */ +/* Manual - anchor is below navbar */ *[id^='idm']:before { display: block; content: " "; @@ -888,6 +891,16 @@ input[type='submit'].running-status-button { visibility: hidden; } +.help-about-links h2 { + font-size: 1rem; + font-weight: bold; +} + +.help-about-links ul { + list-style: none; + padding: 0; +} + /* * Notifications */ From 6bb13140e89c75e80924e526aa93dd21d8037ff6 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 24 Oct 2024 18:22:23 -0700 Subject: [PATCH 26/32] index: Remove links and about text and link to about page - Make a separate image for 'FreedomBox Powered'. - Link to the now publicly available about page. Tests: - Clicking on the new 'Powered' image leads to about page which is available to logged in and logged out users. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/templates/index.html | 71 +------- static/themes/default/css/main.css | 10 -- .../default/img/freedombox-logo-powered.svg | 160 ++++++++++++++++++ 3 files changed, 163 insertions(+), 78 deletions(-) create mode 100644 static/themes/default/img/freedombox-logo-powered.svg diff --git a/plinth/templates/index.html b/plinth/templates/index.html index 1e12f6d5e..089805f7f 100644 --- a/plinth/templates/index.html +++ b/plinth/templates/index.html @@ -92,75 +92,10 @@ {% block footer_block %} -

- - + +

- -

- {% blocktrans trimmed %} - {{ box_name }}, a Debian pure blend, is a 100% free software - self-hosting web server to deploy social applications on small - machines. It provides online communication tools respecting your - privacy and data ownership. - {% endblocktrans %} -

- -

- {% blocktrans trimmed %} - This portal is a part of the {{ box_name }} web interface. {{ box_name }} - is free software, distributed under the GNU Affero General Public License, - Version 3 or later. - {% endblocktrans %} -

- - - {% endblock %} diff --git a/static/themes/default/css/main.css b/static/themes/default/css/main.css index a2496b9a4..7ff79b664 100644 --- a/static/themes/default/css/main.css +++ b/static/themes/default/css/main.css @@ -280,12 +280,6 @@ body { text-align: center; } -/* Sticky footer styles --------------------------------------------------- */ -.footer-logo { - width: 8.75rem; -} - html { position: relative; } @@ -351,10 +345,6 @@ html { footer { text-align: center; - position: relative; - bottom: 0; - width: 100%; - height: 9.375rem; padding-top: 9.375rem; } diff --git a/static/themes/default/img/freedombox-logo-powered.svg b/static/themes/default/img/freedombox-logo-powered.svg new file mode 100644 index 000000000..5730029d7 --- /dev/null +++ b/static/themes/default/img/freedombox-logo-powered.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + From 5c41a672671ed117cc4df86ab6c1d89eb0afcc05 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 24 Oct 2024 18:36:39 -0700 Subject: [PATCH 27/32] base: Add link to about for unauthenticated users Since the about page is now public, adding a link to it in the navbar allows the users to easily discover it and find out about FreedomBox. Tests: - In mobile view, the hamburger menu show the icon and text. Link works. - In desktop mode, only the icon is visible. Link works. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/templates/base.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plinth/templates/base.html b/plinth/templates/base.html index 680d93184..e3897abb4 100644 --- a/plinth/templates/base.html +++ b/plinth/templates/base.html @@ -206,6 +206,15 @@ +
- {% blocktrans trimmed %} - You are running {{ os_release }} and {{ box_name }} version {{ version }}. - {% endblocktrans %} + {% if version %} + + {% if new_version %} + {% url 'upgrades:index' as upgrades_url %} + {% blocktrans trimmed %} + There is a new {{ box_name }} version + available. + {% endblocktrans %} + {% else %} + {% blocktrans trimmed %} + {{ box_name }} is up to date. + {% endblocktrans %} + {% endif %} +