Compare commits

...

213 commits

Author SHA1 Message Date
fruechtchen
9b1e7c4400 fixup 2020-06-24 18:41:35 +00:00
fruechtchen
a529912296 fix typo 2020-06-24 18:39:42 +00:00
fruechtchen
0d31182ef0 shorten member of council list 2020-06-24 18:38:10 +00:00
fruechtchen
15d754250f only list lemmy profile for all council members 2020-06-24 18:37:00 +00:00
fruechtchen
30bcd2b820 gitlab doesn't have PR's enabled 2020-06-24 18:33:01 +00:00
fruechtchen
7386e48c4a make it explicit that pull requests are welcome on non-github platforms 2020-06-24 18:11:26 +00:00
fruechtchen
869714e2f3 update lemmy council memberlist 2020-06-24 18:08:17 +00:00
6e736e1f1e Version v0.7.1 2020-06-24 13:29:44 -04:00
2233c2a76d Merge remote-tracking branch 'weblate/master' 2020-06-24 13:29:39 -04:00
e3860b8e97 Upgrading litely to have better vote colors. 2020-06-24 13:29:13 -04:00
00e2d546bf Making max markdown image height smaller. Fixes #836 2020-06-24 13:18:53 -04:00
maxigaz
d9cac4ce8e Translated using Weblate (Hungarian)
Currently translated at 58.2% (145 of 249 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hu/
2020-06-24 16:11:49 +00:00
Ady Nemo
d3dc5bf7c1 Translated using Weblate (French)
Currently translated at 100.0% (249 of 249 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/
2020-06-24 16:11:49 +00:00
Riccardo Mazzon
254200f8dc Translated using Weblate (Italian)
Currently translated at 100.0% (249 of 249 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/
2020-06-24 16:11:49 +00:00
b94d380729 Adding timeouts to message and delete picture notifications. #834 2020-06-23 21:40:59 -04:00
790b944031 Changing to new lemmynet repo location. 2020-06-23 13:47:02 -04:00
0680dd2398 Version v0.7.0 2020-06-23 12:55:16 -04:00
41777ad51c Merge branch 'master' of https://github.com/lemmynet/lemmy 2020-06-23 12:53:47 -04:00
4c582cf1b6 Making sure new comments don't clear out your current textarea.
- Making a better random string generator.
- Doing better incoming comment checking.
- Fixes #769
2020-06-23 12:52:07 -04:00
43905a041b Litely update. 2020-06-23 12:50:48 -04:00
e015424a83 Improve release notes for 0.7.0 2020-06-23 16:11:04 +02:00
84ed1ecdfd Fixing docker install instructions for pictshare. #831 2020-06-23 09:37:16 -04:00
27f2fd352a Version v0.6.85 2020-06-22 23:04:28 -04:00
eb6b7bca20 Better fonts. 2020-06-22 23:02:34 -04:00
5d1212b83c Version v0.6.84 2020-06-22 22:43:57 -04:00
416344b361 Making a better light theme, litely. #822 2020-06-22 22:42:27 -04:00
11f771469f Version v0.6.83 2020-06-22 22:01:45 -04:00
e3484de3b8 Merge branch 'master' into iav-arm-musl-dessalines 2020-06-22 20:35:09 -04:00
bca76f1f2f Adding pict-rs to release notes. 2020-06-22 20:30:26 -04:00
54c735841e Version v0.6.82 2020-06-22 19:32:30 -04:00
318b8c691a Coerce empty post form to undefined. Fixes #602 2020-06-22 19:27:42 -04:00
be3a375cae Instant mark all as read. Fixes #830 2020-06-22 19:21:13 -04:00
63fbf70eaa Disable submit when loading. Fixes #706 2020-06-22 19:05:55 -04:00
c1dbeb43c9 Making comment, post, and message preview a card. Fixes #817 2020-06-22 18:50:06 -04:00
ef2adcc95d Increasing comment margins. Fixes #826 2020-06-22 18:44:06 -04:00
1777070b46 Changing edit to save for some forms. Fixes #829 2020-06-22 18:33:51 -04:00
9dfdf78697 Moving save behind more menu. Fixes #827 2020-06-22 18:29:08 -04:00
c9dcb2662c Version v0.6.81 2020-06-22 17:53:47 -04:00
234be6fb09 Changing to prod_lemmy:latest 2020-06-22 17:52:39 -04:00
32d1f42626 Version v0.6.80 2020-06-22 16:15:31 -04:00
1b38e33bd3 Fixing deploy script. 2020-06-22 16:13:02 -04:00
132e3534de Removing some commented lines from the dockerfile. 2020-06-22 15:12:37 -04:00
983a45e178 Merge branch 'master' into iav-arm-musl-dessalines 2020-06-22 14:57:55 -04:00
96c9f801a9 Merge branch 'master' of https://github.com/makigi-io/makigi into makigi-io-master 2020-06-22 14:52:46 -04:00
040bd4361f Fixing nginx.conf, adding nginx config update to releases.md. 2020-06-22 14:48:46 -04:00
a4d2c2ab71 Upping thumbnail size. 2020-06-22 13:45:16 -04:00
cbed185040 Version v0.6.79 2020-06-22 12:56:48 -04:00
186e261fc0 Fixing docker-compose to rely on pictrs 2020-06-22 12:53:39 -04:00
1fa7d17e35 Version v0.6.78 2020-06-22 12:51:18 -04:00
f21e694907 Merge remote-tracking branch 'weblate/master' 2020-06-22 12:47:00 -04:00
b161cad982 Fixing up the migration script some more. 2020-06-22 12:43:09 -04:00
4332828dd7 Fixing some things in the pictrs upgrade script. 2020-06-22 12:04:28 -04:00
5872658f8c Checking for imagemagick install. 2020-06-22 10:54:33 -04:00
cb128256ed Updating nginx.conf, upgrading pict-rs 2020-06-22 08:31:15 -04:00
Ernest
8e1e9a521a Edit community name validation, translations #823 2020-06-22 09:23:54 +02:00
Ernest
4247df4295 Community name validation 2020-06-20 11:33:23 +02:00
ceb1284f27 Fixing deploy.sh 2020-06-17 22:32:18 -04:00
207caeda5b Version v0.6.77 2020-06-17 22:20:30 -04:00
294acfe412 Version v0.6.76 2020-06-17 22:18:47 -04:00
0782377ae3 Version v0.6.75 2020-06-17 22:11:48 -04:00
Cee
22c6cb1f99 Translated using Weblate (Chinese (Simplified))
Currently translated at 98.3% (242 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/
2020-06-17 20:25:57 +00:00
skrlet13
032af0f687 Translated using Weblate (Spanish)
Currently translated at 100.0% (246 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/es/
2020-06-17 20:25:57 +00:00
glorfindel
6aa65a917f Translated using Weblate (French)
Currently translated at 100.0% (246 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/
2020-06-17 20:25:57 +00:00
maxigaz
13760647f0 Translated using Weblate (Hungarian)
Currently translated at 39.8% (98 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hu/
2020-06-17 20:25:57 +00:00
9f50432999 wip: Add migration script from pictshare to pictrs 2020-06-15 19:46:57 +02:00
bda657b638 Some reorg of Dockerfiles. 2020-06-14 16:36:18 -04:00
Igor Velkov
6f58be9493 dockerfile for x64 and arm building with musl 2020-06-14 01:28:50 +03:00
6dea945a3a Pictshare image redirect. 2020-06-13 15:24:53 -04:00
5594bed6a8 Merge branch 'master' into use-pictrs-1 2020-06-12 17:51:37 -04:00
f647f2ae6c Blocking pict-rs import location 2020-06-12 17:05:19 -04:00
7cb7c1f0f9 Adding lld to docker builds. #810 2020-06-12 16:17:01 -04:00
da6de03536 Merge branch 'build-optimizations' of https://yerbamate.dev/LemmyNet/lemmy into build-optimizations 2020-06-12 15:53:06 -04:00
4ea6c4ad5d Various build optimizations 2020-06-12 21:45:23 +02:00
cddc23494d Upgrading pict-rs for working gifs. 2020-06-12 09:48:51 -04:00
5a6f39dae4 Various build optimizations 2020-06-12 15:29:50 +02:00
1b0212377d Version v0.6.74 2020-06-11 10:28:21 -04:00
74c5380975 Merge branch 'master' into use-attohttpc 2020-06-11 09:53:18 -04:00
80992aa5ba Merge branch 'master' into local-dev-docs 2020-06-11 09:50:18 -04:00
de0fb631df Improve documentation for local development 2020-06-11 15:04:20 +02:00
831680d27e Remove unneeded config dependencies 2020-06-11 13:09:51 +02:00
4cf1f080bf Adding delete picture via pict-rs delete tokens. Fixes #505 2020-06-10 22:47:06 -04:00
2fbd44c59d Adding pictrs thumbnail caching for urls and embeds. 2020-06-10 18:22:57 -04:00
be84c7b977 Replace isahc with attohttpc to remove curl (fixes #798) 2020-06-10 17:42:20 +02:00
043f484693 Version v0.6.73 2020-06-10 11:27:12 -04:00
kartikynwa
36e8ce624c
Change "Forgot Password" button's type to "button" (#797) 2020-06-10 11:12:53 -04:00
Lorenz Schmidt
13771d56cd
Add default themes with media queries (#796)
* Indicate unstable API in README and mdbook

* Support user preference for light and dark theme

* Add default themes and load them in `setTheme`

* Fixes #758
2020-06-10 11:11:51 -04:00
46bb3064ed Version v0.6.72 2020-06-09 22:49:42 -04:00
def3bb4729 Merge remote-tracking branch 'weblate/master' 2020-06-09 22:49:41 -04:00
da2229a303 Merge branch 'master' into dev 2020-06-09 22:43:28 -04:00
2e4c725647 Fixing toast background for all but i386. Fixes #794 2020-06-09 22:41:04 -04:00
bd26e4e9c1 Fixing some front end pictshare to pictrs conversions. 2020-06-09 17:17:24 -04:00
maxigaz
7bc601ad3b Translated using Weblate (Hungarian)
Currently translated at 5.6% (14 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hu/

Translated using Weblate (Hungarian)

Currently translated at 2.4% (6 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hu/
2020-06-09 13:34:11 +00:00
Ady Nemo
d8859001a7 Translated using Weblate (French)
Currently translated at 100.0% (246 of 246 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/
2020-06-09 13:34:11 +00:00
e583e45d9a Use pictrs instead of pictshare 2020-06-08 19:52:32 +02:00
a13e9fe395 Adding galician, turkish, hungarian, and ukrainian languages. 2020-06-07 07:51:36 -04:00
ernestwisniewski
c1421c9c44
Adding Polish language (#784) 2020-06-07 07:34:15 -04:00
55f179bf33 Merge branch 'master' of https://github.com/lemmynet/lemmy 2020-06-06 21:23:45 -04:00
c7b861e0ba Updating sponsor list. 2020-06-06 21:22:39 -04:00
fe56da1654 Merge remote-tracking branch 'yerba/limit-memory' 2020-06-05 13:05:14 -04:00
Lorenz Schmidt
a3a55dd195
Indicate unstable API in README and mdbook (#781) 2020-06-05 12:03:01 -04:00
63ca6e1e24 Version v0.6.71 2020-06-05 11:49:36 -04:00
9bf444e93a Removing webp for now. 2020-06-05 11:45:04 -04:00
8257b040be Limit memory usage for pictshare and iframely 2020-06-05 17:07:38 +02:00
7c0ea6cfa7 Version v0.6.70 2020-06-04 21:42:08 -04:00
ee32072489 Fixing webp issue for safari. 2020-06-04 21:40:57 -04:00
2e882f75f2 Version v0.6.69 2020-06-03 12:11:52 -04:00
9a9a0b9735 Changing cache to 1 hour until we can figure out caching issue. 2020-06-03 12:11:40 -04:00
361cf995da Version v0.6.68 2020-06-03 11:46:09 -04:00
686f1d57cb Merge branch 'dev' 2020-06-03 11:42:06 -04:00
2deb0b3d7f Adding webp for pictshare images / avatars. Fixes #772 2020-06-03 11:41:22 -04:00
b2a7ce834d Enable gzip compression for .svg 2020-06-03 11:59:39 +02:00
59d22ef775 Version v0.6.67 2020-06-02 19:59:49 -04:00
b1b9568c96 Adding text-body class to message toast. Fixes #736 2020-06-02 19:58:28 -04:00
1e88b815da Updating pictshare docker image. 2020-06-02 19:29:29 -04:00
998b9454b2 Version v0.6.66 2020-06-02 14:05:17 -04:00
d5b97c74f8 Merge remote-tracking branch 'weblate/master' 2020-06-02 14:04:54 -04:00
a8160288ae Merge remote-tracking branch 'yerba/cache-control' 2020-06-02 14:03:38 -04:00
21e913192a Add cache-control header to HTTP responses based on mime 2020-06-02 19:22:00 +02:00
PifyZ
16144c7be6 Translated using Weblate (French)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/
2020-06-01 21:32:00 +00:00
odamite
865185efad Translated using Weblate (Finnish)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fi/

Translated using Weblate (Finnish)

Currently translated at 93.4% (228 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fi/
2020-06-01 21:32:00 +00:00
bertol
4148280a8e Translated using Weblate (Basque)
Currently translated at 25.0% (61 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/eu/

Translated using Weblate (Spanish)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/es/
2020-06-01 21:32:00 +00:00
0c2905fd2d Merge branch 'dev' 2020-05-29 13:03:44 -04:00
445f38eb3c More clear remove / delete translations. #712 2020-05-29 13:03:39 -04:00
847e19f22d Version v0.6.65 2020-05-28 14:13:47 -04:00
ef7d10262a Merge remote-tracking branch 'weblate/master' 2020-05-28 14:13:44 -04:00
Dessalines
29fc3681b9
Validate register usernames on the back-end. Fixes #716 (#750)
* Validate register usernames on the back-end. Fixes #716

* Changing name to is_valid_username
2020-05-28 14:07:36 -04:00
871f09d109 Changing meaning to with in sponsors message. 2020-05-28 13:58:17 -04:00
d9ec95ab5b For top-bar post creation, dont select a default community. Fixes #729 2020-05-28 12:55:18 -04:00
dd5cedcfea Remove only show scrollbar on hover, change to auto. Fixes #734 2020-05-28 11:37:23 -04:00
T
1b132b5780 Translated using Weblate (German)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/

Translated using Weblate (German)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/

Translated using Weblate (German)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/

Translated using Weblate (German)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/

Translated using Weblate (German)

Currently translated at 98.3% (240 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/

Translated using Weblate (Dutch)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/

Translated using Weblate (Dutch)

Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/

Translated using Weblate (Dutch)

Currently translated at 99.1% (242 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/

Translated using Weblate (Dutch)

Currently translated at 94.2% (230 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/

Translated using Weblate (Dutch)

Currently translated at 93.8% (229 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/

Translated using Weblate (Dutch)

Currently translated at 90.1% (220 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/nl/
2020-05-28 10:15:06 +00:00
Dessalines
80653a5317
Remove federation option from master. (#745) 2020-05-25 18:48:24 -04:00
e54eac6227 Adding use_tls to lemmy.hjson for email config. 2020-05-24 18:31:17 -04:00
4f518a5d92 Version v0.6.64 2020-05-24 13:25:50 -04:00
0a167a7bc7 Merge remote-tracking branch 'weblate/master' 2020-05-24 13:25:49 -04:00
7f58ea2725 Updating federation dev branch. 2020-05-24 13:25:29 -04:00
ButterflyOfFire
8ec2dcc633 Translated using Weblate (Arabic)
Currently translated at 76.2% (186 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ar/
2020-05-22 15:24:40 +00:00
Adam Adamsky
67d3595880 Translated using Weblate (Polish)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pl/
2020-05-22 15:24:40 +00:00
diorama
6034303b24 Translated using Weblate (Italian)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/
2020-05-22 15:24:40 +00:00
ed553aa9f0 Updating sponsor link. 2020-05-21 21:15:39 -04:00
18d0edd4eb Version v0.6.63 2020-05-18 09:53:02 -04:00
679aa5d073 Merge remote-tracking branch 'weblate/master' 2020-05-18 09:53:01 -04:00
7bc9bc962a Merge branch 'dev' 2020-05-18 09:52:41 -04:00
b5c1fec845 Fixing issue with empty crossposts. 2020-05-18 09:50:05 -04:00
diorama
bccca4e710 Translated using Weblate (Italian)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/
2020-05-18 11:58:46 +00:00
Riccardo Mazzon
e664d7f5b9 Translated using Weblate (Italian)
Currently translated at 95.0% (232 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/
2020-05-18 11:58:46 +00:00
ButterflyOfFire
879048983c Translated using Weblate (French)
Currently translated at 100.0% (244 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/
2020-05-18 11:58:46 +00:00
diorama
96f6c1913f Translated using Weblate (Italian)
Currently translated at 95.0% (232 of 244 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/it/
2020-05-18 11:58:46 +00:00
106ce6096b Version v0.6.62 2020-05-17 18:46:15 -04:00
99e4551cf6 Simplify cross-posts. Fixes #727 2020-05-17 18:39:15 -04:00
f5e96d9372 Version v0.6.61 2020-05-17 16:53:56 -04:00
6fe3d43049 Adding a sponsor. 2020-05-17 16:49:34 -04:00
6fe4184f72 Adding Greek and Basque languages for weblate. 2020-05-16 22:52:28 -04:00
d2e38b1062 Updating docs to setup lemmy.hjson beforehand. 2020-05-15 10:21:37 -04:00
155b99b64e Version v0.6.60 2020-05-12 15:27:28 -04:00
4d02e4c021 Merge branch 'fix_password_autocomplete_fields' 2020-05-12 15:26:54 -04:00
Dessalines
f971e31171 Changing image_content_type function to Result. (#711)
* Changing image_content_type function to Result.

* Changing image to image/
2020-05-12 15:23:48 -04:00
913383ca75 Removing autocomplete for register and user settings password fields.
- Fixes #708
2020-05-12 14:36:22 -04:00
c22310bdaf Version v0.6.59 2020-05-11 19:08:28 -04:00
eb72ace854 Pictshare only cache image content types. Fixes #709 2020-05-11 19:06:12 -04:00
cb88510964 Version v0.6.58 2020-05-11 14:04:23 -04:00
88b86721c6 Merge remote-tracking branch 'weblate/master' 2020-05-11 14:04:21 -04:00
f7c947888f Removing 2nd match. 2020-05-11 14:01:10 -04:00
2719daffb0 Merge branch 'thumbnail-outsourced' of https://github.com/AndreVallestero/lemmy into AndreVallestero-thumbnail-outsourced 2020-05-11 13:20:03 -04:00
Richie Zhang
d17316508c Improve shell scripts (#705)
* Improve init-db.sh

Allow custom database parameters.
Abstract common functionality.

* Improve install.sh

Abstract common functionality.
Ask to automagically reload the project when source files are changed.
2020-05-11 13:16:08 -04:00
Andre Vallestero
9155469e8a Removed redundant erroneous check 2020-05-11 11:29:35 -04:00
c76b91d595 Translated using Weblate (French)
Currently translated at 97.9% (238 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/fr/

Translated using Weblate (German)

Currently translated at 98.3% (239 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/
2020-05-10 14:29:49 +00:00
Eugeny
ade1d35463 Translated using Weblate (Russian)
Currently translated at 96.7% (235 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ru/

Translated using Weblate (Russian)

Currently translated at 95.4% (232 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ru/
2020-05-10 14:29:49 +00:00
b83aae0b57 Version v0.6.57 2020-05-08 14:08:35 -04:00
df23bf7141 Merge remote-tracking branch 'weblate/master' 2020-05-08 14:08:34 -04:00
a6d9a9e1a8 Merge branch 'dev' 2020-05-08 14:03:05 -04:00
e6717c86ac Adding support for webp images on the front end. Fixes #703 2020-05-08 14:02:42 -04:00
9bc45def17 Removing button wrapper for comment context. Fixes #702 2020-05-08 14:02:29 -04:00
2db6f35b7c Removing button wrapper for comment context. Fixes #702 2020-05-08 13:36:52 -04:00
Nika Tskhakaia
f3fa6c1fc0 Translated using Weblate (Georgian)
Currently translated at 90.9% (221 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ka/
2020-05-08 15:35:19 +00:00
6r6
c836fa6912 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/
2020-05-08 15:35:19 +00:00
Nishant Aswani
a267443777 Translated using Weblate (Hindi)
Currently translated at 25.1% (61 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hi/
2020-05-08 15:35:19 +00:00
Sylvia Ji
2fab9b5b9b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/
2020-05-08 15:35:19 +00:00
Andre Vallestero
38381ba287 Removed hardcoded thumbnail value 2020-05-07 21:33:47 -04:00
Andre Vallestero
ffcbb7613e Added additional check for pictshare thumbnail 2020-05-07 21:30:23 -04:00
Andre Vallestero
bc8ad12b04 Added externally hosted UI thumbnail support 2020-05-07 21:26:08 -04:00
Andre Vallestero
243dda543c Removed debug line 2020-05-06 20:46:00 -04:00
Andre Vallestero
d96c66ba9f Thumbnail generation for iframely incompatible sources 2020-05-06 20:40:36 -04:00
88e1251d6b Version v0.6.56 2020-05-03 17:27:25 -04:00
e7a33f4988 Merge remote-tracking branch 'weblate/master' 2020-05-03 17:27:23 -04:00
2f7a4ecdb2 Updating Cargo deps. Fixes #666 2020-05-03 17:25:55 -04:00
Nishant Aswani
0d08cf36f8 Translated using Weblate (Hindi)
Currently translated at 16.8% (41 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hi/

Translated using Weblate (Hindi)

Currently translated at 4.5% (11 of 243 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/hi/
2020-05-03 17:03:25 +00:00
8c765b25bc Merge branch 'master' of https://github.com/lemmynet/lemmy 2020-05-01 21:53:47 -04:00
51eb538631 Merge remote-tracking branch 'weblate/master' 2020-05-01 21:53:34 -04:00
915fe79d7a Added translation using Weblate (Hindi) 2020-05-02 01:52:15 +00:00
Sylvia Ji
ec087ce28a Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/
2020-05-02 01:52:15 +00:00
43592c32d9 Adding hindi template. 2020-05-01 21:51:04 -04:00
Richie Zhang
8d4ab4555c Improve the yes/no prompt in install.sh (#683)
* Improve the yes/no prompt in install.sh

The old implementation used a feature only present on Bash version 4, which not all systems have. The new version should work on all bash shells.

* Update install.sh
2020-05-01 10:33:28 -04:00
Dessalines
d5f62e1355 Merge pull request #684 from pfcoder/fix-dbinit
fix shell quotation error of server db-init.sh
2020-05-01 10:31:55 -04:00
pfcoder
6d73b16716 fix shell quotation error of server db-init.sh 2020-05-01 21:58:52 +08:00
9b93d4c098 Better private-message form. Fixes #652 2020-04-29 17:52:18 -04:00
59852d6899 Merge branch 'nyu-ossd-s20-link_images' 2020-04-27 18:22:10 -04:00
2f47ea2228 Merge branch 'link_images' of https://github.com/nyu-ossd-s20/lemmy into nyu-ossd-s20-link_images 2020-04-27 18:20:58 -04:00
niniack
09d4bf1d7a swap imgur links for github links 2020-04-27 16:22:56 -04:00
66134823bf Version v0.6.55 2020-04-27 09:07:31 -04:00
72433b37c4 Adding a sponsor. 2020-04-27 08:55:31 -04:00
82941001e8 Fixing sponsor issue. 2020-04-26 19:09:47 -04:00
92e4edf9b7 Version v0.6.54 2020-04-26 10:57:27 -04:00
f4b5c1f27b Adding sponsor. 2020-04-26 10:56:53 -04:00
610cce4162 Hide online counts for now. #664 2020-04-26 10:51:59 -04:00
Nishant Aswani
674e221335 added main_screen and chat_screen (#665) 2020-04-26 10:01:21 -04:00
28ca7a727c Merge branch 'master' of https://github.com/dessalines/lemmy 2020-04-22 16:52:50 -04:00
d448bc6f9a Adding code mirror section to readme. 2020-04-22 16:52:24 -04:00
Nishant Aswani
d0fa235e84 Updated ranking algorithm graph in docs (#654)
* Regraphed ranking algorithm demo with seaborn plot

* use imgur mirror for ranking algo image
2020-04-22 15:38:51 -04:00
c22090c602 Version v0.6.53 2020-04-22 00:30:43 -04:00
313d023eef Changing wait to spawn in websocket. 2020-04-22 00:29:25 -04:00
b2a89cd217 Version v0.6.52 2020-04-21 20:43:38 -04:00
b92b8a9e15 Merge remote-tracking branch 'weblate/master' 2020-04-21 20:43:35 -04:00
Sylvia Ji
ec23de7e33 Translated using Weblate (Japanese)
Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ja/

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (237 of 237 strings)

Translation: Lemmy/lemmy
Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/zh_Hans/
2020-04-21 11:02:26 +00:00
96 changed files with 2295 additions and 1420 deletions

2
CODE_OF_CONDUCT.md vendored
View file

@ -30,6 +30,6 @@ In the Lemmy community we strive to go the extra step to look out for each other
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you couldve communicated better — remember that its your responsibility to make others comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
The enforcement policies listed above apply to all official Lemmy venues; including git repositories under [github.com/LemmyNet/lemmy](https://github.com/LemmyNet/lemmy) and [yerbamate.dev/dessalines/lemmy](https://yerbamate.dev/dessalines/lemmy), the [Matrix channel](https://matrix.to/#/!BZVTUuEiNmRcbFeLeI:matrix.org?via=matrix.org&via=privacytools.io&via=permaweb.io); and all instances under lemmy.ml. For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
The enforcement policies listed above apply to all official Lemmy venues; including git repositories under [github.com/LemmyNet/lemmy](https://github.com/LemmyNet/lemmy) and [yerbamate.dev/LemmyNet/lemmy](https://yerbamate.dev/LemmyNet/lemmy), the [Matrix channel](https://matrix.to/#/!BZVTUuEiNmRcbFeLeI:matrix.org?via=matrix.org&via=privacytools.io&via=permaweb.io); and all instances under lemmy.ml. For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
Adapted from the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct), which is based on the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).

15
README.md vendored
View file

@ -34,7 +34,7 @@
Front Page|Post
---|---
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/chat_screen.png)
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).
@ -44,7 +44,7 @@ The overall goal is to create an easily self-hostable, decentralized alternative
Each lemmy server can set its own moderation policy; appointing site-wide admins, and community moderators to keep out the trolls, and foster a healthy, non-toxic environment where all can feel comfortable contributing.
*Note: Federation is still in active development*
*Note: Federation is still in active development and the WebSocket, as well as, HTTP API are currently unstable*
### Why's it called Lemmy?
@ -125,16 +125,19 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent
- [Docker Development](https://dev.lemmy.ml/docs/contributing_docker_development.html)
- [Local Development](https://dev.lemmy.ml/docs/contributing_local_development.html)
### Translations
### Translations
If you want to help with translating, take a look at [Weblate](https://weblate.yerbamate.dev/projects/lemmy/).
## Contact
- [Mastodon](https://mastodon.social/@LemmyDev) - [![Mastodon Follow](https://img.shields.io/mastodon/follow/810572?domain=https%3A%2F%2Fmastodon.social&style=social)](https://mastodon.social/@LemmyDev)
- [Matrix](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org) - [![Matrix](https://img.shields.io/matrix/rust-reddit-fediverse:matrix.org.svg?label=matrix-chat)](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org)
- [Mastodon](https://mastodon.social/@LemmyDev)
- [Matrix](https://riot.im/app/#/room/#rust-reddit-fediverse:matrix.org)
## Code Mirrors
- [GitHub](https://github.com/LemmyNet/lemmy)
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
- [Gitea](https://yerbamate.dev/LemmyNet/lemmy)
- [GitLab](https://gitlab.com/dessalines/lemmy)
## Credits

63
RELEASES.md vendored
View file

@ -1,3 +1,66 @@
# Lemmy v0.7.0 Release (2020-06-23)
This release replaces [pictshare](https://github.com/HaschekSolutions/pictshare)
with [pict-rs](https://git.asonix.dog/asonix/pict-rs), which improves performance
and security.
Overall, since our last major release in January (v0.6.0), we have closed over
[100 issues!](https://github.com/LemmyNet/lemmy/milestone/16?closed=1)
- Site-wide list of recent comments
- Reconnecting websockets
- Many more themes, including a default light one.
- Expandable embeds for post links (and thumbnails), from
[iframely](https://github.com/itteco/iframely)
- Better icons
- Emoji autocomplete to post and message bodies, and an Emoji Picker
- Post body now searchable
- Community title and description is now searchable
- Simplified cross-posts
- Better documentation
- LOTS more languages
- Lots of bugs squashed
- And more ...
## Upgrading
Before starting the upgrade, make sure that you have a working backup of your
database and image files. See our
[documentation](https://dev.lemmy.ml/docs/administration_backup_and_restore.html)
for backup instructions.
**With Ansible:**
```
# deploy with ansible from your local lemmy git repo
git pull
cd ansible
ansible-playbook lemmy.yml
# connect via ssh to run the migration script
ssh your-server
cd /lemmy/
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/migrate-pictshare-to-pictrs.bash
chmod +x migrate-pictshare-to-pictrs.bash
sudo ./migrate-pictshare-to-pictrs.bash
```
**With manual Docker installation:**
```
# run these commands on your server
cd /lemmy
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/ansible/templates/nginx.conf
# Replace the {{ vars }}
sudo mv nginx.conf /etc/nginx/sites-enabled/lemmy.conf
sudo nginx -s reload
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/migrate-pictshare-to-pictrs.bash
chmod +x migrate-pictshare-to-pictrs.bash
sudo bash migrate-pictshare-to-pictrs.bash
```
**Note:** After upgrading, all users need to reload the page, then logout and
login again, so that images are loaded correctly.
# Lemmy v0.6.0 Release (2020-01-16)
`v0.6.0` is here, and we've closed [41 issues!](https://github.com/LemmyNet/lemmy/milestone/15?closed=1)

2
ansible/VERSION vendored
View file

@ -1 +1 @@
v0.6.51
v0.7.1

1
ansible/ansible.cfg vendored
View file

@ -1,5 +1,6 @@
[defaults]
inventory=inventory
interpreter_python=/usr/bin/python3
[ssh_connection]
pipelining = True

8
ansible/lemmy.yml vendored
View file

@ -24,10 +24,11 @@
creates: '/etc/letsencrypt/live/{{domain}}/privkey.pem'
- name: create lemmy folder
file: path={{item.path}} state=directory
file: path={{item.path}} {{item.owner}} state=directory
with_items:
- { path: '/lemmy/' }
- { path: '/lemmy/volumes/' }
- { path: '/lemmy/', owner: 'root' }
- { path: '/lemmy/volumes/', owner: 'root' }
- { path: '/lemmy/volumes/pictrs/', owner: '991' }
- block:
- name: add template files
@ -59,6 +60,7 @@
project_src: /lemmy/
state: present
pull: yes
remove_orphans: yes
- name: reload nginx with new config
shell: nginx -s reload

View file

@ -26,10 +26,11 @@
creates: '/etc/letsencrypt/live/{{domain}}/privkey.pem'
- name: create lemmy folder
file: path={{item.path}} state=directory
file: path={{item.path}} owner={{item.owner}} state=directory
with_items:
- { path: '/lemmy/' }
- { path: '/lemmy/volumes/' }
- { path: '/lemmy/', owner: 'root' }
- { path: '/lemmy/volumes/', owner: 'root' }
- { path: '/lemmy/volumes/pictrs/', owner: '991' }
- block:
- name: add template files
@ -88,6 +89,7 @@
project_src: /lemmy/
state: present
recreate: always
remove_orphans: yes
ignore_errors: yes
- name: reload nginx with new config

View file

@ -12,7 +12,7 @@ services:
- ./lemmy.hjson:/config/config.hjson:ro
depends_on:
- postgres
- pictshare
- pictrs
- iframely
postgres:
@ -25,12 +25,13 @@ services:
- ./volumes/postgres:/var/lib/postgresql/data
restart: always
pictshare:
image: shtripok/pictshare:latest
pictrs:
image: asonix/pictrs:amd64-v0.1.0-r9
user: 991:991
ports:
- "127.0.0.1:8537:80"
- "127.0.0.1:8537:8080"
volumes:
- ./volumes/pictshare:/usr/share/nginx/html/data
- ./volumes/pictrs:/mnt
restart: always
iframely:

View file

@ -36,7 +36,7 @@ server {
# It might be nice to compress JSON, but leaving that out to protect against potential
# compression+encryption information leak attacks like BREACH.
gzip on;
gzip_types text/css application/javascript;
gzip_types text/css application/javascript image/svg+xml;
gzip_vary on;
# Only connect to this site via HTTPS for the two years
@ -48,8 +48,8 @@ server {
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
# Upload limit for pictshare
client_max_body_size 50M;
# Upload limit for pictrs
client_max_body_size 20M;
location / {
proxy_pass http://0.0.0.0:8536;
@ -70,15 +70,21 @@ server {
proxy_cache_min_uses 5;
}
location /pictshare/ {
proxy_pass http://0.0.0.0:8537/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Redirect pictshare images to pictrs
location ~ /pictshare/(.*)$ {
return 301 /pictrs/image/$1;
}
if ($request_uri ~ \.(?:ico|gif|jpe?g|png|webp|bmp|mp4)$) {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# pict-rs images
location /pictrs {
location /pictrs/image {
proxy_pass http://0.0.0.0:8537/image;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Block the import
return 403;
}
location /iframely/ {

15
docker/dev/Dockerfile vendored
View file

@ -21,17 +21,13 @@ COPY server/Cargo.toml server/Cargo.lock ./
RUN sudo chown -R rust:rust .
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
RUN cargo build --release
RUN cargo build
RUN rm -f ./target/x86_64-unknown-linux-musl/release/deps/lemmy_server*
COPY server/src ./src/
COPY server/migrations ./migrations/
# Build for release
RUN cargo build --frozen --release
# Get diesel-cli on there just in case
# RUN cargo install diesel_cli --no-default-features --features postgres
# Build for debug
RUN cargo build
FROM ekidd/rust-musl-builder:1.42.0-openssl11 as docs
WORKDIR /app
@ -39,15 +35,14 @@ COPY docs ./docs
RUN sudo chown -R rust:rust .
RUN mdbook build docs/
FROM alpine:3.10
FROM alpine:3.12
# Install libpq for postgres
RUN apk add libpq
# Copy resources
COPY server/config/defaults.hjson /config/defaults.hjson
COPY --from=rust /app/server/target/x86_64-unknown-linux-musl/release/lemmy_server /app/lemmy
COPY --from=rust /app/server/target/x86_64-unknown-linux-musl/debug/lemmy_server /app/lemmy
COPY --from=docs /app/docs/book/ /app/dist/documentation/
COPY --from=node /app/ui/dist /app/dist

View file

@ -1,79 +0,0 @@
FROM node:10-jessie as node
WORKDIR /app/ui
# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile
# Build
COPY ui /app/ui
RUN yarn build
# contains qemu-*-static for cross-compilation
FROM multiarch/qemu-user-static as qemu
FROM arm64v8/rust:1.40-buster as rust
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
#COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
# Install musl
#RUN apt-get update && apt-get install -y mc
#RUN apt-get install -y musl-tools mc
#libpq-dev mc
#RUN rustup target add ${TARGET}
# Cache deps
WORKDIR /app
RUN USER=root cargo new server
WORKDIR /app/server
COPY server/Cargo.toml server/Cargo.lock ./
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
RUN cargo build --release
# RUN cargo build
COPY server/src ./src/
COPY server/migrations ./migrations/
RUN rm -f ./target/release/deps/lemmy_server* ; rm -f ./target/debug/deps/lemmy_server*
# build for release
RUN cargo build --frozen --release
# RUN cargo build --frozen
# Get diesel-cli on there just in case
# RUN cargo install diesel_cli --no-default-features --features postgres
# RUN cp /app/server/target/debug/lemmy_server /app/server/ready
RUN cp /app/server/target/release/lemmy_server /app/server/ready
#FROM alpine:3.10
# debian because build with dynamic linking with debian:buster
FROM arm64v8/debian:buster-slim as lemmy
#COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
# Install libpq for postgres
#RUN apk add libpq
RUN apt-get update && apt-get install -y libpq5
RUN addgroup --gid 1000 lemmy
# for alpine
#RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# for debian
RUN adduser --disabled-password --shell /bin/sh --uid 1000 --ingroup lemmy lemmy
# Copy resources
COPY server/config/defaults.hjson /config/defaults.hjson
COPY --from=rust /app/server/ready /app/lemmy
COPY --from=node /app/ui/dist /app/dist
RUN chown lemmy:lemmy /app/lemmy
USER lemmy
EXPOSE 8536
CMD ["/app/lemmy"]

View file

@ -1,79 +0,0 @@
FROM node:10-jessie as node
WORKDIR /app/ui
# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile
# Build
COPY ui /app/ui
RUN yarn build
# contains qemu-*-static for cross-compilation
FROM multiarch/qemu-user-static as qemu
FROM arm32v7/rust:1.37-buster as rust
#COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
# Install musl
#RUN apt-get update && apt-get install -y mc
#RUN apt-get install -y musl-tools mc
#libpq-dev mc
#RUN rustup target add ${TARGET}
# Cache deps
WORKDIR /app
RUN USER=root cargo new server
WORKDIR /app/server
COPY server/Cargo.toml server/Cargo.lock ./
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
#RUN cargo build --release
# RUN cargo build
RUN RUSTFLAGS='-Ccodegen-units=1' cargo build
COPY server/src ./src/
COPY server/migrations ./migrations/
RUN rm -f ./target/release/deps/lemmy_server* ; rm -f ./target/debug/deps/lemmy_server*
# build for release
#RUN cargo build --frozen --release
RUN cargo build --frozen
# Get diesel-cli on there just in case
# RUN cargo install diesel_cli --no-default-features --features postgres
RUN cp /app/server/target/debug/lemmy_server /app/server/ready
#RUN cp /app/server/target/release/lemmy_server /app/server/ready
#FROM alpine:3.10
# debian because build with dynamic linking with debian:buster
FROM arm32v7/debian:buster-slim as lemmy
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
# Install libpq for postgres
#RUN apk add libpq
RUN apt-get update && apt-get install -y libpq5
RUN addgroup --gid 1000 lemmy
# for alpine
#RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# for debian
RUN adduser --disabled-password --shell /bin/sh --uid 1000 --ingroup lemmy lemmy
# Copy resources
COPY server/config/defaults.hjson /config/defaults.hjson
COPY --from=rust /app/server/ready /app/lemmy
COPY --from=node /app/ui/dist /app/dist
RUN chown lemmy:lemmy /app/lemmy
USER lemmy
EXPOSE 8536
CMD ["/app/lemmy"]

View file

@ -1,88 +0,0 @@
# can be build on x64, arm32, arm64 platforms
# to build on target platform run
# docker build -f Dockerfile.libc -t dessalines/lemmy:version ../..
#
# to use docker buildx run
# docker buildx build --platform linux/amd64,linux/arm64 -f Dockerfile.libc -t YOURNAME/lemmy --push ../..
FROM node:12-buster as node
# use this if use docker buildx
#FROM --platform=$BUILDPLATFORM node:12-buster as node
WORKDIR /app/ui
# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile --network-timeout 100000
# Build
COPY ui /app/ui
RUN yarn build
FROM rust:1.42 as rust
# Cache deps
WORKDIR /app
RUN USER=root cargo new server
WORKDIR /app/server
COPY server/Cargo.toml server/Cargo.lock ./
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
RUN cargo build --release
#RUN cargo build && \
# rm -f ./target/release/deps/lemmy_server* ; rm -f ./target/debug/deps/lemmy_server*
COPY server/src ./src/
COPY server/migrations ./migrations/
# build for release
# workaround for https://github.com/rust-lang/rust/issues/62896
#RUN RUSTFLAGS='-Ccodegen-units=1' cargo build --release
RUN cargo build --release --frozen
#RUN cargo build --frozen
# Get diesel-cli on there just in case
# RUN cargo install diesel_cli --no-default-features --features postgres
# make result place always the same for lemmy container
RUN cp /app/server/target/release/lemmy_server /app/server/ready
#RUN cp /app/server/target/debug/lemmy_server /app/server/ready
FROM rust:1.42 as docs
WORKDIR /app
# Build docs
COPY docs ./docs
RUN cargo install mdbook
RUN mdbook build docs/
#FROM alpine:3.10
# debian because build with dynamic linking with debian:buster
FROM debian:buster as lemmy
# Install libpq for postgres
#RUN apk add libpq
RUN apt-get update && apt-get install -y libpq5
RUN addgroup --gid 1000 lemmy
# for alpine
#RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# for debian
RUN adduser --disabled-password --shell /bin/sh --uid 1000 --ingroup lemmy lemmy
# Copy resources
COPY server/config/defaults.hjson /config/defaults.hjson
COPY --from=node /app/ui/dist /app/dist
COPY --from=docs /app/docs/book/ /app/dist/documentation/
COPY --from=rust /app/server/ready /app/lemmy
RUN chown lemmy:lemmy /app/lemmy
USER lemmy
EXPOSE 8536
CMD ["/app/lemmy"]

View file

@ -1,15 +1,6 @@
version: '3.3'
services:
postgres:
image: postgres:12-alpine
environment:
- POSTGRES_USER=lemmy
- POSTGRES_PASSWORD=password
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
restart: always
lemmy:
build:
@ -23,16 +14,27 @@ services:
volumes:
- ../lemmy.hjson:/config/config.hjson
depends_on:
- pictrs
- postgres
- pictshare
- iframely
pictshare:
image: shtripok/pictshare:latest
ports:
- "127.0.0.1:8537:80"
postgres:
image: postgres:12-alpine
environment:
- POSTGRES_USER=lemmy
- POSTGRES_PASSWORD=password
- POSTGRES_DB=lemmy
volumes:
- ./volumes/pictshare:/usr/share/nginx/html/data
- ./volumes/postgres:/var/lib/postgresql/data
restart: always
pictrs:
image: asonix/pictrs:v0.1.13-r0
ports:
- "127.0.0.1:8537:8080"
user: 991:991
volumes:
- ./volumes/pictrs:/mnt
restart: always
iframely:

View file

@ -1,2 +1,6 @@
#!/bin/sh
set -e
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
docker-compose up -d --no-deps --build

View file

@ -1,4 +1,8 @@
#!/bin/sh
set -e
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
# Rebuilding dev docker
docker-compose build

4
docker/lemmy.hjson vendored
View file

@ -23,9 +23,6 @@
jwt_secret: "changeme"
# The dir for the front end
front_end_dir: "/app/dist"
# whether to enable activitypub federation. this feature is in alpha, do not enable in production, as might
# cause problems like remote instances fetching and permanently storing bad data.
federation_enabled: false
# rate limits for various user actions, by user ip
rate_limit: {
# maximum number of messages created in interval
@ -60,6 +57,7 @@
# smtp_password: ""
# # address to send emails from, eg "info@your-instance.com"
# smtp_from_address: ""
# use_tls: true
# }
}

64
docker/prod/Dockerfile vendored Normal file
View file

@ -0,0 +1,64 @@
ARG RUST_BUILDER_IMAGE=shtripok/rust-musl-builder:arm
FROM $RUST_BUILDER_IMAGE as rust
#ARG RUSTRELEASEDIR="debug"
ARG RUSTRELEASEDIR="release"
# Cache deps
WORKDIR /app
RUN sudo chown -R rust:rust .
RUN USER=root cargo new server
WORKDIR /app/server
COPY --chown=rust:rust server/Cargo.toml server/Cargo.lock ./
#RUN sudo chown -R rust:rust .
RUN mkdir -p ./src/bin \
&& echo 'fn main() { println!("Dummy") }' > ./src/bin/main.rs
RUN cargo build --release
RUN rm -f ./target/$CARGO_BUILD_TARGET/$RUSTRELEASEDIR/deps/lemmy_server*
COPY --chown=rust:rust server/src ./src/
COPY --chown=rust:rust server/migrations ./migrations/
# build for release
# workaround for https://github.com/rust-lang/rust/issues/62896
RUN cargo build --frozen --release
# reduce binary size
RUN strip ./target/$CARGO_BUILD_TARGET/$RUSTRELEASEDIR/lemmy_server
RUN cp ./target/$CARGO_BUILD_TARGET/$RUSTRELEASEDIR/lemmy_server /app/server/
FROM $RUST_BUILDER_IMAGE as docs
WORKDIR /app
COPY --chown=rust:rust docs ./docs
RUN mdbook build docs/
FROM node:12-buster as node
WORKDIR /app/ui
# Cache deps
COPY ui/package.json ui/yarn.lock ./
RUN yarn install --pure-lockfile --network-timeout 600000
# Build
COPY ui /app/ui
RUN yarn build
FROM alpine:3.12 as lemmy
# Install libpq for postgres
RUN apk add libpq
RUN addgroup -g 1000 lemmy
RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
# Copy resources
COPY --chown=lemmy:lemmy server/config/defaults.hjson /config/defaults.hjson
COPY --chown=lemmy:lemmy --from=rust /app/server/lemmy_server /app/lemmy
COPY --chown=lemmy:lemmy --from=docs /app/docs/book/ /app/dist/documentation/
COPY --chown=lemmy:lemmy --from=node /app/ui/dist /app/dist
RUN chown lemmy:lemmy /app/lemmy
USER lemmy
EXPOSE 8536
CMD ["/app/lemmy"]

View file

@ -1,4 +1,5 @@
#!/bin/sh
set -e
git checkout master
# Import translations
@ -20,7 +21,7 @@ git add "server/src/version.rs"
echo $new_tag > "ansible/VERSION"
git add "ansible/VERSION"
cd docker/dev || exit
cd docker/prod || exit
# Changing the docker-compose prod
sed -i "s/dessalines\/lemmy:.*/dessalines\/lemmy:$new_tag/" ../prod/docker-compose.yml
@ -32,41 +33,24 @@ git add ../../ansible/templates/docker-compose.yml
git commit -m"Version $new_tag"
git tag $new_tag
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
# Rebuilding docker
docker-compose build
docker tag dev_lemmy:latest dessalines/lemmy:x64-$new_tag
docker push dessalines/lemmy:x64-$new_tag
# Build for Raspberry Pi / other archs
# Arm currently not working
# docker build -t lemmy:armv7hf -f Dockerfile.armv7hf ../../
# docker tag lemmy:armv7hf dessalines/lemmy:armv7hf-$new_tag
# docker push dessalines/lemmy:armv7hf-$new_tag
# aarch64
# Only do this on major releases (IE the third semver is 0)
if [ $third_semver -eq 0 ]; then
# Registering qemu binaries
docker run --rm --privileged multiarch/qemu-user-static:register --reset
docker build -t lemmy:aarch64 -f Dockerfile.aarch64 ../../
docker tag lemmy:aarch64 dessalines/lemmy:arm64-$new_tag
docker push dessalines/lemmy:arm64-$new_tag
fi
# Creating the manifest for the multi-arch build
if [ $third_semver -eq 0 ]; then
docker manifest create dessalines/lemmy:$new_tag \
dessalines/lemmy:x64-$new_tag \
dessalines/lemmy:arm64-$new_tag
# TODO get linux/arm/v7 build working
# Build for Raspberry Pi / other archs too
docker buildx build --platform linux/amd64,linux/arm64 ../../ \
--file Dockerfile \
--tag dessalines/lemmy:$new_tag \
--push
else
docker manifest create dessalines/lemmy:$new_tag \
dessalines/lemmy:x64-$new_tag
docker buildx build --platform linux/amd64 ../../ \
--file Dockerfile \
--tag dessalines/lemmy:$new_tag \
--push
fi
docker manifest push dessalines/lemmy:$new_tag
# Push
git push origin $new_tag
git push

View file

@ -1,4 +1,4 @@
version: '3.3'
version: '2.2'
services:
postgres:
@ -12,7 +12,7 @@ services:
restart: always
lemmy:
image: dessalines/lemmy:v0.6.51
image: dessalines/lemmy:v0.7.1
ports:
- "127.0.0.1:8536:8536"
restart: always
@ -22,15 +22,16 @@ services:
- ./lemmy.hjson:/config/config.hjson
depends_on:
- postgres
- pictshare
- pictrs
- iframely
pictshare:
image: shtripok/pictshare:latest
ports:
- "127.0.0.1:8537:80"
pictrs:
image: asonix/pictrs:v0.1.13-r0
ports:
- "127.0.0.1:8537:8080"
user: 991:991
volumes:
- ./volumes/pictshare:/usr/share/nginx/html/data
- ./volumes/pictrs:/mnt
restart: always
iframely:
@ -40,3 +41,4 @@ services:
volumes:
- ./iframely.config.local.js:/iframely/config.local.js:ro
restart: always
mem_limit: 100m

View file

@ -0,0 +1,60 @@
#!/bin/bash
set -e
if [[ $(id -u) != 0 ]]; then
echo "This migration needs to be run as root"
exit
fi
if [[ ! -f docker-compose.yml ]]; then
echo "No docker-compose.yml found in current directory. Is this the right folder?"
exit
fi
# Fixing pictrs permissions
mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs
echo "Restarting docker-compose, making sure that pictrs is started and pictshare is removed"
docker-compose up -d --remove-orphans
if [[ -z $(docker-compose ps | grep pictrs) ]]; then
echo "Pict-rs is not running, make sure you update Lemmy first"
exit
fi
# echo "Stopping Lemmy so that users dont upload new images during the migration"
# docker-compose stop lemmy
pushd volumes/pictshare/
echo "Importing pictshare images to pict-rs..."
IMAGE_NAMES=*
for image in $IMAGE_NAMES; do
IMAGE_PATH="$(pwd)/$image/$image"
if [[ ! -f $IMAGE_PATH ]]; then
continue
fi
echo -e "\nImporting $IMAGE_PATH"
ret=0
curl --silent --fail -F "images[]=@$IMAGE_PATH" http://127.0.0.1:8537/import || ret=$?
if [[ $ret != 0 ]]; then
echo "Error for $IMAGE_PATH : $ret"
fi
done
echo "Fixing permissions on pictshare folder"
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
popd
echo "Rewrite image links in Lemmy database"
docker-compose exec -u postgres postgres psql -U lemmy -c "UPDATE user_ SET avatar = REPLACE(avatar, 'pictshare', 'pictrs/image') WHERE avatar is not null;"
docker-compose exec -u postgres postgres psql -U lemmy -c "UPDATE post SET url = REPLACE(url, 'pictshare', 'pictrs/image') WHERE url is not null;"
echo "Moving pictshare data folder to pictshare_backup"
mv volumes/pictshare volumes/pictshare_backup
echo "Migration done, starting Lemmy again"
echo "If everything went well, you can delete ./volumes/pictshare_backup/"
docker-compose start lemmy

BIN
docs/img/chat_screen.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
docs/img/main_screen.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/img/rank_algorithm.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

2
docs/src/about.md vendored
View file

@ -2,7 +2,7 @@
Front Page|Post
---|---
![main screen](https://i.imgur.com/kZSRcRu.png)|![chat screen](https://i.imgur.com/4XghNh6.png)
![main screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/main_screen.png)|![chat screen](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/chat_screen.png)
[Lemmy](https://github.com/LemmyNet/lemmy) is similar to sites like [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), or [Hacker News](https://news.ycombinator.com/): you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the [Fediverse](https://en.wikipedia.org/wiki/Fediverse).

View file

@ -26,4 +26,4 @@ Gravity = Decay gravity, 1.8 is default
A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k.
![](https://i.imgur.com/w8oBLlL.png)
![](https://raw.githubusercontent.com/LemmyNet/lemmy/master/docs/img/rank_algorithm.png)

View file

@ -6,19 +6,25 @@ Make sure you have both docker and docker-compose(>=`1.24.0`) installed. On Ubun
# create a folder for the lemmy files. the location doesnt matter, you can put this anywhere you want
mkdir /lemmy
cd /lemmy
# download default config files
wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/lemmy.hjson
wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/iframely.config.local.js
docker-compose up -d
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/lemmy.hjson
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/iframely.config.local.js
# Set correct permissions for pictrs folder
mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs
```
After this, have a look at the [config file](administration_configuration.md) named `lemmy.hjson`, and adjust it, in particular the hostname.
After this, have a look at the [config file](administration_configuration.md) named `lemmy.hjson`, and adjust it, in particular the hostname, and possibly the db password. Then run:
To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](/ansible/templates/nginx.conf), could be setup with:
`docker-compose up -d`
To make Lemmy available outside the server, you need to setup a reverse proxy, like Nginx. [A sample nginx config](https://raw.githubusercontent.com/LemmyNet/lemmy/master/ansible/templates/nginx.conf), could be setup with:
```bash
wget https://raw.githubusercontent.com/dessalines/lemmy/master/ansible/templates/nginx.conf
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/ansible/templates/nginx.conf
# Replace the {{ vars }}
sudo mv nginx.conf /etc/nginx/sites-enabled/lemmy.conf
```
@ -30,6 +36,6 @@ You will also need to setup TLS, for example with [Let's Encrypt](https://letsen
To update to the newest version, you can manually change the version in `docker-compose.yml`. Alternatively, fetch the latest version from our git repo:
```bash
wget https://raw.githubusercontent.com/dessalines/lemmy/master/docker/prod/docker-compose.yml
wget https://raw.githubusercontent.com/LemmyNet/lemmy/master/docker/prod/docker-compose.yml
docker-compose up -d
```

View file

@ -4,9 +4,9 @@ Information about contributing to Lemmy, whether it is translating, testing, des
## Issue tracking / Repositories
- [GitHub (for issues)](https://github.com/LemmyNet/lemmy)
- [Gitea](https://yerbamate.dev/dessalines/lemmy)
- [GitLab](https://gitlab.com/dessalines/lemmy)
- [GitHub (for issues and pull requests)](https://github.com/LemmyNet/lemmy)
- [Gitea (only for pull requests)](https://yerbamate.dev/LemmyNet/lemmy)
- [GitLab (only code-mirror)](https://gitlab.com/dessalines/lemmy)
## Translating

View file

@ -3,11 +3,22 @@
## Running
```bash
sudo apt install git docker-compose
git clone https://github.com/LemmyNet/lemmy
cd lemmy/docker/dev
./docker_update.sh # This builds and runs it, updating for your changes
sudo docker-compose up --no-deps --build
```
and go to http://localhost:8536.
Note that compile times when changing `Cargo.toml` are relatively long with Docker, because builds can't be incrementally cached. If this is a problem for you, you should use [Local Development](contributing_local_development.md).
To speed up the Docker compile, add the following to `/etc/docker/daemon.json` and restart Docker.
```
{
"features": {
"buildkit": true
}
}
```
If the build is still too slow, you will have to use a
[local build](contributing_local_development.md) instead.

View file

@ -5,12 +5,12 @@
If you don't have a local clone of the Lemmy repo yet, just run the following command:
```bash
git clone https://yerbamate.dev/nutomic/lemmy.git -b federation
git clone https://github.com/LemmyNet/lemmy -b federation
```
If you already have the Lemmy repo cloned, you need to add a new remote:
```bash
git remote add federation https://yerbamate.dev/nutomic/lemmy.git
git remote add federation https://github.com/LemmyNet/lemmy
git checkout federation
git pull federation federation
```
@ -34,4 +34,4 @@ After the build is finished and the docker-compose setup is running, open [127.0
[127.0.0.1:8541](http://127.0.0.1:8541) in your browser to use the test instances. You can login as admin with
username `lemmy` and password `lemmy`, or create new accounts.
Please get in touch if you want to contribute to this, so we can coordinate things and avoid duplicate work.
Please get in touch if you want to contribute to this, so we can coordinate things and avoid duplicate work.

View file

@ -1,31 +1,67 @@
#### Requirements
### Ubuntu
- [Rust](https://www.rust-lang.org/)
- [Yarn](https://yarnpkg.com/en/)
- [Postgres](https://www.postgresql.org/)
#### Set up Postgres DB
#### Build requirements:
```
sudo apt install git cargo libssl-dev pkg-config libpq-dev yarn curl gnupg2 git
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
```
```bash
#### Get the source code
```
git clone https://github.com/LemmyNet/lemmy.git
# or alternatively from gitea
# git clone https://yerbamate.dev/LemmyNet/lemmy.git
```
All the following commands need to be run either in `lemmy/server` or `lemmy/ui`, as indicated
by the `cd` command.
#### Build the backend (Rust)
```
cd server
./db-init.sh
cargo build
# for development, use `cargo check` instead)
```
Or run the commands manually:
#### Build the frontend (Typescript)
```
cd ui
yarn
yarn build
```
```bash
psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
#### Setup postgresql
```
sudo apt install postgresql
sudo systemctl start postgresql
# initialize postgres database
sudo -u postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
sudo -u postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
# or execute server/db-init.sh
```
#### Running
```bash
git clone https://github.com/LemmyNet/lemmy
cd lemmy
./install.sh
# For live coding, where both the front and back end, automagically reload on any save, do:
# cd ui && yarn start
# cd server && cargo watch -x run
#### Run a local development instance
```
# run each of these in a seperate terminal
cd server && cargo run
ui & yarn start
```
Then open [localhost:4444](http://localhost:4444) in your browser. It will auto-refresh if you edit
any frontend files. For backend coding, you will have to rerun `cargo run`. You can use
`cargo check` as a faster way to find compilation errors.
To speed up incremental builds, you can add the following to `~/.cargo/config`:
```
[target.x86_64-unknown-linux-gnu]
rustflags = ["-Clink-arg=-fuse-ld=lld"]
```
Note that this setup doesn't include image uploads or link previews (provided by pict-rs and
iframely respectively). If you want to test those, you should use the
[Docker development](contributing_docker_development.md).

View file

@ -1,6 +1,6 @@
# Lemmy API
*Note: this may lag behind the actual API endpoints [here](../server/src/api).*
*Note: this may lag behind the actual API endpoints [here](../server/src/api). The API should be considered unstable and may change any time.*
<!-- toc -->

View file

@ -49,5 +49,7 @@
## Member List / Contact Info
General Contact [@LemmyDev Mastodon](https://mastodon.social/@LemmyDev)
- Dessalines [Matrix](https://matrix.to/#/@happydooby:matrix.org)
- Nutomic [Matrix](https://matrix.to/#/@nutomic:matrix.org), [Mastodon](https://radical.town/@felix)
- [Dessalines](https://dev.lemmy.ml/u/dessalines)
- [Nutomic](https://dev.lemmy.ml/u/nutomic)
- [AgreeableLandscape](https://dev.lemmy.ml/u/AgreeableLandscape)
- [fruechtchen](https://dev.lemmy.ml/u/fruechtchen)

73
install.sh vendored
View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
set -e
# Set the database variable to the default first.
@ -10,25 +10,55 @@ export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
export JWT_SECRET=changeme
export HOSTNAME=rrr
yes_no_prompt_invalid() {
echo "Invalid input. Please enter either \"y\" or \"n\"." 1>&2
}
ask_to_init_db() {
init_db_valid=0
init_db_final=0
while [ "$init_db_valid" == 0 ]
do
read -p "Initialize database (y/n)? " init_db
case "$init_db" in
[yY]* ) init_db_valid=1; init_db_final=1;;
[nN]* ) init_db_valid=1; init_db_final=0;;
* ) yes_no_prompt_invalid;;
esac
echo
done
if [ "$init_db_final" = 1 ]
then
source ./server/db-init.sh
read -n 1 -s -r -p "Press ANY KEY to continue execution of this script, press CTRL+C to quit..."
echo
fi
}
ask_to_auto_reload() {
auto_reload_valid=0
auto_reload_final=0
while [ "$auto_reload_valid" == 0 ]
do
echo "Automagically reload the project when source files are changed?"
echo "ONLY ENABLE THIS FOR DEVELOPMENT!"
read -p "(y/n) " auto_reload
case "$auto_reload" in
[yY]* ) auto_reload_valid=1; auto_reload_final=1;;
[nN]* ) auto_reload_valid=1; auto_reload_final=0;;
* ) yes_no_prompt_invalid;;
esac
echo
done
if [ "$auto_reload_final" = 1 ]
then
cd ui && yarn start
cd server && cargo watch -x run
fi
}
# Optionally initialize the database
init_db_valid=0
init_db_final=0
while [ "$init_db_valid" == 0 ]
do
read -p "Initialize database (y/n)? " init_db
case "${init_db,,}" in
y|yes ) init_db_valid=1; init_db_final=1;;
n|no ) init_db_valid=1; init_db_final=0;;
* ) echo "Invalid input" 1>&2;;
esac
echo
done
if [ "$init_db_final" = 1 ]
then
source ./server/db-init.sh
read -n 1 -s -r -p "Press ANY KEY to continue execution of this script, press CTRL+C to quit..."
echo
fi
ask_to_init_db
# Build the web client
cd ui
@ -39,6 +69,5 @@ yarn build
cd ../server
RUST_LOG=debug cargo run
# For live coding, where both the front and back end, automagically reload on any save, do:
# cd ui && yarn start
# cd server && cargo watch -x run
# For live coding, where both the front and back end, automagically reload on any save
ask_to_auto_reload

492
server/Cargo.lock generated vendored

File diff suppressed because it is too large Load diff

28
server/Cargo.toml vendored
View file

@ -1,24 +1,27 @@
[package]
name = "lemmy_server"
version = "0.0.1"
authors = ["Dessalines <happydooby@gmail.com>"]
authors = ["Dessalines <tyhou13@gmx.com>"]
edition = "2018"
[profile.release]
lto = true
[dependencies]
diesel = { version = "1.4.2", features = ["postgres","chrono", "r2d2", "64-column-tables"] }
diesel_migrations = "1.4.0"
dotenv = "0.15.0"
bcrypt = "0.6.2"
bcrypt = "0.7.0"
activitypub = "0.2.0"
chrono = { version = "0.4.7", features = ["serde"] }
failure = "0.1.5"
serde_json = { version = "1.0.48", features = ["preserve_order"]}
serde = { version = "1.0.105", features = ["derive"] }
chrono = "0.4.7"
failure = "0.1.8"
serde_json = "1.0.52"
serde = "1.0.105"
actix = "0.9.0"
actix-web = "2.0.0"
actix-files = "0.2.1"
actix-web-actors = "2.0.0"
actix-rt = "1.0.0"
actix-rt = "1.1.1"
log = "0.4.0"
env_logger = "0.7.1"
rand = "0.7.3"
@ -27,15 +30,14 @@ strum_macros = "0.18.0"
jsonwebtoken = "7.0.1"
regex = "1.3.5"
lazy_static = "1.3.0"
lettre = "0.9.2"
lettre_email = "0.9.2"
lettre = "0.9.3"
lettre_email = "0.9.4"
sha2 = "0.8.1"
rss = "1.9.0"
htmlescape = "0.3.1"
config = "0.10.1"
hjson = "0.8.2"
config = {version = "0.10.1", default-features = false, features = ["hjson"] }
percent-encoding = "2.1.0"
isahc = "0.9"
attohttpc = { version = "0.14.0", default-features = false, features = ["tls-rustls"] }
comrak = "0.7"
tokio = "0.2.18"
tokio = "0.2.20"
futures = "0.3.4"

115
server/db-init.sh vendored
View file

@ -1,43 +1,106 @@
#!/bin/bash
#!/bin/sh
# Default configurations
username=lemmy
dbname=lemmy
port=5432
password=""
password_confirm=""
password_valid=0
yes_no_prompt_invalid() {
echo "Invalid input. Please enter either \"y\" or \"n\"." 1>&2
}
while [ "$password_valid" == 0 ]
do
read -p "Enter database password: " -s password
print_config() {
echo " database name: $dbname"
echo " username: $username"
echo " port: $port"
}
ask_for_db_config() {
echo "The default database configuration is:"
print_config
echo
read -p "Verify database password: " -s password_confirm
echo
echo
# Start the loop from the top if either check fails
if [ -z "$password" ]
then
echo "Error: Password cannot be empty." 1>&2
default_config_final=0
default_config_valid=0
while [ "$default_config_valid" == 0 ]
do
read -p "Use this configuration (y/n)? " default_config
case "$default_config" in
[yY]* ) default_config_valid=1; default_config_final=1;;
[nN]* ) default_config_valid=1; default_config_final=0;;
* ) yes_no_prompt_invalid;;
esac
echo
continue
fi
if [ "$password" != "$password_confirm" ]
done
if [ "$default_config_final" == 0 ]
then
echo "Error: Passwords don't match." 1>&2
echo
continue
config_ok_final=0
while [ "$config_ok_final" == 0 ]
do
read -p "Database name: " dbname
read -p "Username: " username
read -p "Port: " port
#echo
#echo "The database configuration is:"
#print_config
#echo
config_ok_valid=0
while [ "$config_ok_valid" == 0 ]
do
read -p "Use this configuration (y/n)? " config_ok
case "$config_ok" in
[yY]* ) config_ok_valid=1; config_ok_final=1;;
[nN]* ) config_ok_valid=1; config_ok_final=0;;
* ) yes_no_prompt_invalid;;
esac
echo
done
done
fi
}
# Set the password_valid variable to break out of the loop
password_valid=1
done
ask_for_password() {
password=""
password_confirm=""
password_valid=0
while [ "$password_valid" == 0 ]
do
read -p "Enter database password: " -s password
echo
read -p "Verify database password: " -s password_confirm
echo
echo
# Start the loop from the top if either check fails
if [ -z "$password" ]
then
echo "Error: Password cannot be empty." 1>&2
echo
continue
fi
if [ "$password" != "$password_confirm" ]
then
echo "Error: Passwords don't match." 1>&2
echo
continue
fi
# Set the password_valid variable to break out of the loop
password_valid=1
done
}
ask_for_db_config
ask_for_password
psql -c "CREATE USER $username WITH PASSWORD '$password' SUPERUSER;" -U postgres
psql -c 'CREATE DATABASE $dbname WITH OWNER $username;' -U postgres
psql -c "CREATE DATABASE $dbname WITH OWNER $username;" -U postgres
export LEMMY_DATABASE_URL=postgres://$username:$password@localhost:$port/$dbname
echo $LEMMY_DATABASE_URL
echo "The database URL is $LEMMY_DATABASE_URL"

View file

@ -1,4 +1,5 @@
use super::*;
use crate::is_valid_community_name;
#[derive(Serialize, Deserialize)]
pub struct GetCommunity {
@ -220,6 +221,10 @@ impl Perform for Oper<CreateCommunity> {
}
}
if !is_valid_community_name(&data.name) {
return Err(APIError::err("invalid_community_name").into());
}
let user_id = claims.id;
let conn = pool.get()?;
@ -306,6 +311,10 @@ impl Perform for Oper<EditCommunity> {
Err(_e) => return Err(APIError::err("not_logged_in").into()),
};
if !is_valid_community_name(&data.name) {
return Err(APIError::err("invalid_community_name").into());
}
let user_id = claims.id;
let conn = pool.get()?;

View file

@ -18,7 +18,7 @@ use crate::db::user_mention_view::*;
use crate::db::user_view::*;
use crate::db::*;
use crate::{
extract_usernames, fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix,
extract_usernames, fetch_iframely_and_pictrs_data, generate_random_string, naive_from_unix,
naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str,
};

View file

@ -116,9 +116,9 @@ impl Perform for Oper<CreatePost> {
return Err(APIError::err("site_ban").into());
}
// Fetch Iframely and Pictshare cached image
let (iframely_title, iframely_description, iframely_html, pictshare_thumbnail) =
fetch_iframely_and_pictshare_data(data.url.to_owned());
// Fetch Iframely and pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(data.url.to_owned());
let post_form = PostForm {
name: data.name.to_owned(),
@ -135,7 +135,7 @@ impl Perform for Oper<CreatePost> {
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
thumbnail_url: pictshare_thumbnail,
thumbnail_url: pictrs_thumbnail,
};
let inserted_post = match Post::create(&conn, &post_form) {
@ -450,9 +450,9 @@ impl Perform for Oper<EditPost> {
return Err(APIError::err("site_ban").into());
}
// Fetch Iframely and Pictshare cached image
let (iframely_title, iframely_description, iframely_html, pictshare_thumbnail) =
fetch_iframely_and_pictshare_data(data.url.to_owned());
// Fetch Iframely and Pictrs cached image
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(data.url.to_owned());
let post_form = PostForm {
name: data.name.to_owned(),
@ -469,7 +469,7 @@ impl Perform for Oper<EditPost> {
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
thumbnail_url: pictshare_thumbnail,
thumbnail_url: pictrs_thumbnail,
};
let _updated_post = match Post::update(&conn, data.edit_id, &post_form) {

View file

@ -1,4 +1,5 @@
use super::*;
use crate::is_valid_username;
use bcrypt::verify;
#[derive(Serialize, Deserialize, Debug)]
@ -261,6 +262,10 @@ impl Perform for Oper<Register> {
return Err(APIError::err("admin_already_created").into());
}
if !is_valid_username(&data.username) {
return Err(APIError::err("invalid_username").into());
}
// Register the new user
let user_form = UserForm {
name: data.username.to_owned(),

View file

@ -36,7 +36,6 @@ pub mod websocket;
use actix_web::dev::ConnectionInfo;
use chrono::{DateTime, NaiveDateTime, Utc};
use isahc::prelude::*;
use lettre::smtp::authentication::{Credentials, Mechanism};
use lettre::smtp::extension::ClientId;
use lettre::smtp::ConnectionReuseParameters;
@ -73,6 +72,21 @@ pub fn is_email_regex(test: &str) -> bool {
EMAIL_REGEX.is_match(test)
}
pub fn is_image_content_type(test: &str) -> Result<(), failure::Error> {
if attohttpc::get(test)
.send()?
.headers()
.get("Content-Type")
.ok_or_else(|| format_err!("No Content-Type header"))?
.to_str()?
.starts_with("image/")
{
Ok(())
} else {
Err(format_err!("Not an image type."))
}
}
pub fn remove_slurs(test: &str) -> String {
SLUR_REGEX.replace_all(test, "*removed*").to_string()
}
@ -168,28 +182,40 @@ pub struct IframelyResponse {
pub fn fetch_iframely(url: &str) -> Result<IframelyResponse, failure::Error> {
let fetch_url = format!("http://iframely/oembed?url={}", url);
let text = isahc::get(&fetch_url)?.text()?;
let text: String = attohttpc::get(&fetch_url).send()?.text()?;
let res: IframelyResponse = serde_json::from_str(&text)?;
Ok(res)
}
#[derive(Deserialize, Debug)]
pub struct PictshareResponse {
status: String,
url: String,
#[derive(Deserialize, Debug, Clone)]
pub struct PictrsResponse {
files: Vec<PictrsFile>,
msg: String,
}
pub fn fetch_pictshare(image_url: &str) -> Result<PictshareResponse, failure::Error> {
#[derive(Deserialize, Debug, Clone)]
pub struct PictrsFile {
file: String,
delete_token: String,
}
pub fn fetch_pictrs(image_url: &str) -> Result<PictrsResponse, failure::Error> {
is_image_content_type(image_url)?;
let fetch_url = format!(
"http://pictshare/api/geturl.php?url={}",
utf8_percent_encode(image_url, NON_ALPHANUMERIC)
"http://pictrs:8080/image/download?url={}",
utf8_percent_encode(image_url, NON_ALPHANUMERIC) // TODO this might not be needed
);
let text = isahc::get(&fetch_url)?.text()?;
let res: PictshareResponse = serde_json::from_str(&text)?;
Ok(res)
let text = attohttpc::get(&fetch_url).send()?.text()?;
let res: PictrsResponse = serde_json::from_str(&text)?;
if res.msg == "ok" {
Ok(res)
} else {
Err(format_err!("{}", &res.msg))
}
}
fn fetch_iframely_and_pictshare_data(
fn fetch_iframely_and_pictrs_data(
url: Option<String>,
) -> (
Option<String>,
@ -197,36 +223,46 @@ fn fetch_iframely_and_pictshare_data(
Option<String>,
Option<String>,
) {
// Fetch iframely data
let (iframely_title, iframely_description, iframely_thumbnail_url, iframely_html) = match url {
Some(url) => match fetch_iframely(&url) {
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
Err(e) => {
error!("iframely err: {}", e);
(None, None, None, None)
}
},
match &url {
Some(url) => {
// Fetch iframely data
let (iframely_title, iframely_description, iframely_thumbnail_url, iframely_html) =
match fetch_iframely(url) {
Ok(res) => (res.title, res.description, res.thumbnail_url, res.html),
Err(e) => {
error!("iframely err: {}", e);
(None, None, None, None)
}
};
// Fetch pictrs thumbnail
let pictrs_thumbnail = match iframely_thumbnail_url {
Some(iframely_thumbnail_url) => match fetch_pictrs(&iframely_thumbnail_url) {
Ok(res) => Some(res.files[0].file.to_owned()),
Err(e) => {
error!("pictrs err: {}", e);
None
}
},
// Try to generate a small thumbnail if iframely is not supported
None => match fetch_pictrs(&url) {
Ok(res) => Some(res.files[0].file.to_owned()),
Err(e) => {
error!("pictrs err: {}", e);
None
}
},
};
(
iframely_title,
iframely_description,
iframely_html,
pictrs_thumbnail,
)
}
None => (None, None, None, None),
};
// Fetch pictshare thumbnail
let pictshare_thumbnail = match iframely_thumbnail_url {
Some(iframely_thumbnail_url) => match fetch_pictshare(&iframely_thumbnail_url) {
Ok(res) => Some(res.url),
Err(e) => {
error!("pictshare err: {}", e);
None
}
},
None => None,
};
(
iframely_title,
iframely_description,
iframely_html,
pictshare_thumbnail,
)
}
}
pub fn markdown_to_html(text: &str) -> String {
@ -243,9 +279,29 @@ pub fn get_ip(conn_info: &ConnectionInfo) -> String {
.to_string()
}
pub fn is_valid_username(name: &str) -> bool {
VALID_USERNAME_REGEX.is_match(name)
}
pub fn is_valid_community_name(name: &str) -> bool {
VALID_COMMUNITY_NAME_REGEX.is_match(name)
}
#[cfg(test)]
mod tests {
use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str};
use crate::{
extract_usernames, is_email_regex, is_image_content_type, is_valid_community_name,
is_valid_username, remove_slurs, slur_check, slurs_vec_to_str,
};
#[test]
fn test_image() {
assert!(is_image_content_type("https://1734811051.rsc.cdn77.org/data/images/full/365645/as-virus-kills-navajos-in-their-homes-tribal-women-provide-lifeline.jpg?w=600?w=650").is_ok());
assert!(is_image_content_type(
"https://twitter.com/BenjaminNorton/status/1259922424272957440?s=20"
)
.is_err());
}
#[test]
fn test_email() {
@ -253,6 +309,24 @@ mod tests {
assert!(!is_email_regex("nada_neutho"));
}
#[test]
fn test_valid_register_username() {
assert!(is_valid_username("Hello_98"));
assert!(is_valid_username("ten"));
assert!(!is_valid_username("Hello-98"));
assert!(!is_valid_username("a"));
assert!(!is_valid_username(""));
}
#[test]
fn test_valid_community_name() {
assert!(is_valid_community_name("example"));
assert!(is_valid_community_name("example_community"));
assert!(!is_valid_community_name("Example"));
assert!(!is_valid_community_name("Ex"));
assert!(!is_valid_community_name(""));
}
#[test]
fn test_slur_filter() {
let test =
@ -314,4 +388,6 @@ lazy_static! {
static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
static ref SLUR_REGEX: Regex = RegexBuilder::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|nig(\b|g?(a|er)?(s|z)?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap();
static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").unwrap();
static ref VALID_USERNAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9_]{3,20}$").unwrap();
static ref VALID_COMMUNITY_NAME_REGEX: Regex = Regex::new(r"^[a-z0-9_]{3,20}$").unwrap();
}

View file

@ -1,8 +1,15 @@
extern crate lemmy_server;
#[macro_use]
extern crate diesel_migrations;
#[macro_use]
pub extern crate lazy_static;
use crate::lemmy_server::actix_web::dev::Service;
use actix::prelude::*;
use actix_web::body::Body;
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::http::header::CONTENT_TYPE;
use actix_web::http::{header::CACHE_CONTROL, HeaderValue};
use actix_web::*;
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
@ -12,9 +19,18 @@ use lemmy_server::{
settings::Settings,
websocket::server::*,
};
use regex::Regex;
use std::{io, sync::Arc};
use tokio::sync::Mutex;
lazy_static! {
static ref CACHE_CONTROL_REGEX: Regex =
Regex::new("^((text|image)/.+|application/javascript)$").unwrap();
// static ref CACHE_CONTROL_VALUE: String = format!("public, max-age={}", 365 * 24 * 60 * 60);
// Test out 1 hour here, this is breaking some things
static ref CACHE_CONTROL_VALUE: String = format!("public, max-age={}", 60 * 60);
}
embed_migrations!();
#[actix_rt::main]
@ -51,6 +67,7 @@ async fn main() -> io::Result<()> {
let settings = Settings::get();
let rate_limiter = rate_limiter.clone();
App::new()
.wrap_fn(add_cache_headers)
.wrap(middleware::Logger::default())
.data(pool.clone())
.data(server.clone())
@ -75,3 +92,23 @@ async fn main() -> io::Result<()> {
.run()
.await
}
fn add_cache_headers<S>(
req: ServiceRequest,
srv: &mut S,
) -> impl Future<Output = Result<ServiceResponse, Error>>
where
S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error>,
{
let fut = srv.call(req);
async move {
let mut res = fut.await?;
if let Some(content_type) = res.headers().get(CONTENT_TYPE) {
if CACHE_CONTROL_REGEX.is_match(content_type.to_str().unwrap()) {
let header_val = HeaderValue::from_static(&CACHE_CONTROL_VALUE);
res.headers_mut().insert(CACHE_CONTROL, header_val);
}
}
Ok(res)
}
}

View file

@ -92,7 +92,7 @@ impl RateLimited {
}
RateLimitType::Post => {
limiter.check_rate_limit_full(
self.type_.clone(),
self.type_,
&ip_addr,
rate_limit.post,
rate_limit.post_per_second,

View file

@ -25,11 +25,7 @@ async fn node_info(
Ok(site_view) => site_view,
Err(_) => return Err(format_err!("not_found")),
};
let protocols = if Settings::get().federation_enabled {
vec!["activitypub".to_string()]
} else {
vec![]
};
let protocols = vec![];
Ok(NodeInfo {
version: "2.0".to_string(),
software: NodeInfoSoftware {

View file

@ -7,12 +7,10 @@ pub struct Params {
}
pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation_enabled {
cfg.route(
".well-known/webfinger",
web::get().to(get_webfinger_response),
);
}
cfg.route(
".well-known/webfinger",
web::get().to(get_webfinger_response),
);
}
lazy_static! {

View file

@ -124,7 +124,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WSSession {
}
actix::fut::ready(())
})
.wait(ctx);
.spawn(ctx);
}
ws::Message::Binary(_bin) => info!("Unexpected binary"),
ws::Message::Close(_) => {

View file

@ -20,7 +20,6 @@ pub struct Settings {
pub front_end_dir: String,
pub rate_limit: RateLimitConfig,
pub email: Option<EmailConfig>,
pub federation_enabled: bool,
}
#[derive(Debug, Deserialize, Clone)]

View file

@ -1 +1 @@
pub const VERSION: &str = "v0.6.51";
pub const VERSION: &str = "v0.7.1";

View file

@ -940,7 +940,7 @@ impl Handler<Connect> for ChatServer {
id,
SessionInfo {
addr: msg.addr,
ip: msg.ip.to_owned(),
ip: msg.ip,
},
);

View file

@ -37,7 +37,7 @@
}
.md-div img {
max-height: 90vh;
max-height: 40vh;
max-width: 100%;
height: auto;
}
@ -128,10 +128,7 @@ blockquote {
.new-comments {
max-height: 50vh;
overflow: hidden;
}
.new-comments:hover {
overflow-x: hidden;
overflow-y: auto;
}

View file

@ -0,0 +1,30 @@
$white: #ffffff;
$orange: #faa077;
$cyan: #02bdc2;
$green: #d4e9d7;
$secondary: $green;
$body-color: $gray-700;
$link-color: theme-color("danger");;
$primary: $orange;
$red: #d8486a;
$border-radius: 1.5rem;
$border-radius-lg: 1.5rem;
$border-radius-sm: 1rem;
$font-family-sans-serif: Guardian-EgypTT,serif,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
$headings-color: $gray-700;
$input-btn-focus-color: rgba($component-active-bg, .75);
$form-feedback-valid-color: theme-color("info");
$navbar-light-color: $gray-600;
$black: #222222;
$navbar-dark-toggler-border-color: rgba($black, .1);
$navbar-light-active-color: $gray-900;
$card-color: $gray-700;
$card-cap-color: $gray-700;
$info: darken($green, 25%);;
$body-bg: #f2f0f0;
$success: darken($green, 25%);;
$danger: darken($primary, 25%);
$navbar-light-hover-color: $gray-900;
$card-bg: $gray-100;
$border-color: $gray-700;

1
ui/assets/css/themes/litely.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -18,6 +18,7 @@ import {
setupTribute,
wsJsonToRes,
emojiPicker,
pictrsDeleteToast,
} from '../utils';
import { WebSocketService, UserService } from '../services';
import autosize from 'autosize';
@ -60,7 +61,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
buttonTitle: !this.props.node
? capitalizeFirstLetter(i18n.t('post'))
: this.props.edit
? capitalizeFirstLetter(i18n.t('edit'))
? capitalizeFirstLetter(i18n.t('save'))
: capitalizeFirstLetter(i18n.t('reply')),
previewMode: false,
loading: false,
@ -137,7 +138,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
/>
{this.state.previewMode && (
<div
className="md-div"
className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(
this.state.commentForm.content
)}
@ -150,7 +151,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
<button
type="submit"
class="btn btn-sm btn-secondary mr-2"
disabled={this.props.disabled}
disabled={this.props.disabled || this.state.loading}
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
@ -162,8 +163,9 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
</button>
{this.state.commentForm.content && (
<button
className={`btn btn-sm mr-2 btn-secondary ${this.state
.previewMode && 'active'}`}
className={`btn btn-sm mr-2 btn-secondary ${
this.state.previewMode && 'active'
}`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
@ -243,18 +245,32 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
});
}
handleFinished() {
this.state.previewMode = false;
this.state.loading = false;
this.state.commentForm.content = '';
this.setState(this.state);
let form: any = document.getElementById(this.formId);
form.reset();
if (this.props.node) {
this.props.onReplyCancel();
handleFinished(data: CommentResponse) {
let isReply =
this.props.node !== undefined && data.comment.parent_id !== null;
let xor =
+!(data.comment.parent_id !== null) ^ +(this.props.node !== undefined);
if (
(data.comment.creator_id == UserService.Instance.user.id &&
// If its a reply, make sure parent child match
isReply &&
data.comment.parent_id == this.props.node.comment.id) ||
// Otherwise, check the XOR of the two
(!isReply && xor)
) {
this.state.previewMode = false;
this.state.loading = false;
this.state.commentForm.content = '';
this.setState(this.state);
let form: any = document.getElementById(this.formId);
form.reset();
if (this.props.node) {
this.props.onReplyCancel();
}
autosize.update(form);
this.setState(this.state);
}
autosize.update(document.querySelector('textarea'));
this.setState(this.state);
}
handleCommentSubmit(i: CommentForm, event: any) {
@ -304,9 +320,9 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
file = event;
}
const imageUploadUrl = `/pictshare/api/upload.php`;
const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
formData.append('file', file);
formData.append('images[]', file);
i.state.imageLoading = true;
i.setState(i.state);
@ -317,16 +333,31 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
})
.then(res => res.json())
.then(res => {
let url = `${window.location.origin}/pictshare/${res.url}`;
let imageMarkdown =
res.filetype == 'mp4' ? `[vid](${url}/raw)` : `![](${url})`;
let content = i.state.commentForm.content;
content = content ? `${content}\n${imageMarkdown}` : imageMarkdown;
i.state.commentForm.content = content;
i.state.imageLoading = false;
i.setState(i.state);
let textarea: any = document.getElementById(i.id);
autosize.update(textarea);
console.log('pictrs upload:');
console.log(res);
if (res.msg == 'ok') {
let hash = res.files[0].file;
let url = `${window.location.origin}/pictrs/image/${hash}`;
let deleteToken = res.files[0].delete_token;
let deleteUrl = `${window.location.origin}/pictrs/image/delete/${deleteToken}/${hash}`;
let imageMarkdown = `![](${url})`;
let content = i.state.commentForm.content;
content = content ? `${content}\n${imageMarkdown}` : imageMarkdown;
i.state.commentForm.content = content;
i.state.imageLoading = false;
i.setState(i.state);
let textarea: any = document.getElementById(i.id);
autosize.update(textarea);
pictrsDeleteToast(
i18n.t('click_to_delete_picture'),
i18n.t('picture_deleted'),
deleteUrl
);
} else {
i.state.imageLoading = false;
i.setState(i.state);
toast(JSON.stringify(res), 'danger');
}
})
.catch(error => {
i.state.imageLoading = false;
@ -342,14 +373,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
if (UserService.Instance.user) {
if (res.op == UserOperation.CreateComment) {
let data = res.data as CommentResponse;
if (data.comment.creator_id == UserService.Instance.user.id) {
this.handleFinished();
}
this.handleFinished(data);
} else if (res.op == UserOperation.EditComment) {
let data = res.data as CommentResponse;
if (data.comment.creator_id == UserService.Instance.user.id) {
this.handleFinished();
}
this.handleFinished(data);
}
}
}

View file

@ -132,7 +132,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
>
<div
id={`comment-${node.comment.id}`}
className={`details comment-node border-top border-light ${
className={`details comment-node border-top border-light py-2 ${
this.isCommentNew ? 'mark' : ''
}`}
style={
@ -142,11 +142,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
}
>
<div
class={`${!this.props.noIndent &&
class={`${
!this.props.noIndent &&
this.props.node.comment.parent_id &&
'ml-2'}`}
'ml-2'
}`}
>
<div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
<div class="d-flex flex-wrap align-items-center text-muted small">
<span class="mr-2">
<UserListing
user={{
@ -249,8 +251,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this.loadingIcon
) : (
<svg
class={`icon icon-inline ${node.comment.read &&
'text-success'}`}
class={`icon icon-inline ${
node.comment.read && 'text-success'
}`}
>
<use xlinkHref="#icon-check"></use>
</svg>
@ -291,24 +294,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)}
</button>
)}
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleSaveCommentClick)}
data-tippy-content={
node.comment.saved ? i18n.t('unsave') : i18n.t('save')
}
>
{this.state.saveLoading ? (
this.loadingIcon
) : (
<svg
class={`icon icon-inline ${node.comment.saved &&
'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
)}
</button>
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleReplyClick)}
@ -344,14 +329,39 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</button>
)}
{!this.props.showContext && this.linkBtn}
<button
class="btn btn-link btn-animate text-muted"
onClick={linkEvent(
this,
this.handleSaveCommentClick
)}
data-tippy-content={
node.comment.saved
? i18n.t('unsave')
: i18n.t('save')
}
>
{this.state.saveLoading ? (
this.loadingIcon
) : (
<svg
class={`icon icon-inline ${
node.comment.saved && 'text-warning'
}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
)}
</button>
<button
className="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
>
<svg
class={`icon icon-inline ${this.state
.viewSource && 'text-success'}`}
class={`icon icon-inline ${
this.state.viewSource && 'text-success'
}`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
@ -380,8 +390,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
}
>
<svg
class={`icon icon-inline ${node.comment
.deleted && 'text-danger'}`}
class={`icon icon-inline ${
node.comment.deleted && 'text-danger'
}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
@ -700,19 +711,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get linkBtn() {
let node = this.props.node;
return (
<button className="btn btn-link btn-animate">
<Link
class="text-muted"
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={
this.props.showContext ? i18n.t('show_context') : i18n.t('link')
}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-link"></use>
</svg>
</Link>
</button>
<Link
class="btn btn-link btn-animate text-muted"
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={this.props.showContext ? i18n.t('show_context') : i18n.t('link')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-link"></use>
</svg>
</Link>
);
}

View file

@ -207,7 +207,11 @@ export class CommunityForm extends Component<
)}
<div class="form-group row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
<button
type="submit"
class="btn btn-secondary mr-2"
disabled={this.state.loading}
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>

View file

@ -123,7 +123,10 @@ export class Inbox extends Component<any, InboxState> {
this.state.unreadOrAll == UnreadOrAll.Unread && (
<ul class="list-inline mb-1 text-muted small font-weight-bold">
<li className="list-inline-item">
<span class="pointer" onClick={this.markAllAsRead}>
<span
class="pointer"
onClick={linkEvent(this, this.markAllAsRead)}
>
{i18n.t('mark_all_as_read')}
</span>
</li>
@ -392,8 +395,14 @@ export class Inbox extends Component<any, InboxState> {
this.refetch();
}
markAllAsRead() {
markAllAsRead(i: Inbox) {
WebSocketService.Instance.markAllAsRead();
i.state.replies = [];
i.state.mentions = [];
i.state.messages = [];
i.sendUnreadCount();
window.scrollTo(0, 0);
i.setState(i.state);
}
parseMessage(msg: WebSocketJsonResponse) {
@ -447,12 +456,7 @@ export class Inbox extends Component<any, InboxState> {
this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.MarkAllAsRead) {
this.state.replies = [];
this.state.mentions = [];
this.state.messages = [];
this.sendUnreadCount();
window.scrollTo(0, 0);
this.setState(this.state);
// Moved to be instant
} else if (res.op == UserOperation.EditComment) {
let data = res.data as CommentResponse;
editCommentRes(data, this.state.replies);

View file

@ -111,6 +111,7 @@ export class Login extends Component<any, State> {
required
/>
<button
type="button"
disabled={!validEmail(this.state.loginForm.username_or_email)}
onClick={linkEvent(this, this.handlePasswordReset)}
className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
@ -187,6 +188,7 @@ export class Login extends Component<any, State> {
type="password"
id="register-password"
value={this.state.registerForm.password}
autoComplete="new-password"
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
class="form-control"
required
@ -206,6 +208,7 @@ export class Login extends Component<any, State> {
type="password"
id="register-verify-password"
value={this.state.registerForm.password_verify}
autoComplete="new-password"
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
class="form-control"
required

View file

@ -282,9 +282,11 @@ export class Main extends Component<any, MainState> {
</ul>
)}
<ul class="my-2 list-inline">
{/*
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_online', { count: this.state.siteRes.online })}
</li>
*/}
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_of_users', {
count: this.state.siteRes.site.number_of_users,

View file

@ -22,7 +22,7 @@ import {
} from '../interfaces';
import {
wsJsonToRes,
pictshareAvatarThumbnail,
pictrsAvatarThumbnail,
showAvatars,
fetchLimit,
isCommentType,
@ -218,7 +218,7 @@ export class Navbar extends Component<any, NavbarState> {
<span>
{UserService.Instance.user.avatar && showAvatars() && (
<img
src={pictshareAvatarThumbnail(
src={pictrsAvatarThumbnail(
UserService.Instance.user.avatar
)}
height="32"
@ -381,7 +381,7 @@ export class Navbar extends Component<any, NavbarState> {
requestNotificationPermission() {
if (UserService.Instance.user) {
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function () {
if (!Notification) {
toast(i18n.t('notifications_error'), 'danger');
return;

View file

@ -35,6 +35,7 @@ import {
setupTribute,
setupTippy,
emojiPicker,
pictrsDeleteToast,
} from '../utils';
import autosize from 'autosize';
import Tribute from 'tributejs/src/Tribute.js';
@ -194,8 +195,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<form>
<label
htmlFor="file-upload"
className={`${UserService.Instance.user &&
'pointer'} d-inline-block float-right text-muted font-weight-bold`}
className={`${
UserService.Instance.user && 'pointer'
} d-inline-block float-right text-muted font-weight-bold`}
data-tippy-content={i18n.t('upload_image')}
>
<svg class="icon icon-inline">
@ -282,14 +284,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
/>
{this.state.previewMode && (
<div
className="md-div"
className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(this.state.postForm.body)}
/>
)}
{this.state.postForm.body && (
<button
className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state
.previewMode && 'active'}`}
className={`mt-1 mr-2 btn btn-sm btn-secondary ${
this.state.previewMode && 'active'
}`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
@ -328,6 +331,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
value={this.state.postForm.community_id}
onInput={linkEvent(this, this.handlePostCommunityChange)}
>
<option>{i18n.t('select_a_community')}</option>
{this.state.communities.map(community => (
<option value={community.id}>{community.name}</option>
))}
@ -355,7 +359,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
)}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary mr-2">
<button
disabled={
!this.state.postForm.community_id || this.state.loading
}
type="submit"
class="btn btn-secondary mr-2"
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
@ -398,6 +408,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
handlePostSubmit(i: PostForm, event: any) {
event.preventDefault();
// Coerce empty url string to undefined
if (i.state.postForm.url && i.state.postForm.url === '') {
i.state.postForm.url = undefined;
}
if (i.props.post) {
WebSocketService.Instance.editPost(i.state.postForm);
} else {
@ -511,9 +527,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
file = event;
}
const imageUploadUrl = `/pictshare/api/upload.php`;
const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
formData.append('file', file);
formData.append('images[]', file);
i.state.imageLoading = true;
i.setState(i.state);
@ -524,13 +540,26 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
})
.then(res => res.json())
.then(res => {
let url = `${window.location.origin}/pictshare/${encodeURI(res.url)}`;
if (res.filetype == 'mp4') {
url += '/raw';
console.log('pictrs upload:');
console.log(res);
if (res.msg == 'ok') {
let hash = res.files[0].file;
let url = `${window.location.origin}/pictrs/image/${hash}`;
let deleteToken = res.files[0].delete_token;
let deleteUrl = `${window.location.origin}/pictrs/image/delete/${deleteToken}/${hash}`;
i.state.postForm.url = url;
i.state.imageLoading = false;
i.setState(i.state);
pictrsDeleteToast(
i18n.t('click_to_delete_picture'),
i18n.t('picture_deleted'),
deleteUrl
);
} else {
i.state.imageLoading = false;
i.setState(i.state);
toast(JSON.stringify(res), 'danger');
}
i.state.postForm.url = url;
i.state.imageLoading = false;
i.setState(i.state);
})
.catch(error => {
i.state.imageLoading = false;
@ -561,7 +590,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
).id;
this.state.postForm.community_id = foundCommunityId;
} else {
this.state.postForm.community_id = data.communities[0].id;
// By default, the null valued 'Select a Community'
}
this.setState(this.state);
@ -571,6 +600,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
let selector = new Selectr(selectId, { nativeDropdown: false });
selector.on('selectr.select', option => {
this.state.postForm.community_id = Number(option.value);
this.setState(this.state);
});
}
} else if (res.op == UserOperation.CreatePost) {

View file

@ -28,7 +28,7 @@ import {
isImage,
isVideo,
getUnixTime,
pictshareImage,
pictrsImage,
setupTippy,
previewLines,
} from '../utils';
@ -150,9 +150,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
let post = this.props.post;
return (
<img
className={`img-fluid thumbnail rounded ${(post.nsfw ||
post.community_nsfw) &&
'img-blur'}`}
className={`img-fluid thumbnail rounded ${
(post.nsfw || post.community_nsfw) && 'img-blur'
}`}
src={src}
/>
);
@ -161,13 +161,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
getImage(thumbnail: boolean = false) {
let post = this.props.post;
if (isImage(post.url)) {
if (post.url.includes('pictshare')) {
return pictshareImage(post.url, thumbnail);
if (post.url.includes('pictrs')) {
return pictrsImage(post.url, thumbnail);
} else if (post.thumbnail_url) {
return pictrsImage(post.thumbnail_url, thumbnail);
} else {
return post.url;
}
} else if (post.thumbnail_url) {
return pictshareImage(post.thumbnail_url, thumbnail);
return pictrsImage(post.thumbnail_url, thumbnail);
}
}
@ -542,8 +544,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
>
<svg
class={`icon icon-inline ${post.saved &&
'text-warning'}`}
class={`icon icon-inline ${
post.saved && 'text-warning'
}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
@ -586,8 +589,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
>
<svg
class={`icon icon-inline ${post.deleted &&
'text-danger'}`}
class={`icon icon-inline ${
post.deleted && 'text-danger'
}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
@ -618,8 +622,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
data-tippy-content={i18n.t('view_source')}
>
<svg
class={`icon icon-inline ${this.state
.viewSource && 'text-success'}`}
class={`icon icon-inline ${
this.state.viewSource && 'text-success'
}`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
@ -639,8 +644,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
>
<svg
class={`icon icon-inline ${post.locked &&
'text-danger'}`}
class={`icon icon-inline ${
post.locked && 'text-danger'
}`}
>
<use xlinkHref="#icon-lock"></use>
</svg>
@ -657,8 +663,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
}
>
<svg
class={`icon icon-inline ${post.stickied &&
'text-success'}`}
class={`icon icon-inline ${
post.stickied && 'text-success'
}`}
>
<use xlinkHref="#icon-pin"></use>
</svg>

View file

@ -40,7 +40,6 @@ import {
setupTippy,
} from '../utils';
import { PostListing } from './post-listing';
import { PostListings } from './post-listings';
import { Sidebar } from './sidebar';
import { CommentForm } from './comment-form';
import { CommentNodes } from './comment-nodes';
@ -183,14 +182,6 @@ export class Post extends Component<any, PostState> {
moderators={this.state.moderators}
admins={this.state.admins}
/>
{this.state.crossPosts.length > 0 && (
<>
<div class="my-1 text-muted small font-weight-bold">
{i18n.t('cross_posts')}
</div>
<PostListings showCommunity posts={this.state.crossPosts} />
</>
)}
<div className="mb-2" />
<CommentForm
postId={this.state.post.id}
@ -213,8 +204,9 @@ export class Post extends Component<any, PostState> {
return (
<div class="btn-group btn-group-toggle mb-2">
<label
className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.Hot && 'active'}`}
className={`btn btn-sm btn-secondary pointer ${
this.state.commentSort === CommentSortType.Hot && 'active'
}`}
>
{i18n.t('hot')}
<input
@ -225,8 +217,9 @@ export class Post extends Component<any, PostState> {
/>
</label>
<label
className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.Top && 'active'}`}
className={`btn btn-sm btn-secondary pointer ${
this.state.commentSort === CommentSortType.Top && 'active'
}`}
>
{i18n.t('top')}
<input
@ -237,8 +230,9 @@ export class Post extends Component<any, PostState> {
/>
</label>
<label
className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.New && 'active'}`}
className={`btn btn-sm btn-secondary pointer ${
this.state.commentSort === CommentSortType.New && 'active'
}`}
>
{i18n.t('new')}
<input
@ -249,8 +243,9 @@ export class Post extends Component<any, PostState> {
/>
</label>
<label
className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.Old && 'active'}`}
className={`btn btn-sm btn-secondary pointer ${
this.state.commentSort === CommentSortType.Old && 'active'
}`}
>
{i18n.t('old')}
<input
@ -462,6 +457,9 @@ export class Post extends Component<any, PostState> {
this.state.crossPosts = data.posts.filter(
p => p.id != Number(this.props.match.params.id)
);
if (this.state.crossPosts.length) {
this.state.post.duplicates = this.state.crossPosts;
}
this.setState(this.state);
} else if (res.op == UserOperation.TransferSite) {
let data = res.data as GetSiteResponse;

View file

@ -154,14 +154,70 @@ export class PrivateMessageForm extends Component<
/>
{this.state.previewMode && (
<div
className="md-div"
className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(
this.state.privateMessageForm.content
)}
/>
)}
</div>
</div>
<ul class="float-right list-inline mb-1 text-muted font-weight-bold">
{this.state.showDisclaimer && (
<div class="form-group row">
<div class="offset-sm-2 col-sm-10">
<div class="alert alert-danger" role="alert">
<T i18nKey="private_message_disclaimer">
#
<a
class="alert-link"
target="_blank"
href="https://about.riot.im/"
>
#
</a>
</T>
</div>
</div>
</div>
)}
<div class="form-group row">
<div class="offset-sm-2 col-sm-10">
<button
type="submit"
class="btn btn-secondary mr-2"
disabled={this.state.loading}
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
</svg>
) : this.props.privateMessage ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
capitalizeFirstLetter(i18n.t('send_message'))
)}
</button>
{this.state.privateMessageForm.content && (
<button
className={`btn btn-secondary mr-2 ${
this.state.previewMode && 'active'
}`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
</button>
)}
{this.props.privateMessage && (
<button
type="button"
class="btn btn-secondary"
onClick={linkEvent(this, this.handleCancel)}
>
{i18n.t('cancel')}
</button>
)}
<ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
<li class="list-inline-item">
<span
onClick={linkEvent(this, this.handleShowDisclaimer)}
@ -188,58 +244,6 @@ export class PrivateMessageForm extends Component<
</ul>
</div>
</div>
{this.state.showDisclaimer && (
<div class="form-group row">
<div class="col-sm-10">
<div class="alert alert-danger" role="alert">
<T i18nKey="private_message_disclaimer">
#
<a
class="alert-link"
target="_blank"
href="https://about.riot.im/"
>
#
</a>
</T>
</div>
</div>
</div>
)}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary mr-2">
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
</svg>
) : this.props.privateMessage ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
capitalizeFirstLetter(i18n.t('send_message'))
)}
</button>
{this.state.privateMessageForm.content && (
<button
className={`btn btn-secondary mr-2 ${this.state.previewMode &&
'active'}`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
</button>
)}
{this.props.privateMessage && (
<button
type="button"
class="btn btn-secondary"
onClick={linkEvent(this, this.handleCancel)}
>
{i18n.t('cancel')}
</button>
)}
</div>
</div>
</form>
</div>
);

View file

@ -5,12 +5,7 @@ import {
EditPrivateMessageForm,
} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import {
mdToHtml,
pictshareAvatarThumbnail,
showAvatars,
toast,
} from '../utils';
import { mdToHtml, pictrsAvatarThumbnail, showAvatars, toast } from '../utils';
import { MomentTime } from './moment-time';
import { PrivateMessageForm } from './private-message-form';
import { i18n } from '../i18next';
@ -78,7 +73,7 @@ export class PrivateMessage extends Component<
<img
height="32"
width="32"
src={pictshareAvatarThumbnail(
src={pictrsAvatarThumbnail(
this.mine
? message.recipient_avatar
: message.creator_avatar
@ -144,8 +139,9 @@ export class PrivateMessage extends Component<
}
>
<svg
class={`icon icon-inline ${message.read &&
'text-success'}`}
class={`icon icon-inline ${
message.read && 'text-success'
}`}
>
<use xlinkHref="#icon-check"></use>
</svg>
@ -188,8 +184,9 @@ export class PrivateMessage extends Component<
}
>
<svg
class={`icon icon-inline ${message.deleted &&
'text-danger'}`}
class={`icon icon-inline ${
message.deleted && 'text-danger'
}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
@ -204,8 +201,9 @@ export class PrivateMessage extends Component<
data-tippy-content={i18n.t('view_source')}
>
<svg
class={`icon icon-inline ${this.state.viewSource &&
'text-success'}`}
class={`icon icon-inline ${
this.state.viewSource && 'text-success'
}`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>

View file

@ -22,7 +22,7 @@ import {
fetchLimit,
routeSearchTypeToEnum,
routeSortTypeToEnum,
pictshareAvatarThumbnail,
pictrsAvatarThumbnail,
showAvatars,
toast,
createCommentLikeRes,

View file

@ -11,7 +11,7 @@ import { WebSocketService, UserService } from '../services';
import {
mdToHtml,
getUnixTime,
pictshareAvatarThumbnail,
pictrsAvatarThumbnail,
showAvatars,
} from '../utils';
import { CommunityForm } from './community-form';
@ -111,8 +111,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
}
>
<svg
class={`icon icon-inline ${community.deleted &&
'text-danger'}`}
class={`icon icon-inline ${
community.deleted && 'text-danger'
}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
@ -169,9 +170,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
</form>
)}
<ul class="my-1 list-inline">
{/*
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_online', { count: this.props.online })}
</li>
*/}
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_of_subscribers', {
count: community.number_of_subscribers,
@ -215,9 +218,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
))}
</ul>
<Link
class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
community.removed) &&
'no-click'}`}
class={`btn btn-sm btn-secondary btn-block mb-3 ${
(community.deleted || community.removed) && 'no-click'
}`}
to={`/create_post?community=${community.name}`}
>
{i18n.t('create_a_post')}

View file

@ -78,7 +78,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
<h5>{`${
this.props.site
? capitalizeFirstLetter(i18n.t('edit'))
? capitalizeFirstLetter(i18n.t('save'))
: capitalizeFirstLetter(i18n.t('name'))
} ${i18n.t('your_site')}`}</h5>
<div class="form-group row">
@ -175,7 +175,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
</div>
<div class="form-group row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
<button
type="submit"
class="btn btn-secondary mr-2"
disabled={this.state.loading}
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>

View file

@ -4,14 +4,25 @@ import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
import { repoUrl } from '../utils';
interface SilverUser {
name: string;
link: string;
}
let general = [
'alexx henry',
'Nathan J. Goode',
'Ernest Wiśniewski',
'HN',
'Forrest Weghorst',
'Andre Vallestero',
'NotTooHighToHack',
];
let highlighted = ['Oskenso Kashi', 'Alex Benishek'];
// let silver = [];
let silver: Array<SilverUser> = [
{
name: 'Redjoker',
link: 'https://iww.org',
},
];
// let gold = [];
// let latinum = [];
@ -70,6 +81,18 @@ export class Sponsors extends Component<any, any> {
return (
<div class="container">
<h5>{i18n.t('sponsors')}</h5>
<p>{i18n.t('silver_sponsors')}</p>
<div class="row card-columns">
{silver.map(s => (
<div class="card col-12 col-md-2">
<div>
<a href={s.link} target="_blank">
💎 {s.name}
</a>
</div>
</div>
))}
</div>
<p>{i18n.t('general_sponsors')}</p>
<div class="row card-columns">
{highlighted.map(s => (

View file

@ -1,7 +1,7 @@
import { Component } from 'inferno';
import { Link } from 'inferno-router';
import { UserView } from '../interfaces';
import { pictshareAvatarThumbnail, showAvatars } from '../utils';
import { pictrsAvatarThumbnail, showAvatars } from '../utils';
interface UserOther {
name: string;
@ -25,7 +25,7 @@ export class UserListing extends Component<UserListingProps, any> {
<img
height="32"
width="32"
src={pictshareAvatarThumbnail(user.avatar)}
src={pictrsAvatarThumbnail(user.avatar)}
class="rounded-circle mr-2"
/>
)}

View file

@ -81,7 +81,6 @@ export class User extends Component<any, UserState> {
user: {
id: null,
name: null,
fedi_name: null,
published: null,
number_of_posts: null,
post_score: null,
@ -455,8 +454,9 @@ export class User extends Component<any, UserState> {
) : (
<>
<a
className={`btn btn-block btn-secondary mt-3 ${!this.state
.user.matrix_user_id && 'disabled'}`}
className={`btn btn-block btn-secondary mt-3 ${
!this.state.user.matrix_user_id && 'disabled'
}`}
target="_blank"
href={`https://matrix.to/#/${this.state.user.matrix_user_id}`}
>
@ -609,6 +609,7 @@ export class User extends Component<any, UserState> {
id="user-password"
class="form-control"
value={this.state.userSettingsForm.new_password}
autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordChange
@ -629,6 +630,7 @@ export class User extends Component<any, UserState> {
id="user-verify-password"
class="form-control"
value={this.state.userSettingsForm.new_password_verify}
autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordVerifyChange
@ -649,6 +651,7 @@ export class User extends Component<any, UserState> {
id="user-old-password"
class="form-control"
value={this.state.userSettingsForm.old_password}
autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsOldPasswordChange
@ -745,6 +748,7 @@ export class User extends Component<any, UserState> {
<input
type="password"
value={this.state.deleteAccountForm.password}
autoComplete="new-password"
onInput={linkEvent(
this,
this.handleDeleteAccountPasswordChange
@ -918,7 +922,7 @@ export class User extends Component<any, UserState> {
handleUserSettingsThemeChange(i: User, event: any) {
i.state.userSettingsForm.theme = event.target.value;
setTheme(event.target.value);
setTheme(event.target.value, true);
i.setState(i.state);
}
@ -984,9 +988,9 @@ export class User extends Component<any, UserState> {
handleImageUpload(i: User, event: any) {
event.preventDefault();
let file = event.target.files[0];
const imageUploadUrl = `/pictshare/api/upload.php`;
const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
formData.append('file', file);
formData.append('images[]', file);
i.state.avatarLoading = true;
i.setState(i.state);
@ -997,14 +1001,19 @@ export class User extends Component<any, UserState> {
})
.then(res => res.json())
.then(res => {
let url = `${window.location.origin}/pictshare/${res.url}`;
if (res.filetype == 'mp4') {
url += '/raw';
console.log('pictrs upload:');
console.log(res);
if (res.msg == 'ok') {
let hash = res.files[0].file;
let url = `${window.location.origin}/pictrs/image/${hash}`;
i.state.userSettingsForm.avatar = url;
i.state.avatarLoading = false;
i.setState(i.state);
} else {
i.state.avatarLoading = false;
i.setState(i.state);
toast(JSON.stringify(res), 'danger');
}
i.state.userSettingsForm.avatar = url;
console.log(url);
i.state.avatarLoading = false;
i.setState(i.state);
})
.catch(error => {
i.state.avatarLoading = false;

18
ui/src/i18next.ts vendored
View file

@ -1,6 +1,8 @@
import i18next from 'i18next';
import { getLanguage } from './utils';
import { en } from './translations/en';
import { el } from './translations/el';
import { eu } from './translations/eu';
import { eo } from './translations/eo';
import { es } from './translations/es';
import { de } from './translations/de';
@ -13,14 +15,25 @@ import { it } from './translations/it';
import { fi } from './translations/fi';
import { ca } from './translations/ca';
import { fa } from './translations/fa';
import { hi } from './translations/hi';
import { pl } from './translations/pl';
import { pt_BR } from './translations/pt_BR';
import { ja } from './translations/ja';
import { ka } from './translations/ka';
import { gl } from './translations/gl';
import { tr } from './translations/tr';
import { hu } from './translations/hu';
import { uk } from './translations/uk';
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
const resources = {
en,
el,
eu,
eo,
es,
ka,
hi,
de,
zh,
fr,
@ -31,8 +44,13 @@ const resources = {
fi,
ca,
fa,
pl,
pt_BR,
ja,
gl,
tr,
hu,
uk,
};
function format(value: any, format: any, lng: any): any {

3
ui/src/index.html vendored
View file

@ -15,7 +15,8 @@
<link rel="stylesheet" type="text/css" href="/static/assets/css/toastify.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/selectr.min.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/tippy.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/darkly.min.css" id="darkly" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/litely.min.css" id="default-light" media="(prefers-color-scheme: light)" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/darkly.min.css" id="default-dark" media="(prefers-color-scheme: no-preference), (prefers-color-scheme: dark)" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/main.css" />
<!-- Scripts -->

View file

@ -41,9 +41,7 @@ export class UserService {
private setUser(jwt: string) {
this.user = jwt_decode(jwt);
if (this.user.theme != 'darkly') {
setTheme(this.user.theme);
}
setTheme(this.user.theme, true);
this.sub.next({ user: this.user });
console.log(this.user);
}

170
ui/src/utils.ts vendored
View file

@ -1,4 +1,6 @@
import 'moment/locale/es';
import 'moment/locale/el';
import 'moment/locale/eu';
import 'moment/locale/eo';
import 'moment/locale/de';
import 'moment/locale/zh-cn';
@ -10,9 +12,15 @@ import 'moment/locale/it';
import 'moment/locale/fi';
import 'moment/locale/ca';
import 'moment/locale/fa';
import 'moment/locale/pl';
import 'moment/locale/pt-br';
import 'moment/locale/ja';
import 'moment/locale/ka';
import 'moment/locale/hi';
import 'moment/locale/gl';
import 'moment/locale/tr';
import 'moment/locale/hu';
import 'moment/locale/uk';
import {
UserOperation,
@ -58,17 +66,25 @@ export const mentionDropdownFetchLimit = 10;
export const languages = [
{ code: 'ca', name: 'Català' },
{ code: 'en', name: 'English' },
{ code: 'el', name: 'Ελληνικά' },
{ code: 'eu', name: 'Euskara' },
{ code: 'eo', name: 'Esperanto' },
{ code: 'es', name: 'Español' },
{ code: 'de', name: 'Deutsch' },
{ code: 'gl', name: 'Galego' },
{ code: 'hu', name: 'Magyar Nyelv' },
{ code: 'ka', name: 'ქართული ენა' },
{ code: 'hi', name: 'मानक हिन्दी' },
{ code: 'fa', name: 'فارسی' },
{ code: 'ja', name: '日本語' },
{ code: 'pl', name: 'Polski' },
{ code: 'pt_BR', name: 'Português Brasileiro' },
{ code: 'zh', name: '中文' },
{ code: 'fi', name: 'Suomi' },
{ code: 'fr', name: 'Français' },
{ code: 'sv', name: 'Svenska' },
{ code: 'tr', name: 'Türkçe' },
{ code: 'uk', name: 'українська мова' },
{ code: 'ru', name: 'Русский' },
{ code: 'nl', name: 'Nederlands' },
{ code: 'it', name: 'Italiano' },
@ -87,6 +103,7 @@ export const themes = [
'vaporwave',
'vaporwave-dark',
'i386',
'litely',
];
export const emojiPicker = new EmojiButton({
@ -97,11 +114,26 @@ export const emojiPicker = new EmojiButton({
// TODO i18n
});
export function randomStr() {
return Math.random()
.toString(36)
.replace(/[^a-z]+/g, '')
.substr(2, 10);
const DEFAULT_ALPHABET =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
function getRandomCharFromAlphabet(alphabet: string): string {
return alphabet.charAt(Math.floor(Math.random() * alphabet.length));
}
export function randomStr(
idDesiredLength: number = 20,
alphabet = DEFAULT_ALPHABET
): string {
/**
* Create n-long array and map it to random chars from given alphabet.
* Then join individual chars as string
*/
return Array.from({ length: idDesiredLength })
.map(() => {
return getRandomCharFromAlphabet(alphabet);
})
.join('');
}
export function wsJsonToRes(msg: WebSocketJsonResponse): WebSocketResponse {
@ -118,11 +150,11 @@ export const md = new markdown_it({
typographer: true,
})
.use(markdown_it_container, 'spoiler', {
validate: function(params: any) {
validate: function (params: any) {
return params.trim().match(/^spoiler\s+(.*)$/);
},
render: function(tokens: any, idx: any) {
render: function (tokens: any, idx: any) {
var m = tokens[idx].info.trim().match(/^spoiler\s+(.*)$/);
if (tokens[idx].nesting === 1) {
@ -138,7 +170,7 @@ export const md = new markdown_it({
defs: objectFlip(emojiShortName),
});
md.renderer.rules.emoji = function(token, idx) {
md.renderer.rules.emoji = function (token, idx) {
return twemoji.parse(token[idx].content);
};
@ -208,7 +240,7 @@ export function isMod(modIds: Array<number>, creator_id: number): boolean {
}
const imageRegex = new RegExp(
/(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg))/
/(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/
);
const videoRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:mp4))`);
@ -284,7 +316,7 @@ export function debounce(
let timeout: any;
// Calling debounce returns a new anonymous function
return function() {
return function () {
// reference the context and args for the setTimeout function
var context = this,
args = arguments;
@ -300,7 +332,7 @@ export function debounce(
clearTimeout(timeout);
// Set the new timeout
timeout = setTimeout(function() {
timeout = setTimeout(function () {
// Inside the timeout function, clear the timeout variable
// which will let the next execution run when in 'immediate' mode
timeout = null;
@ -360,19 +392,35 @@ export function getMomentLanguage(): string {
lang = 'ca';
} else if (lang.startsWith('fa')) {
lang = 'fa';
} else if (lang.startsWith('pl')) {
lang = 'pl';
} else if (lang.startsWith('pt')) {
lang = 'pt-br';
} else if (lang.startsWith('ja')) {
lang = 'ja';
} else if (lang.startsWith('ka')) {
lang = 'ka';
} else if (lang.startsWith('hi')) {
lang = 'hi';
} else if (lang.startsWith('el')) {
lang = 'el';
} else if (lang.startsWith('eu')) {
lang = 'eu';
} else if (lang.startsWith('gl')) {
lang = 'gl';
} else if (lang.startsWith('tr')) {
lang = 'tr';
} else if (lang.startsWith('hu')) {
lang = 'hu';
} else if (lang.startsWith('uk')) {
lang = 'uk';
} else {
lang = 'en';
}
return lang;
}
export function setTheme(theme: string = 'darkly') {
export function setTheme(theme: string = 'darkly', loggedIn: boolean = false) {
// unload all the other themes
for (var i = 0; i < themes.length; i++) {
let styleSheet = document.getElementById(themes[i]);
@ -381,18 +429,36 @@ export function setTheme(theme: string = 'darkly') {
}
}
// Load the theme dynamically
if (!document.getElementById(theme)) {
// if the user is not logged in, we load the default themes and let the browser decide
if (!loggedIn) {
document.getElementById('default-light').removeAttribute('disabled');
document.getElementById('default-dark').removeAttribute('disabled');
} else {
document
.getElementById('default-light')
.setAttribute('disabled', 'disabled');
document
.getElementById('default-dark')
.setAttribute('disabled', 'disabled');
// Load the theme dynamically
let cssLoc = `/static/assets/css/themes/${theme}.min.css`;
loadCss(theme, cssLoc);
document.getElementById(theme).removeAttribute('disabled');
}
}
export function loadCss(id: string, loc: string) {
if (!document.getElementById(id)) {
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = theme;
link.id = id;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = `/static/assets/css/themes/${theme}.min.css`;
link.href = loc;
link.media = 'all';
head.appendChild(link);
}
document.getElementById(theme).removeAttribute('disabled');
}
export function objectFlip(obj: any) {
@ -403,10 +469,12 @@ export function objectFlip(obj: any) {
return ret;
}
export function pictshareAvatarThumbnail(src: string): string {
// sample url: http://localhost:8535/pictshare/gs7xuu.jpg
let split = src.split('pictshare');
let out = `${split[0]}pictshare/96${split[1]}`;
export function pictrsAvatarThumbnail(src: string): string {
// sample url: http://localhost:8535/pictrs/image/thumbnail256/gs7xuu.jpg
let split = src.split('/pictrs/image');
let out = `${split[0]}/pictrs/image/${
canUseWebP() ? 'webp/' : ''
}thumbnail96${split[1]}`;
return out;
}
@ -418,20 +486,19 @@ export function showAvatars(): boolean {
}
// Converts to image thumbnail
export function pictshareImage(
hash: string,
thumbnail: boolean = false
): string {
let root = `/pictshare`;
export function pictrsImage(hash: string, thumbnail: boolean = false): string {
let root = `/pictrs/image`;
// Necessary for other servers / domains
if (hash.includes('pictshare')) {
let split = hash.split('/pictshare/');
root = `${split[0]}/pictshare`;
if (hash.includes('pictrs')) {
let split = hash.split('/pictrs/image/');
root = `${split[0]}/pictrs/image`;
hash = split[1];
}
let out = `${root}/${thumbnail ? '192/' : ''}${hash}`;
let out = `${root}/${canUseWebP() ? 'webp/' : ''}${
thumbnail ? 'thumbnail256/' : ''
}${hash}`;
return out;
}
@ -449,6 +516,29 @@ export function toast(text: string, background: string = 'success') {
}).showToast();
}
export function pictrsDeleteToast(
clickToDeleteText: string,
deletePictureText: string,
deleteUrl: string
) {
let backgroundColor = `var(--light)`;
let toast = Toastify({
text: clickToDeleteText,
backgroundColor: backgroundColor,
gravity: 'top',
position: 'right',
duration: 10000,
onClick: () => {
if (toast) {
window.location.replace(deleteUrl);
alert(deletePictureText);
toast.hideToast();
}
},
close: true,
}).showToast();
}
export function messageToastify(
creator: string,
avatar: string,
@ -462,10 +552,11 @@ export function messageToastify(
text: `${body}<br />${creator}`,
avatar: avatar,
backgroundColor: backgroundColor,
className: 'text-dark',
close: true,
gravity: 'top',
position: 'right',
duration: 0,
duration: 5000,
onClick: () => {
if (toast) {
toast.hideToast();
@ -841,3 +932,20 @@ export function previewLines(text: string, lines: number = 3): string {
.slice(0, lines * 2)
.join('\n');
}
function canUseWebP() {
// TODO pictshare might have a webp conversion bug, try disabling this
return false;
// var elem = document.createElement('canvas');
// if (!!(elem.getContext && elem.getContext('2d'))) {
// var testString = !(window.mozInnerScreenX == null) ? 'png' : 'webp';
// // was able or not to get WebP representation
// return (
// elem.toDataURL('image/webp').startsWith('data:image/' + testString)
// );
// }
// // very old browser like IE 8, canvas not supported
// return false;
}

2
ui/src/version.ts vendored
View file

@ -1 +1 @@
export const version: string = 'v0.6.51';
export const version: string = 'v0.7.1';

View file

@ -8,7 +8,12 @@
"create_post": "إنشاء منشور",
"posts": "منشورات",
"comments": "التعليقات",
"number_of_posts": "{{count}} منشورات",
"number_of_posts_0": "لا توجد منشورات",
"number_of_posts_1": "منشور واحد",
"number_of_posts_2": "منشورَيْن",
"number_of_posts_3": "{{count}} منشورات",
"number_of_posts_4": "{{count}} منشورات",
"number_of_posts_5": "{{count}} منشورات",
"related_posts": "يمكن لهذه المنشورات أن تكون ذات صلة",
"communities": "المجتمعات",
"users": "المستخدِمون",
@ -50,9 +55,24 @@
"ban_from_site": "طرده مِن الموقع",
"banned": "مطرود",
"save": "حفظ",
"number_of_users": "{{count}} مستخدِمين",
"number_of_points": "{{count}} نقاط",
"number_online": "{{count}} مستخدمين متّصلين",
"number_of_users_0": "بلا مستخدمين",
"number_of_users_1": "مستخدم واحد",
"number_of_users_2": "مستخدمَيْن",
"number_of_users_3": "{{count}} مستخدمين",
"number_of_users_4": "{{count}} مستخدمين",
"number_of_users_5": "{{count}} مستخدمين",
"number_of_points_0": "بلا نقاط",
"number_of_points_1": "نقطة واحدة",
"number_of_points_2": "نقطتَيْن",
"number_of_points_3": "{{count}} نقاط",
"number_of_points_4": "{{count}} نقاط",
"number_of_points_5": "{{count}} نقاط",
"number_online_0": "لا مستخدمين متّصلين",
"number_online_1": "مستخدم واحد متصل",
"number_online_2": "مستخدمَين متصلَيْن",
"number_online_3": "{{count}} مستخدمون متصلون",
"number_online_4": "{{count}} مستخدمون متصلون",
"number_online_5": "{{count}} مستخدمون متصلون",
"name": "الإسم",
"title": "العنوان",
"category": "الفئة",
@ -114,7 +134,12 @@
"delete_account": "حذف الحساب",
"create": "إنشاء",
"email_or_username": "عنوان البريد أو اسم المستخدم",
"number_of_subscribers": "{{count}} مُتابِعين",
"number_of_subscribers_0": "بلا مُتابِعين",
"number_of_subscribers_1": "متابِع واحد",
"number_of_subscribers_2": "متابِعَيْن",
"number_of_subscribers_3": "{{count}} متابِعون",
"number_of_subscribers_4": "{{count}} متابِعون",
"number_of_subscribers_5": "{{count}} متابِعون",
"unsubscribe": "إلغاء الإشتراك",
"week": "أسبوع",
"reply_sent": "تم إرسال الرد",
@ -127,7 +152,12 @@
"are_you_sure": "هل أنت متأكّد؟",
"logged_in": "إنّك متّصل.",
"user_already_exists": "هذا المستخدِم موجود بالفعل.",
"number_of_communities": "{{count}} مجتمعات",
"number_of_communities_0": "لا توجد مجتمعات",
"number_of_communities_1": "مجتمع واحد",
"number_of_communities_2": "مجتمعَيْن",
"number_of_communities_3": "{{count}} مجتمعات",
"number_of_communities_4": "{{count}} مجتمعات",
"number_of_communities_5": "{{count}} مجتمعات",
"subscribed": "مُتابِعون",
"url": "الرابط",
"nsfw": "محتوى حساس",
@ -145,7 +175,12 @@
"support_on_patreon": "ساندنا على Patreon",
"support_on_liberapay": "ساندنا عبر Liberapay",
"crypto": "العملات الرقمية",
"number_of_comments": "{{count}} تعليقات",
"number_of_comments_0": "لا توجد تعليقات",
"number_of_comments_1": "تعليق واحد",
"number_of_comments_2": "تعليقَيْن",
"number_of_comments_3": "{{count}} تعليقات",
"number_of_comments_4": "{{count}} تعليقات",
"number_of_comments_5": "{{count}} تعليقات",
"cross_posts": "لقد تم نشر هذا الرابط كذلك على:",
"cross_post": "منشور نُشِر تبادليا",
"cross_posted_to": "نشر تبادلي إلى: ",
@ -176,5 +211,13 @@
"lemmy_instance_setup": "تنصيب مثيل خادم Lemmy",
"show_nsfw": "إظهار المحتوى الحساس",
"sponsors": "الرعاة",
"sponsors_of_lemmy": "رعاة مشروع Lemmy"
"sponsors_of_lemmy": "رعاة مشروع Lemmy",
"inbox_for": "صندوق الواردات لـ <1>{{user}}</1>",
"show_context": "اظهر السياق",
"admin_settings": "الإعدادات الإدارية",
"site_config": "إعدادات الموقع",
"banned_users": "المستخدمون المحظورون",
"reset_password_mail_sent": "لقد أرسِلت إليك رسالة إلكترونية لتصفير كلمتك السرية.",
"upvote": "صوّت إيجابيا",
"downvote": "صوّت سلبيا"
}

View file

@ -1,5 +1,5 @@
{
"post": "post",
"post": "Beitrag",
"remove_post": "Beitrag löschen",
"no_posts": "Keine Beiträge.",
"create_a_post": "Einen Beitrag anlegen",
@ -14,16 +14,16 @@
"number_of_comments": "{{count}} Kommentar",
"number_of_comments_plural": "{{count}} Kommentare",
"remove_comment": "Kommentar löschen",
"communities": "Communities",
"communities": "Communitys",
"users": "Benutzer",
"create_a_community": "Eine Gemeinschaft anlegen",
"create_community": "Gemeinschaft anlegen",
"remove_community": "Gemeinschaft entfernen",
"subscribed_to_communities": "Abonnierte <1>communities</1>",
"trending_communities": "Trending <1>communities</1>",
"list_of_communities": "Liste von communities",
"create_a_community": "Eine Community anlegen",
"create_community": "Community erstellen",
"remove_community": "Community entfernen",
"subscribed_to_communities": "Abonnierte <1>Communitys</1>",
"trending_communities": "Populäre <1>Communitys</1>",
"list_of_communities": "Liste von Communitys",
"number_of_communities": "{{count}} Community",
"number_of_communities_plural": "{{count}} Communities",
"number_of_communities_plural": "{{count}} Communitys",
"community_reqs": "Kleinbuchstaben, Großbuchstaben und keine Leerzeichen.",
"edit": "editieren",
"reply": "antworten",
@ -44,7 +44,7 @@
"settings": "Einstellungen",
"remove_as_mod": "Als Moderator entfernen",
"appoint_as_mod": "Zum Moderator ernennen",
"modlog": "Modlog",
"modlog": "Moderations-Log",
"admin": "Administrator",
"admins": "Administratoren",
"remove_as_admin": "Als Administrator entfernen",
@ -73,27 +73,27 @@
"username": "Benutzername",
"email_or_username": "E-mail oder Username",
"number_of_users": "{{count}} Benutzer",
"number_of_users_plural": "{{count}} Benutzer",
"number_of_users_plural": "{{count}} Benutzer*innen",
"number_of_subscribers": "{{count}} Abonnent",
"number_of_subscribers_plural": "{{count}} Abonnenten",
"number_of_subscribers_plural": "{{count}} Abonnent*innen",
"number_of_points": "{{count}} Punkt",
"number_of_points_plural": "{{count}} Punkte",
"number_online": "{{count}} Benutzer online",
"number_online_plural": "{{count}} Benutzer online",
"number_online_plural": "{{count}} Benutzer*innen online",
"name": "Name",
"title": "Titel",
"category": "Kategorie",
"subscribers": "Abonnenten",
"subscribers": "Abonnent*innen",
"both": "Beide",
"saved": "Gespeichert",
"unsubscribe": "Abbestellen",
"unsubscribe": "Deabonnieren",
"subscribe": "Abonnieren",
"subscribed": "Abonniert",
"prev": "Zurück",
"next": "Weiter",
"sidebar": "Seitenleiste",
"sort_type": "Sortieren nach",
"hot": "Hot",
"hot": "Heiß",
"new": "Neu",
"top_day": "Top täglich",
"week": "Woche",
@ -115,14 +115,14 @@
"view": "Ansicht",
"logout": "Ausloggen",
"login_sign_up": "Einloggen / Registrieren",
"notifications_error": "Desktop-Benachrichtigungen sind in deinem browser nicht verfügbar. Versuche Firefox oder Chrome.",
"notifications_error": "Desktop-Benachrichtigungen sind in Ihrem Browser nicht verfügbar. Versuchen Sie es mit Firefox oder Chrome.",
"unread_messages": "Ungelesene Nachrichten",
"password": "Passwort",
"verify_password": "Passwort überprüfen",
"forgot_password": "Passwort vergessen",
"reset_password_mail_sent": "Eine E-Mail wurde geschickt, um dein Passwort zurückzusetzen.",
"password_change": "Passwort geändert",
"new_password": "neues Passwort",
"reset_password_mail_sent": "Eine E-Mail wurde geschickt, um Ihr Passwort zurückzusetzen.",
"password_change": "Passwort ändern",
"new_password": "Neues Passwort",
"no_email_setup": "Dieser Server hat E-Mails nicht korrekt eingerichtet.",
"login": "Einloggen",
"sign_up": "Registrieren",
@ -134,15 +134,15 @@
"url": "URL",
"body": "Text",
"copy_suggested_title": "Vorgeschlagenen Titel übernehmen: {{title}}",
"community": "Gemeinschaft",
"community": "Community",
"expand_here": "hier erweitern",
"subscribe_to_communities": "Abonniere ein paar <1>communities</1>.",
"subscribe_to_communities": "Abonniere einige <1>Communitys</1>.",
"chat": "Chat",
"recent_comments": "Neueste Kommentare",
"no_results": "Keine Ergebnisse.",
"setup": "Einrichten",
"lemmy_instance_setup": "Lemmy Instanz Einrichten",
"setup_admin": "Seiten Administrator konfigurieren",
"lemmy_instance_setup": "Lemmy-Instanz einrichten",
"setup_admin": "Seiten-Administrator konfigurieren",
"your_site": "deine Seite",
"modified": "verändert",
"nsfw": "NSFW",
@ -161,8 +161,8 @@
"code": "Code",
"joined": "beigetreten",
"by": "von",
"to": "bis",
"transfer_community": "Gemeinschaft übertragen",
"to": "in",
"transfer_community": "Community übertragen",
"transfer_site": "Transferseite",
"are_you_sure": "Bist du sicher?",
"yes": "Ja",
@ -170,7 +170,7 @@
"powered_by": "Bereitgestellt durch",
"landing_0": "Lemmy ist ein <1>Link-Aggregator</1> / Reddit Alternative im <2>Fediverse</2>.<3></3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB</4>). Federation in das ActivityPub Netzwerk ist geplant. <5></5>Dies ist eine <6>sehr frühe Beta Version</6>, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7></7>Schlage neue Features vor oder melde Bugs <8>hier.</8><9></9>Gebaut mit <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Nicht eingeloggt.",
"community_ban": "Du wurdest von dieser Gemeinschaft gebannt.",
"community_ban": "Du wurdest von dieser Community gebannt.",
"site_ban": "Du wurdest von dieser Seite gebannt",
"couldnt_create_comment": "Konnte Kommentar nicht anlegen.",
"couldnt_like_comment": "Konnte nicht liken.",
@ -178,18 +178,18 @@
"couldnt_save_comment": "Konnte Kommentar nicht speichern.",
"no_comment_edit_allowed": "Keine Erlaubnis Kommentar zu editieren.",
"no_post_edit_allowed": "Keine Erlaubnis Beitrag zu editieren.",
"no_community_edit_allowed": "Keine Erlaubnis Gemeinschaft zu editieren.",
"couldnt_find_community": "Konnte Gemeinschaft nicht finden.",
"couldnt_update_community": "Konnte Gemeinschaft nicht aktualisieren.",
"community_already_exists": "Gemeinschaft existiert bereits.",
"community_moderator_already_exists": "Gemeinschaft Moderator existiert bereits.",
"community_follower_already_exists": "Gemeinschaft Follower existiert bereits.",
"community_user_already_banned": "Gemeinschaft Nutzer schon gebannt.",
"no_community_edit_allowed": "Keine Erlaubnis, die Community zu editieren.",
"couldnt_find_community": "Konnte Community nicht finden.",
"couldnt_update_community": "Konnte die Community nicht aktualisieren.",
"community_already_exists": "Die Community existiert bereits.",
"community_moderator_already_exists": "Community-Moderator*in existiert bereits.",
"community_follower_already_exists": "Community-Abonennt*in existiert bereits.",
"community_user_already_banned": "Der*die Community-Benutzer*in ist schon gebannt.",
"couldnt_create_post": "Konnte Beitrag nicht anlegen.",
"couldnt_like_post": "Konnte Beitrag nicht liken.",
"couldnt_find_post": "Konnte Beitrag nicht finden.",
"couldnt_get_posts": "Konnte Beiträge nicht holen.",
"couldnt_update_post": "Konnte Beitrag nicht aktualisieren.",
"couldnt_get_posts": "Konnte Beiträge nicht holen",
"couldnt_update_post": "Konnte Beitrag nicht aktualisieren",
"couldnt_save_post": "Konnte Beitrag nicht speichern.",
"no_slurs": "Keine Beleidigungen.",
"not_an_admin": "Kein Administrator.",
@ -200,9 +200,9 @@
"passwords_dont_match": "Passwörter stimmen nicht überein.",
"admin_already_created": "Entschuldigung, es gibt schon einen Administrator.",
"user_already_exists": "Nutzer existiert bereits.",
"couldnt_update_user": "Konnte Nutzer nicht aktualisieren",
"couldnt_update_user": "Konnte Nutzer nicht aktualisieren.",
"system_err_login": "Systemfehler. Versuche dich aus- und wieder einzuloggen.",
"cross_posted_to": "Crossposted auf: ",
"cross_posted_to": "Crossgeposted auf: ",
"create_private_message": "Privatnachricht erstellen",
"send_secure_message": "Sichere Nachricht absenden",
"send_message": "Nachricht absenden",
@ -223,10 +223,10 @@
"open_registration": "Registrierung öffnen",
"registration_closed": "Registrierung geschlossen",
"enable_nsfw": "NSFW Erlauben",
"donate_to_lemmy": "Lemmy spenden",
"donate_to_lemmy": "An Lemmy spenden",
"donate": "Spenden",
"from": "von",
"logged_in": "Eingeloggt",
"logged_in": "Eingeloggt.",
"couldnt_get_comments": "Konnte Kommentare nicht laden.",
"post_title_too_long": "Posttitel zu lang.",
"email_already_exists": "Email existiert bereits.",
@ -235,5 +235,21 @@
"couldnt_update_private_message": "Konnte Privatnachricht nicht aktualisieren.",
"time": "Zeit",
"action": "Aktion",
"more": "mehr"
"more": "mehr",
"admin_settings": "Admin-Einstellungen",
"sorting_help": "Sortierhilfe",
"banned_users": "Gebannte Benutzer",
"show_context": "Kontext anzeigen",
"block_leaving": "Sind Sie sicher, dass Sie gehen wollen?",
"site_config": "Website-Konfiguration",
"support_on_open_collective": "Auf OpenCollective unterstützen",
"site_saved": "Seite gespeichert.",
"emoji_picker": "Emoji-Tastatur",
"silver_sponsors": "Silbersponsoren sind die, die $40 zu Lemmy beitragen.",
"upvote": "Hochstimmen",
"downvote": "Runterstimmen",
"number_of_upvotes": "{{count}} Stimme",
"number_of_upvotes_plural": "{{count}} Stimmen",
"number_of_downvotes": "{{count}} Gegenstimme",
"number_of_downvotes_plural": "{{count}} Gegenstimmen"
}

1
ui/translations/el.json vendored Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -18,6 +18,7 @@
"communities": "Communities",
"users": "Users",
"create_a_community": "Create a community",
"select_a_community": "Select a community",
"create_community": "Create Community",
"remove_community": "Remove Community",
"subscribed_to_communities": "Subscribed to <1>communities</1>",
@ -26,6 +27,7 @@
"number_of_communities": "{{count}} Community",
"number_of_communities_plural": "{{count}} Communities",
"community_reqs": "lowercase, underscores, and no spaces.",
"invalid_community_name": "Invalid name.",
"create_private_message": "Create Private Message",
"send_secure_message": "Send Secure Message",
"send_message": "Send Message",
@ -63,17 +65,19 @@
"remove_as_admin": "remove as admin",
"appoint_as_admin": "appoint as admin",
"remove": "remove",
"removed": "removed",
"removed": "removed by mod",
"locked": "locked",
"stickied": "stickied",
"reason": "Reason",
"mark_as_read": "mark as read",
"mark_as_unread": "mark as unread",
"delete": "delete",
"deleted": "deleted",
"deleted": "deleted by creator",
"delete_account": "Delete Account",
"delete_account_confirm":
"Warning: this will permanently delete all your data. Enter your password to confirm.",
"click_to_delete_picture": "Click to delete picture.",
"picture_deleted": "Picture deleted.",
"restore": "restore",
"ban": "ban",
"ban_from_site": "ban from site",
@ -187,7 +191,7 @@
"sponsors": "Sponsors",
"sponsors_of_lemmy": "Sponsors of Lemmy",
"sponsor_message":
"Lemmy is free, <1>open-source</1> software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:",
"Lemmy is free, <1>open-source</1> software, with no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project. Thank you to the following people:",
"support_on_patreon": "Support on Patreon",
"support_on_liberapay": "Support on Liberapay",
"support_on_open_collective": "Support on OpenCollective",
@ -195,6 +199,8 @@
"donate": "Donate",
"general_sponsors":
"General Sponsors are those that pledged $10 to $39 to Lemmy.",
"silver_sponsors":
"Silver Sponsors are those that pledged $40 to Lemmy.",
"crypto": "Crypto",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
@ -246,6 +252,7 @@
"Couldn't find that username or email.",
"password_incorrect": "Password incorrect.",
"passwords_dont_match": "Passwords do not match.",
"invalid_username": "Invalid username.",
"admin_already_created": "Sorry, there's already an admin.",
"user_already_exists": "User already exists.",
"email_already_exists": "Email already exists.",

View file

@ -5,7 +5,7 @@
"create_a_post": "Crear una publicación",
"create_post": "Crear Publicación",
"number_of_posts": "{{count}} Publicación",
"number_of_posts_plural": "{{count}} Publicaciónes",
"number_of_posts_plural": "{{count}} Publicaciones",
"posts": "Publicaciones",
"related_posts": "Estas publicaciones podrían estar relacionadas",
"cross_posts": "Este link también ha sido publicado en:",
@ -57,16 +57,16 @@
"remove_as_admin": "eliminar como administrador",
"appoint_as_admin": "designar como administrador",
"remove": "eliminar",
"removed": "eliminado",
"removed": "eliminado por moderador",
"locked": "bloqueado",
"stickied": "fijado",
"reason": "Razón",
"mark_as_read": "marcar como leído",
"mark_as_unread": "marcar como no leído",
"delete": "eliminar",
"deleted": "eliminado",
"deleted": "eliminado por creador",
"delete_account": "Eliminar Cuenta",
"delete_account_confirm": "Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar",
"delete_account_confirm": "Advertencia: esta acción eliminará permanentemente toda tu información. Introduce tu contraseña para confirmar.",
"restore": "restaurar",
"ban": "expulsar",
"ban_from_site": "expulsar del sitio",
@ -169,13 +169,13 @@
"theme": "Tema",
"sponsors": "Patrocinadores",
"sponsors_of_lemmy": "Patrocinadores de Lemmy",
"sponsor_message": "Lemmy es software libre y de <1>código abierto</1>, lo que significa que no tendrá publicidades, monetización, ni capitales emprendedores, nunca. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:",
"sponsor_message": "Lemmy es software libre y de <1>código abierto</1>, lo que significa que nunca tendrá publicidad, monetización, ni capitales emprendedores. Tus donaciones apoyan directamente el desarrollo a tiempo completo del proyecto. Muchas gracias a las siguientes personas:",
"support_on_patreon": "Apoyo en Patreon",
"support_on_liberapay": "Apoyo en Liberapay",
"donate_to_lemmy": "Donar a Lemmy",
"donate": "Donar",
"general_sponsors": "Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.",
"crypto": "Crypto",
"crypto": "Cripto",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
"monero": "Monero",
@ -233,7 +233,7 @@
"time": "Tiempo",
"action": "Acción",
"more": "más",
"cross_posted_to": "publicado también en:",
"cross_posted_to": "publicado también en: ",
"sorting_help": "ayuda del orden",
"upvote": "Voto Positivo",
"number_of_upvotes": "{{count}} Voto Positivo",
@ -244,5 +244,14 @@
"couldnt_get_comments": "No se pudo obtener los comentarios.",
"post_title_too_long": "El título de la publicación es muy largo.",
"block_leaving": "¿Está seguro de que desea salir?",
"show_context": "Mostrar contexto"
"show_context": "Mostrar contexto",
"silver_sponsors": "Sponsors Plata son los que han dado $40 a Lemmy.",
"site_config": "Configuración del Sitio",
"banned_users": "Usuarios Baneados",
"support_on_open_collective": "Dona en OpenCollective",
"site_saved": "Sitio Guardado.",
"emoji_picker": "Lista de emojis",
"admin_settings": "Panel de Administración",
"select_a_community": "Selecciona una comunidad",
"invalid_username": "Nombre de usuario inválido."
}

67
ui/translations/eu.json vendored Normal file
View file

@ -0,0 +1,67 @@
{
"post": "Argitaratu",
"remove_post": "Argitalpena Ezabatu",
"no_posts": "Argitalpenik gabe.",
"create_a_post": "Argitalpen bat sortu",
"number_of_posts": "Argitalpen {{count}}",
"number_of_posts_plural": "{{count}} Argitalpen",
"users": "Erabiltzaileak",
"send_message": "Mezua Bidali",
"message": "Mezu",
"edit": "Editatu",
"reply": "Erantzun",
"more": "gehiago",
"upload_image": "Irudia igo",
"link": "esteka",
"remove": "ezabatu",
"mark_as_unread": "irakurri gabea",
"delete": "ezabatu",
"delete_account": "Kontua Ezabatu",
"ban": "kaleratu",
"ban_from_site": "Lekutik kaleratu",
"unban": "onartu",
"save": "gorde",
"create": "sortu",
"creator": "sortzaile",
"username": "Erabiltzailea",
"name": "Izena",
"title": "Izenburua",
"both": "Biak",
"saved": "Gordeta",
"week": "Aste",
"month": "Hilabete",
"year": "Urte",
"all": "Dena",
"api": "API",
"unread": "Irakurri gabe",
"replies": "Erantzunak",
"search": "Bilatu",
"sign_up": "Kontua Sortu",
"messages": "Mezuak",
"password": "Pasahitza",
"password_change": "Pasahitza Aldatu",
"new_password": "Pasahitz Berria",
"email": "Posta elektronikoa",
"language": "Hizkuntza",
"url": "URL",
"chat": "Txat",
"your_site": "zure lekua",
"nsfw": "NSFW",
"block_leaving": "Ziur ahal zaude atera nahi duzula?",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
"monero": "Monero",
"yes": "bai",
"no": "ez",
"couldnt_find_post": "Ezinezkoa argitalpena aurkitzea.",
"couldnt_save_post": "Ezinezkoa argitalpena gordetzea.",
"site_already_exists": "Lekua jada existitzen da.",
"action": "Ekintza",
"time": "Denbora",
"number_of_points": "Puntu {{count}}",
"number_of_points_plural": "{{count}} Puntu",
"number_of_users": "Erabiltzaile {{count}}",
"number_of_users_plural": "{{count}} Erabiltzaile",
"number_of_subscribers": "Jarraitzaile {{count}}",
"number_of_subscribers_plural": "{{count}} Jarraitzaile"
}

View file

@ -1,28 +1,30 @@
{
"post": "viesti",
"remove_post": "Poista viesti",
"no_posts": "Ei viestjä.",
"no_posts": "Ei viestejä.",
"create_a_post": "Luo viesti",
"create_post": "Luo viesti",
"number_of_posts": "{{count}} viestiä",
"number_of_posts": "{{count}} viesti",
"number_of_posts_plural": "{{count}} viestiä",
"posts": "Viestit",
"related_posts": "Nämä viestit voivat liittyä toisiinsa",
"cross_posts": "Tämä linkki on jaettu:",
"cross_post": "jaa ristiin",
"comments": "Kommentit",
"number_of_comments": "{{count}} kommenttia",
"number_of_comments": "{{count}} kommentti",
"number_of_comments_plural": "{{count}} kommenttia",
"remove_comment": "Poista kommentti",
"communities": "Yhteisöt",
"users": "Käyttäjät",
"create_a_community": "Luo yhteisö",
"create_community": "Luo yhteisö",
"remove_community": "Poista yhteisö",
"subscribed_to_communities": "Tilatut <1>yhteisöt</1>",
"subscribed_to_communities": "Tilatut <1>yhteisöt</1>",
"trending_communities": "Nousevat <1>yhteisöt</1>",
"list_of_communities": "Lista yhteisöistä",
"number_of_communities": "{{count}} yhteisöä",
"community_reqs":
"pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.",
"number_of_communities": "{{count}} yhteisö",
"number_of_communities_plural": "{{count}} yhteisöä",
"community_reqs": "pienillä kirjaimilla, alleviivauksella, eikä välilyöntejä.",
"create_private_message": "Luo yksityisviesti",
"send_secure_message": "Lähetä suojattu viesti",
"send_message": "Lähetä viesti",
@ -50,7 +52,7 @@
"remove_as_mod": "Poista moderaattorina",
"appoint_as_mod": "Nimitä moderaattoriksi",
"modlog": "Moderoinnin loki",
"admin": "Ylläpitäjä",
"admin": "ylläpitäjä",
"admins": "ylläpitäjät",
"remove_as_admin": "poista ylläpitäjänä",
"appoint_as_admin": "nimitä ylläpitäjäksi",
@ -62,10 +64,9 @@
"mark_as_read": "merkitse luetuksi",
"mark_as_unread": "merkitse lukemattomaksi",
"delete": "poista",
"deleted": "deleted",
"deleted": "poistettu",
"delete_account": "Poista tili",
"delete_account_confirm":
"Varoitus: tämä poistaa pysyvästi kaiken datasi. Anna salasanasi varmistukseksi.",
"delete_account_confirm": "Varoitus: tämä poistaa pysyvästi kaiken datasi. Anna salasanasi varmistukseksi.",
"restore": "palauta",
"ban": "porttikielto",
"ban_from_site": "aseta porttikielto sivulle",
@ -78,12 +79,16 @@
"creator": "luoja",
"username": "Käyttäjänimi",
"email_or_username": "Sähköposti tai käyttäjätunnus",
"number_of_users": "{{count}} käyttäjää",
"number_of_subscribers": "{{count}} tilaajaa",
"number_of_points": "{{count}} pistettä",
"number_online": "{{count}} käyttäjää aktiivisena",
"number_of_users": "{{count}} käyttäjä",
"number_of_users_plural": "{{count}} käyttäjää",
"number_of_subscribers": "{{count}} tilaaja",
"number_of_subscribers_plural": "{{count}} tilaajaa",
"number_of_points": "{{count}} piste",
"number_of_points_plural": "{{count}} pistettä",
"number_online": "{{count}} käyttäjä aktiivisena",
"number_online_plural": "{{count}} käyttäjää aktiivisena",
"name": "Nimi",
"title": "Kuvaus",
"title": "Otsikko",
"category": "Luokka",
"subscribers": "Tilaajat",
"both": "Molemmat",
@ -121,8 +126,7 @@
"login_sign_up": "Kirjaudu sisään / Rekisteröidy",
"login": "Kirjaudu sisään",
"sign_up": "Rekisteröidy",
"notifications_error":
"Työpöydän ilmoitukset eivät ole saatavilla selaimellesi. Yritä Firefoxia tai Chromea.",
"notifications_error": "Työpöydän ilmoitukset eivät ole saatavilla selaimellesi. Yritä Firefoxia tai Chromea.",
"unread_messages": "Lukemattomat viestit",
"messages": "Viestit",
"password": "Salasana",
@ -134,9 +138,8 @@
"new_password": "Uusi salasana",
"no_email_setup": "Tämä palvelin ei ole asettanut sähköpostia oikein.",
"email": "Sähköposti",
"matrix_user_id": " Matrix-käyttäjä",
"private_message_disclaimer":
"Varoitus: Yksityisviestit Lemmyssä eivät ole turvallisia. Luo tili <1>Riot.im</1> -palveluun turvallista viestintää varten.",
"matrix_user_id": "Matrix-käyttäjä",
"private_message_disclaimer": "Varoitus: Yksityisviestit Lemmyssä eivät ole turvallisia. Luo tili <1>Riot.im</1> -palveluun turvallista viestintää varten.",
"send_notifications_to_email": "Lähetä ilmoitukset sähköpostiin",
"optional": "Valinnainen",
"expires": "Umpeutuu",
@ -148,7 +151,7 @@
"registration_closed": "Rekisteröityminen suljettu",
"enable_nsfw": "Salli NSFW",
"url": "URL",
"body": "Body",
"body": "Sisältö",
"copy_suggested_title": "kopioi ehdotettu otsikko: {{title}}",
"community": "Yhteisö",
"expand_here": "Laajenna tässä",
@ -166,14 +169,12 @@
"theme": "Teema",
"sponsors": "Sponsorit",
"sponsors_of_lemmy": "Lemmy-sponsorit",
"sponsor_message":
"Lemmy on vapaa, <1>avoimen lähdekoodin</1> -ohjelmisto, eli mainontaa, rahantekemistä, tai pääomasijoitusta täällä ei tule ikinä olemaan. Lahjoituksesi tukevat suoraan projektin täysipäiväistä kehitystä. Kiitokset seuraaville ihmisille:",
"sponsor_message": "Lemmy on vapaa, <1>avoimen lähdekoodin</1> -ohjelmisto, eli mainontaa, rahantekemistä, tai pääomasijoitusta täällä ei tule ikinä olemaan. Lahjoituksesi tukevat suoraan projektin täysipäiväistä kehitystä. Kiitokset seuraaville ihmisille:",
"support_on_patreon": "Tue Patreonissa",
"donate_to_lemmy": "Lahjoita Lemmylle",
"donate": "Lahjoita",
"general_sponsors":
"Yleisiä sponsoreja ovat he, jotka lupaavat 10-39 dollaria Lemmylle.",
"crypto": "Crypto",
"general_sponsors": "Yleisiä sponsoreja ovat he, jotka lupaavat 10-39 dollaria Lemmylle.",
"crypto": "Krypto",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
"monero": "Monero",
@ -188,8 +189,7 @@
"yes": "kyllä",
"no": "ei",
"powered_by": "Vauhdittajana",
"landing_0":
"Lemmy on <1>linkinkerääjä</1> / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä</2>.<3></3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua</4>). Federointi ActivityPub-verkkoon on suunnittelun alla. <5></5>Tämä on <6>hyvin varhainen betaversio</6>, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7></7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.</8><9></9>Tehty teknologioilla <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"landing_0": "Lemmy on <1>linkinkerääjä</1> / Reddit-vaihtoehto, tarkoitettu toimimaan <2>fediversessä</2>.<3></3>Sitä voi isännöidä itse, siinä on tosiaikaisesti päivittyvät kommenttiketjut, ja se on pieni (<4>~80 kilotavua</4>). Federointi ActivityPub-verkkoon on suunnittelun alla. <5></5>Tämä on <6>hyvin varhainen betaversio</6>, ja monet ominaisuudet ovat toistaiseksi rikki tai poissa. <7></7>Ehdota uusia ominaisuuksia tai raportoi bugeja <8>tänne.</8><9></9>Tehty teknologioilla <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Ei kirjautunut sisään.",
"logged_in": "Kirjautunut sisään.",
"community_ban": "Sinulle on asetettu porttikielto tähän yhteisöön.",
@ -198,9 +198,9 @@
"couldnt_like_comment": "Kommentista ei voitu tykätä.",
"couldnt_update_comment": "Kommenttia ei voitu päivittää.",
"couldnt_save_comment": "Kommenttia ei voitu tallentaa.",
"no_comment_edit_allowed": "Et ole sallittu muokkaamaan kommenttia.",
"no_post_edit_allowed": "Et ole sallittu muokkaamaan viestiä.",
"no_community_edit_allowed": "Et ole sallittu muokkaamaan yhteisöä.",
"no_comment_edit_allowed": "Sinulla ei ole oikeutta muokata kommenttia.",
"no_post_edit_allowed": "Sinulla ei ole oikeutta muokata viestiä.",
"no_community_edit_allowed": "Sinulla ei ole oikeutta muokata yhteisöä.",
"couldnt_find_community": "Yhteisöä ei voitu löytää.",
"couldnt_update_community": "Yhteisöä ei voitu päivittää.",
"community_already_exists": "Yhteisö on jo olemassa.",
@ -217,18 +217,39 @@
"not_an_admin": "Ei ole ylläpitäjä.",
"site_already_exists": "Sivusto on jo olemassa.",
"couldnt_update_site": "Sivustoa ei voitu päivittää.",
"couldnt_find_that_username_or_email":
"Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.",
"couldnt_find_that_username_or_email": "Käyttäjänimeä tai sähköpostia ei onnistuttu löytämään.",
"password_incorrect": "Salasana on väärin.",
"passwords_dont_match": "Salasanat eivät täsmää.",
"admin_already_created": "Anteeksi, mutta täällä on jo ylläpitäjä.",
"user_already_exists": "Käyttäjä on jo olemassa.",
"email_already_exists": "Sähköposti on jo olemassa.",
"couldnt_update_user": "Käyttäjää ei voitu päivittää.",
"system_err_login":
"Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.",
"system_err_login": "Järjestelmävirhe. Yritä kirjautua ulos ja kirjautua uudestaan sisään.",
"couldnt_create_private_message": "Yksityisviestiä ei voitu luoda.",
"no_private_message_edit_allowed":
"Et ole sallittu muokkaamaan yksityisviestiä.",
"couldnt_update_private_message": "Yksityisviestiä ei voitu päivittää."
"no_private_message_edit_allowed": "Sinulla ei ole oikeutta muokata yksityisviestiä.",
"couldnt_update_private_message": "Yksityisviestiä ei voitu päivittää.",
"more": "lisää",
"cross_posted_to": "ristipostattu: ",
"sorting_help": "apua lajitteluun",
"show_context": "Näytä yhteys",
"admin_settings": "Ylläpitäjän asetukset",
"site_config": "Sivun asetukset",
"banned_users": "Porttikieltoon asetetut käyttäjät",
"emoji_picker": "Emoji-valitsin",
"upvote": "Anna ylä-ääni",
"number_of_upvotes": "{{count}} ylä-ääni",
"number_of_upvotes_plural": "{{count}} ylä-ääntä",
"old": "Vanhat",
"downvote": "Anna alaääni",
"number_of_downvotes": "{{count}} alaääni",
"number_of_downvotes_plural": "{{count}} alaääntä",
"couldnt_get_comments": "Kommentteja ei voitu hakea.",
"support_on_liberapay": "Tue Liberapayssa",
"time": "Aika",
"action": "Toiminto",
"block_leaving": "Haluatko varmasti poistua?",
"silver_sponsors": "Hopeasponsoreita ovat ne, jotka lupaavat 40 dollaria Lemmylle.",
"post_title_too_long": "Viestin otsikko on liian pitkä.",
"support_on_open_collective": "Tie OpenCollectivessa",
"site_saved": "Sivu tallennettu."
}

View file

@ -1,86 +1,93 @@
{
"post": "publication",
"remove_post": "Supprimer la publication",
"no_posts": "Pas de publications.",
"no_posts": "Aucune publication.",
"create_a_post": "Créer une publication",
"create_post": "Créer la publication",
"number_of_posts": "{{count}} Publications",
"create_post": "Créer une publication",
"number_of_posts": "{{count}} Publication",
"number_of_posts_plural": "{{count}} Publications",
"posts": "Publications",
"related_posts": "Ces sujets peuvent être corrélés",
"cross_posts": "Ce sujet a également été posté sur :",
"related_posts": "Publications similaires",
"cross_posts": "Ce lien a également été publié sur :",
"cross_post": "publication croisée",
"cross_posted_to": "publication croisée à : ",
"cross_posted_to": "publication croisée sur : ",
"comments": "Commentaires",
"number_of_comments": "{{count}} Commentaires",
"number_of_comments": "{{count}} Commentaire",
"number_of_comments_plural": "{{count}} Commentaires",
"remove_comment": "Supprimer le commentaire",
"communities": "Communautés",
"users": "Utilisateurs",
"create_a_community": "Créer une communauté",
"create_community": "Créer la communauté",
"create_community": "Créer une communauté",
"remove_community": "Supprimer la Communauté",
"subscribed_to_communities": "Abonné à ces <1>communautés</1>",
"trending_communities": "<1>Communautés</1> appréciées",
"list_of_communities": "Liste des communautés",
"number_of_communities": "{{count}} communautés",
"community_reqs": "en minuscule, sans espace et avec tiret du bas.",
"number_of_communities": "{{count}} Communauté",
"number_of_communities_plural": "{{count}} Communautés",
"community_reqs": "en minuscule, tirets du bas et sans espace.",
"create_private_message": "Créer un message privé",
"send_secure_message": "Envoyer le message sécurisé",
"send_message": "Enovyer le message",
"send_secure_message": "Envoyer un message sécurisé",
"send_message": "Envoyer le message",
"message": "Message",
"edit": "éditer",
"reply": "répondre",
"cancel": "Annuler",
"preview": "prévisualiser",
"preview": "Prévisualiser",
"upload_image": "envoyer une image",
"avatar": "Avatar",
"upload_avatar": "Télécharger une avatar",
"upload_avatar": "Télécharger un avatar",
"show_avatars": "Afficher les avatars",
"formatting_help": "aide au formattage",
"formatting_help": "aide au formatage",
"view_source": "voir la source",
"unlock": "débloquer",
"lock": "bloquer",
"unlock": "déverrouiller",
"lock": "verrouiller",
"sticky": "épingler",
"unsticky": "décrocher",
"unsticky": "désépingler",
"link": "lien",
"archive_link": "archiver le lien",
"mod": "modérateur",
"mods": "modérateurs",
"moderates": "Modérer",
"settings": "Paramètres",
"remove_as_mod": "Supprimer comme modérateur",
"appoint_as_mod": "Nommer comme modérateur",
"remove_as_mod": "supprimer comme modérateur",
"appoint_as_mod": "nommer comme modérateur",
"modlog": "Historique de modération",
"admin": "admin",
"admins": "admins",
"remove_as_admin": "Supprimer comme admin",
"appoint_as_admin": "Nommer comme admin",
"remove_as_admin": "supprimer comme admin",
"appoint_as_admin": "nommer comme admin",
"remove": "retirer",
"removed": "retiré",
"locked": "bloqué",
"removed": "supprimé par le modérateur",
"locked": "verrouillé",
"stickied": "épinglé",
"reason": "Raison",
"mark_as_read": "marquer comme lu",
"mark_as_unread": "marquer comme non-lu",
"delete": "supprimer",
"deleted": "supprimé",
"deleted": "supprimé par le créateur",
"delete_account": "Supprimer le compte",
"delete_account_confirm": "Avertissement : cette action supprimera toutes vos données de façons permanente ! Saisissez votre mot de passe pour confirmer.",
"restore": "restaurer",
"ban": "bannir",
"ban_from_site": "bannir du site",
"unban": "pardon",
"unban_from_site": "faire revenir sur le site",
"unban": "débannir",
"unban_from_site": "débannir du site",
"banned": "banni",
"save": "sauvegarder",
"unsave": "retirer",
"create": "créer",
"creator": "createur",
"username": "Nom d'utilisateur",
"email_or_username": "Email ou Nom d'utilisateur",
"number_of_users": "{{count}} Utilisateurs",
"number_of_subscribers": "{{count}} Abonnés",
"number_of_points": "{{count}} Points",
"number_online": "{{count}} Utilisateurs en ligne",
"creator": "créateur",
"username": "Nom dutilisateur·rice",
"email_or_username": "Email ou nom dutilisateur·rice",
"number_of_users": "{{count}} Utilisateur",
"number_of_users_plural": "{{count}} Utilisateurs",
"number_of_subscribers": "{{count}} Abonné",
"number_of_subscribers_plural": "{{count}} Abonnés",
"number_of_points": "{{count}} Point",
"number_of_points_plural": "{{count}} Points",
"number_online": "{{count}} Utilisateur en ligne",
"number_online_plural": "{{count}} Utilisateurs en ligne",
"name": "Nom",
"title": "Titre",
"category": "Catégorie",
@ -88,7 +95,7 @@
"both": "Les deux",
"saved": "Sauvegardé",
"unsubscribe": "Se désabonner",
"subscribe": "S'abonner",
"subscribe": "Sabonner",
"subscribed": "Abonnés",
"prev": "Précédent",
"next": "Suivant",
@ -118,10 +125,10 @@
"overview": "Général",
"view": "Voir",
"logout": "Se déconnecter",
"login_sign_up": "Se connecter / S'inscrire",
"login_sign_up": "Se connecter / Sinscrire",
"login": "Se connecter",
"sign_up": "S'inscrire",
"notifications_error": "Les notifications de bureau ne sont pas discponibles sur votre navigateur. Essayez Firefox ou Chrome.",
"sign_up": "Sinscrire",
"notifications_error": "Les notifications de bureau ne sont pas disponibles sur votre navigateur. Essayez Firefox ou Chrome.",
"unread_messages": "Messages non-lu",
"messages": "Messages",
"password": "Mot de passe",
@ -131,45 +138,45 @@
"reset_password_mail_sent": "Un email a été envoyé pour réinitialiser votre mot de passe.",
"password_change": "Changement de mot de passe",
"new_password": "Nouveau mot de passe",
"no_email_setup": "Ce serveur n'a pas correctement configuré la messagerie de email.",
"no_email_setup": "Ce serveur na pas correctement configuré la messagerie du courrier.",
"email": "Email",
"matrix_user_id": "Utilisateur Matrix",
"private_message_disclaimer": "Attention : les messages privés dans Lemmy ne sont pas sécurisés. Veuillez créer un compte sur <1>Riot.im</1> pour pouvoir envoyer des messages sécurisés.",
"send_notifications_to_email": "Envoyer des notifications par email",
"optional": "Optionnel",
"optional": "Facultatif",
"expires": "Expire",
"language": "Langue",
"browser_default": "Défaut pour le navigateur",
"downvotes_disabled": "Votes négatifs désactivés",
"enable_downvotes": "Votes négatifs activés",
"open_registration": "Ouvrir la regestration",
"registration_closed": "Régestration fermée",
"open_registration": "Ouvrir les inscriptions",
"registration_closed": "Inscriptions fermées",
"enable_nsfw": "Activer NSFW",
"url": "URL",
"body": "Texte",
"copy_suggested_title": "copier le titre suggéré : {{title}}",
"community": "Communauté",
"expand_here": "Développer ici",
"subscribe_to_communities": "S'abonner à quelques <1>communautés</1>.",
"subscribe_to_communities": "Sabonner à quelques <1>communautés</1>.",
"chat": "Chat",
"recent_comments": "Commentaires récents",
"no_results": "Pas de résultats.",
"setup": "Installation",
"lemmy_instance_setup": "Installation d'une instance Lemmy",
"lemmy_instance_setup": "Installation dune instance Lemmy",
"setup_admin": "Créer un administrateur",
"your_site": "votre site",
"modified": "modifié",
"nsfw": "Pas sûr pour le travail",
"nsfw": "Pas sûr pour le travail (NSFW)",
"show_nsfw": "Afficher le contenu NSFW",
"theme": "Thème",
"sponsors": "Sponsors",
"sponsors_of_lemmy": "Sponsors de Lemmy",
"sponsor_message": "Lemmy est un logiciel libre et <1>open-source</1>, cest à dire, il fonctionne sans publicité et sans monétisation aucune. Vos dons soutiennent directement le développement du projet à temps plein. Merci à toutes ces personnes :",
"sponsor_message": "Lemmy est un logiciel libre et <1>open-source</1>, sans jamais aucune publicité, ni monétisation ou capital-risque. Vos dons soutiennent directement le développement du projet à temps plein. Merci à toutes ces personnes :",
"support_on_patreon": "Soutenir sur Patreon",
"support_on_liberapay": "Soutenir sur Liberapay",
"donate_to_lemmy": "Faire un don à Lemmy",
"donate": "Faire un don",
"general_sponsors": "Les sponsors généraux sont ceux garantissant de 10 à 39$.",
"general_sponsors": "Les Sponsors Généraux sont celles et ceux qui ont fait une donation entre 10 et 39$.",
"crypto": "Cryptomonnaies",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
@ -185,48 +192,69 @@
"yes": "oui",
"no": "non",
"powered_by": "Propulsé par",
"landing_0": "Lemmy est un <1>aggrégateur de lien</1>, similaire à reddit et conçu pour fonctionner sur le <2>fédiverse</2>.<3></3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB</4>). La fédération via Activitypub est prévue. <5></5>Lemmy est une <6>version beta très précoce</6>, et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7></7>Vous pouvez rapporter des bugs et suggérez de nouvelles fonctionnalités <8>ici.</8><9></9>Crée avec <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Vous n'êtes pas connecté.",
"landing": "Lemmy est un <1>aggrégateur de liens</1>, similaire à Reddit et conçu pour fonctionner sur le <2>Fédivers</2>.<3></3>Il est auto-hébergeable, se met à jour en direct et est léger (<4>~80kB</4>). La fédération via ActivityPub est prévue dans sa feuille de route. <5></5>Lemmy est une <6>version beta très précoce</6> et de nombreuses fonctionnalités sont manquantes ou non fonctionnelles. <7></7>Vous pouvez signaler des bugs et suggérer de nouvelles fonctionnalités <8>ici.</8><9></9>Créé avec <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Vous nêtes pas connecté.",
"logged_in": "Vous êtes connecté.",
"community_ban": "Vous avez été banni de cette communauté.",
"site_ban": "Vous avez été banni du site",
"couldnt_create_comment": "Impossible de poster le commentaire.",
"couldnt_like_comment": "Impossible d'aimer le commentaire.",
"couldnt_create_comment": "Impossible de publier le commentaire.",
"couldnt_like_comment": "Impossible daimer le commentaire.",
"couldnt_update_comment": "Impossible de mettre à jour le commentaire.",
"couldnt_save_comment": "Impossible de sauvegarder le commentaire.",
"couldnt_get_comments": "Impossible de obtenir les commentaires.",
"no_comment_edit_allowed": "Vous n'êtes pas autorisé à éditer ce commentaire.",
"no_post_edit_allowed": "Vous n'êtes pas autorisé à éditer sujet.",
"no_community_edit_allowed": "Vous n'êtes pas autorisé à éditer cette communauté.",
"couldnt_get_comments": "Impossible d'obtenir les commentaires.",
"no_comment_edit_allowed": "Vous nêtes pas autorisé à éditer ce commentaire.",
"no_post_edit_allowed": "Vous nêtes pas autorisé à éditer la publication.",
"no_community_edit_allowed": "Vous nêtes pas autorisé à éditer cette communauté.",
"couldnt_find_community": "Impossible de trouver cette communauté.",
"couldnt_update_community": "Impossible d'éditer cette communauté.",
"couldnt_update_community": "Impossible déditer cette communauté.",
"community_already_exists": "Cette communauté existe déjà.",
"community_moderator_already_exists": "Ce membre est déjà modérateur.",
"community_follower_already_exists": "Ce membre est déjà abonné.",
"community_user_already_banned": "Ce membre est déjà banni.",
"couldnt_create_post": "Impossible de créer le sujet.",
"post_title_too_long": "Sujet titre trop long.",
"couldnt_like_post": "Impossible d'aimer le sujet.",
"couldnt_find_post": "Impossible de trouver le sujet.",
"couldnt_get_posts": "Impossible d'obtenir les sujets",
"couldnt_update_post": "Impossible de mettre à jour le sujet",
"couldnt_save_post": "Impossible de sauvegarder le sujet.",
"no_slurs": "Pas d'insultes.",
"couldnt_create_post": "Impossible de créer la publication.",
"post_title_too_long": "Le titre de la publication est trop long.",
"couldnt_like_post": "Impossible daimer la publication.",
"couldnt_find_post": "Impossible de trouver la publication.",
"couldnt_get_posts": "Impossible dobtenir les publications",
"couldnt_update_post": "Impossible de mettre à jour la publication",
"couldnt_save_post": "Impossible de sauvegarder la publication.",
"no_slurs": "Pas dinsultes.",
"not_an_admin": "Pas administrateur.",
"site_already_exists": "Le site existe déjà.",
"couldnt_update_site": "Impossible de mettre à jour le site.",
"couldnt_find_that_username_or_email": "Impossible de trouver cet utilisateur ou cet email.",
"couldnt_find_that_username_or_email": "Impossible de trouver cet·te utilisateur·rice ou cet email.",
"password_incorrect": "Mot de passe incorrect.",
"passwords_dont_match": "Les mots de passes ne correspondent pas..",
"admin_already_created": "Désolé, il y a déjà un admin.",
"user_already_exists": "L'utilisateur existe déjà.",
"user_already_exists": "Lutilisateur·rice existe déjà.",
"email_already_exists": "Lemail existe déjà.",
"couldnt_update_user": "Impossible de mettre à jour l'utilisateur.",
"couldnt_update_user": "Impossible de mettre à jour lutilisateur·rice.",
"system_err_login": "Erreur système. Essayez de vous déconneter puis de vous reconnecter.",
"couldnt_create_private_message": "Impossible de créer un message privé.",
"no_private_message_edit_allowed": "Pas autorisé à modifier un message privé.",
"couldnt_update_private_message": "Impossible de modifier un message privé.",
"time": "Temps",
"action": "Action",
"more": "plus"
"more": "plus",
"admin_settings": "Paramètres Administrateur",
"site_config": "Configuration du site",
"banned_users": "Utilisateurs interdits",
"site_saved": "Site sauvegardé.",
"support_on_open_collective": "Soutien sur OpenCollective",
"sorting_help": "aide au tri",
"upvote": "Voter pour",
"show_context": "Afficher le contexte",
"block_leaving": "Vous êtes sûr de vouloir partir ?",
"number_of_upvotes": "{{count}} Votes pour",
"number_of_upvotes_plural": "{{count}} Votes contre",
"number_of_downvotes": "{{count}} Vote contre",
"number_of_downvotes_plural": "{{count}} Votes contre",
"downvote": "Voter contre",
"emoji_picker": "Sélecteur démojis",
"silver_sponsors": "Les Sponsors Argent sont celles et ceux qui ont fait une donation de 40$ à Lemmy.",
"select_a_community": "Sélectionner une communauté",
"invalid_username": "Nom d'utilisateur invalide.",
"invalid_community_name": "Nom invalide.",
"click_to_delete_picture": "Cliquer pour supprimer l'image.",
"picture_deleted": "Image supprimée."
}

1
ui/translations/gl.json vendored Normal file
View file

@ -0,0 +1 @@
{}

66
ui/translations/hi.json vendored Normal file
View file

@ -0,0 +1,66 @@
{
"create_a_post": "पोस्ट बनाएँ",
"create_post": "पोस्ट बनाएँ",
"posts": "पोस्ट",
"unlock": "ताला खोलें",
"avatar": "अवतार",
"upload_image": "फ़ोटो अपलोड करें",
"comments": "टिप्पणी (कमेंट )",
"remove_comment": "टिप्पणी हटाएँ (कमेंट हटाएँ)",
"upload_avatar": "अवतार अपलोड करें",
"post": "पोस्ट",
"remove_post": "पोस्ट हटाएँ",
"number_of_posts": "{{count}} पोस्ट",
"number_of_posts_plural": "{{count}} पोस्ट्स",
"cross_posts": "यह लिंक अन्य स्थान पर भी पोस्ट किया गया हैं :",
"related_posts": "यह पोस्ट्स संबंधित हो सकते हैं |",
"number_of_comments": "{{ count }} टिप्पणी (कमेंट )",
"number_of_comments_plural": "{{ count }} टिप्पणियाँ (कोम्मेंट्स )",
"communities": "सामुदायिक",
"users": "उपयोगकर्ता",
"create_a_community": "समुदाय बनाएँ",
"create_community": "समुदाय बनाएँ",
"remove_community": "समुदाय हटाएँ",
"list_of_communities": "समुदायों की सूची",
"create_private_message": "निजी संदेश बनाएँ",
"send_secure_message": "सुरक्षित संदेश भेजें",
"send_message": "संदेश भेजें",
"message": "संदेश",
"edit": "संपादित करें ( एडिट करें )",
"reply": "जवाब दें",
"more": "और भी",
"cancel": "रद्द करें",
"preview": "पूर्वावलोकन करें",
"show_avatars": "अवतार दिखाएँ",
"show_context": "संदर्भ दिखाएँ",
"community": "समुदाय",
"number_of_communities": "{{count}} समुदाय",
"number_of_communities_plural": "{{count}} समुदाय",
"community_reqs": "छोटे अक्षर, अंडरस्कोर, और कोई रिक्त स्थान नहीं |",
"forgot_password": "पासवर्ड भूल गए",
"no_posts": "कोई पोस्ट नहीं |",
"community_already_exists": "यह समुदाय पहले स मौजूद है |",
"couldnt_create_comment": "टिप्पणी (कमेंट) नहीं बना पाईं |",
"couldnt_find_community": "समुदायों नहीं ढूंढ़ पाएं |",
"couldnt_find_post": "पोस्ट नहीं ढूंढ़ पाएं |",
"trending_communities": "अभी प्रचलित <1>समुदाय</1>",
"formatting_help": "स्वरूपण सहायता",
"view_source": "स्रोत देखें",
"lock": "बंद करें",
"link": "लिंक",
"mods": "मध्यस्थों",
"settings": "समायोजन (सेटिंग्स)",
"site_config": "साइड कॉन्फ़िगरेशन",
"appoint_as_mod": "मध्यथ के रूप में नियुक्त करें",
"admin": "प्रशासक",
"remove": "हटाएँ",
"removed": "हटाए गए",
"locked": "बंद",
"reason": "कारण",
"mod": "मध्यस्थ",
"admin_settings": "प्रशासक समायोजन (सेटिंग्स)",
"remove_as_mod": "मध्यथ के स्थान से हटाएँ",
"admins": "प्रशासकों",
"remove_as_admin": "प्रशासक के स्थान से हटाएँ",
"appoint_as_admin": "प्रशासक के रूप में नियुक्त करें"
}

154
ui/translations/hu.json vendored Normal file
View file

@ -0,0 +1,154 @@
{
"post": "Elküld",
"remove_post": "Bejegyzés eltávolítása",
"no_posts": "Nincs bejegyzés.",
"create_post": "Bejegyzés létrehozása",
"create_a_post": "Bejegyzés létrehozása",
"number_of_posts": "{{count}} bejegyzés",
"number_of_posts_plural": "{{count}} bejegyzés",
"posts": "Bejegyzések",
"related_posts": "Ezek a bejegyzések kapcsolódhatnak",
"cross_posts": "Ez a hivatkozás itt is be lett küldve:",
"cross_post": "keresztbejegyzés",
"comments": "Hozzászólások",
"remove_comment": "Hozzászólások eltávolítása",
"cross_posted_to": "beküldve ide is: ",
"number_of_comments": "{{count}} hozzászólás",
"number_of_comments_plural": "{{count}} hozzászólás",
"communities": "Közösségek",
"users": "Felhasználók",
"create_a_community": "Közösség létrehozása",
"select_a_community": "Közösség kiválasztása",
"create_community": "Közösség létrehozása",
"remove_community": "Közösség eltávolítása",
"trending_communities": "Népszerű <1>közösségek</1>",
"list_of_communities": "Közösségek listája",
"community_reqs": "Kisbetű és alsóvonás megengedett, szóköz nem.",
"create_private_message": "Privát üzenet létrehozása",
"send_secure_message": "Biztonságos üzenet küldése",
"send_message": "Üzenet küldése",
"message": "Üzenet",
"edit": "szerkesztés",
"reply": "válasz",
"more": "több",
"cancel": "Mégse",
"preview": "Előnézet",
"upload_image": "kép feltöltése",
"avatar": "Avatár",
"upload_avatar": "Avatár feltöltése",
"show_avatars": "Avatárok mutatása",
"show_context": "Összefüggés mutatása",
"sorting_help": "rendezési segítség",
"view_source": "forrás megtekintése",
"unlock": "zárolás feloldása",
"lock": "zárolás",
"sticky": "rögzítés",
"unsticky": "rögzítés feloldása",
"link": "hivatkozás",
"mod": "moderátor",
"mods": "moderátorok",
"moderates": "Moderált közösségek",
"settings": "Beállítások",
"admin_settings": "Adminisztrációs beállítások",
"remove_as_mod": "moderátori jog eltávolítása",
"appoint_as_mod": "kinevezés moderátornak",
"modlog": "Moderációs napló",
"admin": "admin",
"admins": "adminok",
"remove_as_admin": "adminjog eltávolítása",
"appoint_as_admin": "kinevezés adminnak",
"remove": "eltávolítás",
"locked": "zárolva",
"stickied": "rögzítve",
"reason": "Indok",
"mark_as_read": "megjelölés olvasottnak",
"mark_as_unread": "megjelölés olvasatlannak",
"delete": "törlés",
"deleted": "eltávolítva a szerző által",
"delete_account": "FIók törlése",
"restore": "visszaállítás",
"ban": "kitiltás",
"ban_from_site": "kitiltás az oldalról",
"unban": "kitiltás visszavonása",
"unban_from_site": "az oldalról történő kitiltás visszavonása",
"banned": "kitiltva",
"banned_users": "Kitiltott felhasználók",
"save": "mentés",
"unsave": "mentés visszavonása",
"create": "létrehozás",
"creator": "szerző",
"username": "Felhasználónév",
"number_of_points": "{{count}} pont",
"number_of_points_plural": "{{count}} pont",
"number_of_subscribers": "{{count}} feliratkozó",
"number_of_subscribers_plural": "{{count}} feliratkozó",
"name": "Név",
"title": "Cím",
"category": "Kategória",
"both": "Mindkettő",
"saved": "Mentve",
"unsubscribe": "Leiratkozás",
"subscribe": "Feliratkozás",
"subscribed": "Feliratkozva",
"subscribed_to_communities": "Követett <1>közösségek</1>",
"number_of_communities": "{{count}} közösség",
"number_of_communities_plural": "{{count}} közösség",
"formatting_help": "formázási segítség",
"archive_link": "hivatkozás archiválása",
"site_config": "Oldalbeállítások",
"removed": "eltávolítva egy mod által",
"delete_account_confirm": "Figyelmeztetés: ez véglegesen törölni fogja az összes adatodat. A megerősítéshez írd be a jelszavad!",
"email_or_username": "Email vagy felhasználónév",
"number_of_users": "{{count}} felhasználó",
"number_of_users_plural": "{{count}} felhasználó",
"number_online": "{{count}} online felhasználó",
"number_online_plural": "{{count}} online felhasználó",
"subscribers": "Feliratkozók",
"prev": "Előző",
"next": "Következő",
"sidebar": "Oldalsáv",
"sort_type": "Rendezési mód",
"hot": "Népszerű",
"new": "Új",
"old": "Régi",
"invalid_community_name": "Érvénytelen név.",
"inbox_for": "Bejövő üzenetek <1>{{user}}</1> részére",
"overview": "Áttekintés",
"notifications_error": "Az asztali értesítések nem érhetőek el a böngésződben. Próbáld meg Firefoxszal vagy Chrome-mal!",
"no_email_setup": "Az email nincs megfelelően beállítva ezen a szerveren.",
"click_to_delete_picture": "Kattints a kép törléséhez!",
"picture_deleted": "Kép törölve.",
"top_day": "A nap bejegyzése",
"week": "Hét",
"month": "Hónap",
"year": "Év",
"all": "Mind",
"top": "Legjobb",
"api": "API",
"docs": "Dokumentáció",
"inbox": "Bejövő üzenetek",
"mark_all_as_read": "az összes megjelölése olvasottként",
"type": "Típus",
"unread": "Olvastalan",
"replies": "Válaszok",
"mentions": "Említések",
"reply_sent": "Válasz elküldve",
"message_sent": "Üzenet elküldve",
"search": "Keresés",
"view": "Nézet",
"logout": "Kijelentkezés",
"login_sign_up": "Bejelentkezés / Regisztráció",
"login": "Bejelentkezés",
"sign_up": "Regisztráció",
"unread_messages": "Olvastalan üzenetek",
"messages": "Üzenetek",
"password": "Jelszó",
"verify_password": "Jelszó megerősítése",
"old_password": "Régi jelszó",
"forgot_password": "elfelejtettem a jelszavamat",
"reset_password_mail_sent": "Egy email el lett küldve a jelszó visszaállításához.",
"password_change": "Jelszó megváltoztatása",
"new_password": "Új jelszó",
"email": "Email",
"matrix_user_id": "Matrix felhasználó"
}

View file

@ -1,26 +1,29 @@
{
"post": "post",
"remove_post": "Rimuovi Post",
"no_posts": "Nessun Post.",
"create_a_post": "Crea un post",
"create_post": "Crea Post",
"number_of_posts": "{{count}} Posts",
"posts": "Posts",
"related_posts": "Questi post potrebbero essere correlati",
"cross_posts": "Questo link è stato postato anche in:",
"cross_post": "cross-post",
"post": "pubblica",
"remove_post": "Elimina la pubblicazione",
"no_posts": "Nessuna pubblicazione.",
"create_a_post": "Crea una pubblicazione",
"create_post": "Crea una pubblicazione",
"number_of_posts": "{{count}} Pubblicazione",
"number_of_posts_plural": "{{count}} Pubblicazioni",
"posts": "Pubblicazioni",
"related_posts": "Queste pubblicazioni potrebbero essere collegate",
"cross_posts": "Questo collegamento è stato postato anche in:",
"cross_post": "pubblica altrove",
"comments": "Commenti",
"number_of_comments": "{{count}} Commenti",
"remove_comment": "Rimuovi Commento",
"number_of_comments": "{{count}} Commento",
"number_of_comments_plural": "{{count}} Commenti",
"remove_comment": "Elimina Commento",
"communities": "Comunità",
"users": "Utenti",
"create_a_community": "Crea una Comunità",
"create_a_community": "Crea una comunità",
"create_community": "Crea Comunità",
"remove_community": "Rimuovi Comunità",
"subscribed_to_communities": "Iscritto alle <1>comunità</1>",
"remove_community": "Elimina Comunità",
"subscribed_to_communities": "Iscritt* alle <1>comunità</1>",
"trending_communities": "<1>Comunità</1> in crescita",
"list_of_communities": "Lista di comunità",
"list_of_communities": "Elenco di comunità",
"number_of_communities": "{{count}} Comunità",
"number_of_communities_plural": "{{count}} Comunità",
"community_reqs": "minuscole, trattini bassi e nessuno spazio.",
"edit": "modifica",
"reply": "rispondi",
@ -33,7 +36,7 @@
"lock": "blocca",
"sticky": "evidenzia",
"unsticky": "rimuovi evidenza",
"link": "link",
"link": "collegamento",
"mod": "moderatore",
"mods": "moderatori",
"moderates": "Moderatore di",
@ -46,32 +49,36 @@
"remove_as_admin": "rimuovi come amministratore",
"appoint_as_admin": "nomina come amministratore",
"remove": "rimuovi",
"removed": "rimosso",
"removed": "rimosso da un moderatore",
"locked": "bloccato",
"stickied": "evidenziato",
"reason": "Ragione",
"reason": "Motivo",
"mark_as_read": "segna come letto",
"mark_as_unread": "segna come non letto",
"delete": "cancella",
"deleted": "cancellato",
"deleted": "eliminato dal creatore",
"delete_account": "Cancella Account",
"delete_account_confirm": "Attenzione: stai per cancellare permanentemente tutti i tuoi dati. Inserisci la tua password per confermare questa azione.",
"restore": "ripristina",
"ban": "ban",
"ban_from_site": "banna dal sito",
"unban": "rimuovi ban",
"unban_from_site": "rimuove il ban dal sito",
"banned": "bannato",
"ban": "espulsione",
"ban_from_site": "espulsione dal sito",
"unban": "rimuovi espulsione",
"unban_from_site": "rimuove l'espulsione dal sito",
"banned": "espulso",
"save": "salva",
"unsave": "rimuovi",
"create": "crea",
"creator": "autore",
"username": "Username",
"email_or_username": "Email o Username",
"number_of_users": "{{count}} Utenti",
"number_of_subscribers": "{{count}} Iscritti",
"number_of_points": "{{count}} Punti",
"number_online": "{{count}} Utenti Online",
"username": "Nome Utente",
"email_or_username": "Email o Nome Utente",
"number_of_users": "{{count}} Utente",
"number_of_users_plural": "{{count}} Utenti",
"number_of_subscribers": "{{count}} Iscritto",
"number_of_subscribers_plural": "{{count}} Iscritti",
"number_of_points": "{{count}} Punto",
"number_of_points_plural": "{{count}} Punti",
"number_online": "{{count}} Utente Connesso",
"number_online_plural": "{{count}} Utenti Connessi",
"name": "Nome",
"title": "Titolo",
"category": "Categoria",
@ -105,11 +112,11 @@
"search": "Cerca",
"overview": "Panoramica",
"view": "Visualizza",
"logout": "Logout",
"login_sign_up": "Login / Iscriviti",
"login": "Login",
"logout": "Esci",
"login_sign_up": "Accedi / Iscriviti",
"login": "Accedi",
"sign_up": "Iscriviti",
"notifications_error": "Le notifiche desktop non sono supportate sul tuo browser. Prova Firefox o Chrome.",
"notifications_error": "Le notifiche desktop non sono disponibili sul tuo browser. Prova Firefox o Chrome.",
"unread_messages": "Messaggi Non Letti",
"password": "Password",
"verify_password": "Verifica Password",
@ -118,75 +125,75 @@
"expires": "Scade",
"url": "URL",
"body": "Contenuto",
"copy_suggested_title": "copia titolo suggerito: {{title}}",
"copy_suggested_title": "copia titolo consigliato: {{title}}",
"community": "Comunità",
"expand_here": "Visualizza qui",
"subscribe_to_communities": "Iscriviti ad una <1>comunità</1>.",
"chat": "Chat",
"recent_comments": "Commenti Recenti",
"no_results": "Nessun risultato.",
"setup": "Setup",
"lemmy_instance_setup": "Setup dell'istanza di Lemmy",
"setup": "Configura",
"lemmy_instance_setup": "Configurazione dell'istanza di Lemmy",
"setup_admin": "Imposta Amministratore del Sito",
"your_site": "il tuo sito",
"modified": "modificato",
"nsfw": "NSFW",
"show_nsfw": "Mostra contenuto NSFW",
"theme": "Tema",
"sponsors": "Sponsors",
"sponsors_of_lemmy": "Sponsors di Lemmy",
"sponsor_message": "Lemmy è un software gratuito e <1>open-source</1>, il che significa nessuna pubblicità, monetizzazione o investitori esterni, per sempre. Le tue donazioni supportano direttamente lo sviluppo full-time del progetto. Si ringraziano le seguenti persone:",
"support_on_patreon": "Supporta su Patreon",
"support_on_liberapay": "Supporta su Liberapay",
"general_sponsors": "I \"General Sponsors\" sono quelli che hanno investito dai 10$ ai 39$ su Lemmy.",
"crypto": "Crypto",
"sponsors": "Sponsor",
"sponsors_of_lemmy": "Sponsor di Lemmy",
"sponsor_message": "Lemmy è software libero e <1>open-source</1>, senza nessuna pubblicità, monetizzazione o investitori esterni, per sempre. Le tue donazioni sostengono direttamente lo sviluppo full-time del progetto. Si ringraziano le seguenti persone:",
"support_on_patreon": "Sostieni su Patreon",
"support_on_liberapay": "Sostieni su Liberapay",
"general_sponsors": "Gli sponsor generali sono quelli che hanno investito dai 10$ ai 39$ su Lemmy.",
"crypto": "Criptomonete",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
"monero": "Monero",
"code": "Code",
"code": "Codice",
"joined": "Iscritto da",
"by": "di",
"to": "su",
"transfer_community": "trasferisci comunità",
"transfer_site": "trasferisci sito",
"are_you_sure": "sei sicuro?",
"yes": "si",
"yes": "sì",
"no": "no",
"powered_by": "Powered by",
"landing_0": "Lemmy è un <1>aggregatore di link</1> / alternativa a reddit, creato per integrarsi con il <2>fediverse</2>. <3></3>È self-hosted, i commenti sono aggiornati in tempo reale ed è molto piccolo (<4>~80kB</4>). La Federazione con la rete ActivityPub sarà implementata nel futuro. <5></5>Questa versione è una <6>beta molto giovane</6> e molte funzionalità sono incomplete o mancanti. <7></7>Suggerisci nuove funzionalità o segnala errori a <8>questa pagina.</8><9></9>Sviluppato con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"powered_by": "Offerto da",
"landing": "Lemmy è un <1>aggregatore di link</1> / alternativa a reddit, creato per integrarsi con il <2>fediverso</2>. <3></3>È self-hosted, i commenti sono aggiornati in tempo reale ed è molto piccolo (<4>~80kB</4>). La federazione con la rete ActivityPub sarà implementata nel futuro. <5></5>Questa versione è una <6>beta molto giovane</6> e molte funzionalità sono incomplete o mancanti. <7></7>Suggerisci nuove funzionalità o segnala errori a <8>questa pagina.</8><9></9>Sviluppato con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Non hai effettuato l'accesso.",
"community_ban": "Sei stato bannato da questa comunità.",
"site_ban": "Sei stato bannato dal sito",
"community_ban": "Sei stato escluso da questa comunità.",
"site_ban": "Sei stato escluso dal sito",
"couldnt_create_comment": "Impossibile creare il commento.",
"couldnt_like_comment": "Impossibile mettere 'Mi piace' al commento.",
"couldnt_like_comment": "Impossibile apprezzare il commento.",
"couldnt_update_comment": "Impossibile aggiornare il commento.",
"couldnt_save_comment": "Impossibile salvare il commento.",
"no_comment_edit_allowed": "Non sei autorizzato a modificare il commento.",
"no_post_edit_allowed": "Non sei autorizzato a modificare il post.",
"no_post_edit_allowed": "Non sei autorizzato a modificare la pubblicazione.",
"no_community_edit_allowed": "Non sei autorizzato a modificare la comunità.",
"couldnt_find_community": "Impossibile trovare la comunità.",
"couldnt_update_community": "Impossibile aggiornare la comunità.",
"community_already_exists": "La comunità esiste già.",
"community_moderator_already_exists": "Questo utente è già moderatore della comunità.",
"community_follower_already_exists": "Questo utente è già moderatore della comunità.",
"community_user_already_banned": "L'utente della comunità è già stato bannato.",
"couldnt_create_post": "Impossibile creare il post.",
"couldnt_like_post": "Impossibile mettere 'Mi piace' post.",
"couldnt_find_post": "Impossibile trovare il post.",
"couldnt_get_posts": "Impossibile recuperare i post",
"couldnt_update_post": "Impossibile aggiornare il post",
"couldnt_save_post": "Impossibile salvare il post.",
"community_user_already_banned": "L'utente della comunità è già stato espulso.",
"couldnt_create_post": "Impossibile creare la pubblicazione.",
"couldnt_like_post": "Impossibile apprezzare la pubblicazione.",
"couldnt_find_post": "Impossibile trovare la pubblicazione.",
"couldnt_get_posts": "Impossibile recuperare le pubblicazioni",
"couldnt_update_post": "Impossibile aggiornare la pubblicazione",
"couldnt_save_post": "Impossibile salvare la pubblicazione.",
"no_slurs": "Niente offese.",
"not_an_admin": "Non un amministratore.",
"site_already_exists": "Il sito esiste già.",
"couldnt_update_site": "Impossibile aggiornare il sito.",
"couldnt_find_that_username_or_email": "L'username o la email non sono stati trovati.",
"couldnt_find_that_username_or_email": "Il nome utente o l'email non sono stati trovati.",
"password_incorrect": "Password non corretta.",
"passwords_dont_match": "Le password non corrispondono.",
"admin_already_created": "Spiacente, esiste già un amministratore.",
"user_already_exists": "L'utente esiste già.",
"couldnt_update_user": "Impossibile aggiornare l'utente.",
"system_err_login": "Si è verificato un errore. Prova ad effettuare nuovamente il login.",
"system_err_login": "Si è verificato un errore. Prova ad effettuare nuovamente l'accesso.",
"more": "altro",
"message": "Messaggio",
"avatar": "Avatar",
@ -200,14 +207,54 @@
"new_password": "Nuova Password",
"private_message_disclaimer": "Attenzione: i messaggi privati su Lemmy non sono sicuri. Crea un account su <1>Riot.im</1> per una messaggistica sicura.",
"language": "Lingua",
"enable_downvotes": "Abilita Downvote",
"enable_downvotes": "Abilita voti negativi",
"enable_nsfw": "Abilita NSFW",
"donate_to_lemmy": "Dona a Lemmy",
"donate": "Dona",
"from": "da",
"archive_link": "link archivio",
"archive_link": "archivia collegamento",
"matrix_user_id": "Utente Matrix",
"downvotes_disabled": "Downvote disabilitati",
"post_title_too_long": "Titolo del post troppo lungo.",
"email_already_exists": "Indirizzo email già presente."
"downvotes_disabled": "Voti negativi disabilitati",
"post_title_too_long": "Titolo della pubblicazione troppo lungo.",
"email_already_exists": "Indirizzo email già presente.",
"cross_posted_to": "pubblicato pure su: ",
"support_on_open_collective": "Sostieni su OpenCollective",
"admin_settings": "Impostazioni per Admin",
"site_config": "Configurazione del sito",
"banned_users": "Utenti Espulsi",
"emoji_picker": "Selettore Emoji",
"send_message": "Invia Messaggio",
"create_private_message": "Crea Messaggio Privato",
"send_secure_message": "Invia Messaggio Sicuro",
"password_change": "Cambia password",
"reset_password_mail_sent": "Un'email è stata inviata per resettare la tua password.",
"no_email_setup": "Questo server non ha impostato un'email correttamente.",
"send_notifications_to_email": "Invia notifiche via email",
"upvote": "Voto Positivo",
"sorting_help": "aiuto ordinamento",
"old": "Vecchi",
"browser_default": "Default del browser",
"couldnt_get_comments": "Impossibile ottenere i commenti.",
"couldnt_update_private_message": "Impossibile aggiornare un messaggio privato.",
"block_leaving": "Sei sicuro di voler uscire?",
"couldnt_create_private_message": "Impossibile creare un messaggio privato.",
"show_context": "Mostra contesto",
"site_saved": "Sito Salvato.",
"downvote": "Voto Negativo",
"number_of_upvotes": "{{count}} Voto Positivo",
"number_of_upvotes_plural": "{{count}} Voti Positivi",
"number_of_downvotes": "{{count}} Voto Negativo",
"number_of_downvotes_plural": "{{count}} Voti Negativi",
"open_registration": "Registrazione Aperta",
"logged_in": "Connesso.",
"registration_closed": "Registrazione Chiusa",
"no_private_message_edit_allowed": "Non hai i permessi per modificare un messaggio privato.",
"time": "Tempo",
"action": "Azione",
"silver_sponsors": "Gli sponsor generali sono quelli che hanno investito 40$ su Lemmy.",
"invalid_community_name": "Nome non valido.",
"click_to_delete_picture": "Clicca per eliminare la foto.",
"picture_deleted": "Foto eliminata.",
"select_a_community": "Seleziona una comunità",
"invalid_username": "Username non valido."
}

View file

@ -144,7 +144,7 @@
"enable_nsfw": "閲覧注意を有効化",
"url": "URL",
"body": "本文",
"copy_suggested_title": "タイトルの提案をコピーする: {{title}}",
"copy_suggested_title": "提案されたタイトルをコピーする: {{title}}",
"community": "コミュニティ",
"expand_here": "拡大表示",
"subscribe_to_communities": "<1>コミュニティ</1>をいくつか登録してみましょう。",
@ -233,5 +233,7 @@
"system_err_login": "システムエラーが発生しました。一度ログアウトして、再度ログインをお試しください。",
"couldnt_create_private_message": "プライベートメッセージが作成されない。",
"no_private_message_edit_allowed": "プライベートメッセージの編集許可がありません。",
"couldnt_update_private_message": "プライベートメッセージが更新されない。"
"couldnt_update_private_message": "プライベートメッセージが更新されない。",
"couldnt_like_comment": "コメントが「いいね」できない。",
"couldnt_like_post": "投稿が「いいね」できない。"
}

View file

@ -221,5 +221,27 @@
"couldnt_like_post": "პოსტის მოწონება ვერ მოხერხდა.",
"community_moderator_already_exists": "ამ თემის მოდერატორი უკვე არსებობს.",
"couldnt_create_post": "პოსტი ვერ შეიქმნა.",
"post_title_too_long": "პოსტის სათაური ძალიან გრძელია."
"post_title_too_long": "პოსტის სათაური ძალიან გრძელია.",
"admin_settings": "ადმინი პარამეტრები",
"site_config": "საიტის კონფიგურაცია",
"banned_users": "გაშავებული მომხმარებლები",
"support_on_open_collective": "Support on OpenCollective",
"site_saved": "Site Saved.",
"couldnt_find_post": "პოსტი ვერ მოიძებნა.",
"couldnt_get_posts": "პოსტები არ არის.",
"couldnt_update_post": "პოსტი ვერ განახლდა",
"couldnt_save_post": "პოსტის დასეივება ვერ მოხერხდა.",
"monero": "Monero",
"no_slurs": "No slurs.",
"not_an_admin": "ადმინი არ არის",
"site_already_exists": "Site already exists.",
"couldnt_update_site": "Couldn't update site.",
"couldnt_find_that_username_or_email": "სახელი ან ელ-პოსტა ვერ მოიძებნა.",
"password_incorrect": "პაროლი არასწორია .",
"passwords_dont_match": "პაროლები იგივი არ არის.",
"admin_already_created": "ადმინი უკვე არსებობს.",
"user_already_exists": "მომხმარებელი უკვე არსებობს.",
"email_already_exists": "ელ-პოსტა უკვე არსებობს.",
"couldnt_update_user": "მომხმარებლის განახლება ვერ მოხერხდა.",
"system_err_login": "ერორი. თავიდან შემოსვლა ცადეთ."
}

View file

@ -4,13 +4,15 @@
"no_posts": "Geen posts.",
"create_a_post": "Plaats een post",
"create_post": "Plaats post",
"number_of_posts": "{{count}} posts",
"number_of_posts": "{{count}} post",
"number_of_posts_plural": "{{count}} posts",
"posts": "posts",
"related_posts": "Deze posts kunnen gerelateerd zijn",
"cross_posts": "Deze link is ook geplaatst in:",
"cross_post": "cross-post",
"comments": "Reacties",
"number_of_comments": "{{count}} reacties",
"number_of_comments": "{{count}} reactie",
"number_of_comments_plural": "{{count}} reacties",
"remove_comment": "Verwijder reactie",
"communities": "Communities",
"users": "Gebruikers",
@ -20,8 +22,9 @@
"subscribed_to_communities": "Geabonneerd op <1>communities</1>",
"trending_communities": "Populaire <1>communities</1>",
"list_of_communities": "Lijst van communities",
"number_of_communities": "{{count}} communities",
"community_reqs": "kleine letters, onderstrepingsteken en geen spaties",
"number_of_communities": "{{count}} community",
"number_of_communities_plural": "{{count}} community's",
"community_reqs": "kleine letters, onderstrepingsteken en geen spaties.",
"edit": "bewerk",
"reply": "reageer",
"cancel": "Annuleer",
@ -57,9 +60,12 @@
"create": "maak",
"username": "Gebruikersnaam",
"email_or_username": "E-mail of gebruikersnaam",
"number_of_users": "{{count}} gebruikers",
"number_of_subscribers": "{{count}} abonnees",
"number_of_points": "{{count}} punten",
"number_of_users": "{{count}} gebruiker",
"number_of_users_plural": "{{count}} gebruikers",
"number_of_subscribers": "{{count}} abonnee",
"number_of_subscribers_plural": "{{count}} abonnees",
"number_of_points": "{{count}} punt",
"number_of_points_plural": "{{count}} punten",
"name": "Naam",
"title": "Titel",
"category": "Categorie",
@ -95,8 +101,7 @@
"login_sign_up": "Log in / Aanmelden",
"login": "Log in",
"sign_up": "Aanmelden",
"notifications_error":
"Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.",
"notifications_error": "Bureabladberichten niet beschikbaar in je browser. Probeer Firefox of Chrome.",
"unread_messages": "Ongelezen berichten",
"password": "Wachtwoord",
"verify_password": "Herhaal wachtwoord",
@ -111,7 +116,7 @@
"subscribe_to_communities": "Abonneer je op een paar <1>communities</1>.",
"chat": "Praat",
"recent_comments": "Recente reacties",
"no_results": "Geen resultaten",
"no_results": "Geen resultaten.",
"setup": "Installatie",
"lemmy_instance_setup": "Installatie van Lemmy-instantie",
"setup_admin": "Maak een administrator",
@ -121,12 +126,10 @@
"show_nsfw": "Laat NSFW-inhoud zien",
"sponsors": "Sponsoren",
"sponsors_of_lemmy": "Sponsoren van Lemmy",
"sponsor_message":
"Lemmy is vrije, <1>open-source</1> software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:",
"sponsor_message": "Lemmy is vrije, <1>open-source</1> software, dus zonder reclame, winstoogmerk en durfkapitaal, punt. Jouw donaties gaan direct naar de full-time-ontwikkeling van het project. Met veel dank aan de volgende mensen:",
"support_on_patreon": "Ondersteun op Patreon",
"support_on_liberapay": "Ondersteun op Liberapay",
"general_sponsors":
"Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.",
"general_sponsors": "Algemene sponsors zijn sponsors die tussen de $10 en $39 hebben gegeven aan Lemmy.",
"crypto": "Cryptovaluta",
"bitcoin": "Bitcoin",
"ethereum": "Ethereum",
@ -141,11 +144,10 @@
"yes": "ja",
"no": "nee",
"powered_by": "Mogelijk gemaakt door",
"landing_0":
"Lemmy is een <1>linkverzameler</1> / reddit-alternatief, bedoeld om in de <2>fediverse</2> te werken.<3></3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB</4>). Federatie in hte ActivityPub-netwerk is gepland. <5></5>Dit is een <6>erg vroege bèta-versie</6>, en een hoop functies zijn stuk of afwezig. <7></7>Stel nieuwe functies voor of meldt fouten <8>hier</8>.<9></9>Gemaakt met <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> en <13>Typescript</13>.",
"landing_0": "Lemmy is een <1>linkverzameler</1> / reddit-alternatief, bedoeld om in de <2>fediverse</2> te werken.<3></3>Lemmy kan door om het even wie gehost worden, heeft live-bijgewerkte reacties en is superklein (<4>ca. 80 kB</4>). Federatie in hte ActivityPub-netwerk is gepland. <5></5>Dit is een <6>erg vroege bèta-versie</6>, en een hoop functies zijn stuk of afwezig. <7></7>Stel nieuwe functies voor of meldt fouten <8>hier</8>.<9></9>Gemaakt met <10>Rust</10>, <11>Actix</11>, <12>Inferno</12> en <13>Typescript</13>.",
"not_logged_in": "Niet ingelogd.",
"community_ban": "Je bent verbannen uit deze community.",
"site_ban": "Je bent verbannen van deze site.",
"site_ban": "Je bent verbannen van deze site",
"couldnt_create_comment": "Kon reactie niet maken.",
"couldnt_like_comment": "Kon reactie niet leuk vinden.",
"couldnt_update_comment": "Kon reactie niet bijwerken.",
@ -162,22 +164,20 @@
"couldnt_create_post": "Kon post niet maken.",
"couldnt_like_post": "Kon post niet leuk vinden.",
"couldnt_find_post": "Kon post niet vinden.",
"couldnt_get_posts": "Kon posts niet ophalen.",
"couldnt_update_post": "Kon post niet bijwerken.",
"couldnt_get_posts": "Kon posts niet ophalen",
"couldnt_update_post": "Kon post niet bijwerken",
"couldnt_save_post": "Kon post niet opslaan.",
"no_slurs": "Geen beledigingen.",
"not_an_admin": "Niet een beheerder.",
"site_already_exists": "Site bestaat al.",
"couldnt_update_site": "Kon site niet bijwerken.",
"couldnt_find_that_username_or_email":
"Kon gebruikersnaam of e-mailadres niet vinden.",
"couldnt_find_that_username_or_email": "Kon gebruikersnaam of e-mailadres niet vinden.",
"password_incorrect": "Wachtwoord incorrect.",
"passwords_dont_match": "Wachtwoorden zijn niet gelijk.",
"admin_already_created": "Sorry, er is al een beheerder.",
"user_already_exists": "Gebruiker bestaat al.",
"couldnt_update_user": "Kon gebruiker niet bijwerken.",
"system_err_login":
"Systeemfout. Probeer uit te loggen en weer in te loggen.",
"system_err_login": "Systeemfout. Probeer uit te loggen en weer in te loggen.",
"preview": "voorbeeld",
"upload_image": "Afbeelding uploaden",
"avatar": "Avatar",
@ -193,16 +193,17 @@
"delete_account_confirm": "Waarschuwing: dit zal al uw data voorgoed verwijderen, vul uw wachtwoord in om te bevestigen.",
"banned": "verbannen",
"creator": "auteur",
"number_online": "{{count}} gebruikers online",
"number_online": "{{count}} gebruiker online",
"number_online_plural": "{{count}} gebruikers online",
"docs": "Documentatie",
"replies": "Reacties",
"mentions": "vermeldingen",
"old_password": "Oud wachtwoord",
"forgot_password": "wachtwoord vergeten",
"reset_password_mail_sent": "Stuur een email om uw wachtwoord te resetten",
"reset_password_mail_sent": "Stuurde een email om uw wachtwoord te resetten.",
"password_change": "Wachtwoord aanpassen",
"new_password": "Nieuw wachtwoord",
"no_email_setup": "Deze server heeft email niet correct opgezet",
"no_email_setup": "Deze server heeft email niet correct opgezet.",
"send_notifications_to_email": "Stuur meldingen naar je email",
"language": "Taal",
"browser_default": "Browser standaard",
@ -220,13 +221,35 @@
"message_sent": "Bericht verstuurd",
"messages": "Berichten",
"matrix_user_id": "Matrix gebruikers-id",
"private_message_disclaimer": "Waarschuwing: Privé berichten in Lemmy zijn niet beveiligd. Maak een account aan op <1>Riot.im</1> om veilig te communiceren",
"private_message_disclaimer": "Waarschuwing: Privé berichten in Lemmy zijn niet beveiligd. Maak een account aan op <1>Riot.im</1> om veilig te communiceren.",
"donate_to_lemmy": "Doneer aan Lemmy",
"donate": "Doneer",
"from": "van",
"logged_in": "Ingelogd",
"email_already_exists": "Email bestaat al",
"couldnt_create_private_message": "Kan beveiligd bericht niet maken",
"no_private_message_edit_allowed": "Niet toegestaan om privé berichten te wijzigen",
"couldnt_update_private_message": "Kan beveiligd bericht niet bijwerken"
"logged_in": "Ingelogd.",
"email_already_exists": "Email bestaat al.",
"couldnt_create_private_message": "Kan beveiligd bericht niet maken.",
"no_private_message_edit_allowed": "Niet toegestaan om privé berichten te wijzigen.",
"couldnt_update_private_message": "Kan beveiligd bericht niet bijwerken.",
"cross_posted_to": "gecross-post naar: ",
"more": "meer",
"site_config": "Configuratie van de website",
"sorting_help": "hulp bij sorteren",
"show_context": "Toon context",
"support_on_open_collective": "Ondersteun op OpenCollective",
"site_saved": "Pagina opgeslaan.",
"emoji_picker": "Emojikiezer",
"upvote": "Upvote",
"number_of_upvotes": "{{count}} upvote",
"number_of_upvotes_plural": "{{count}} upvotes",
"downvote": "Downvote",
"number_of_downvotes": "{{count}} downvote",
"number_of_downvotes_plural": "{{count}} downvotes",
"couldnt_get_comments": "Kon reacties niet ophalen.",
"time": "Tijd",
"action": "Actie",
"block_leaving": "Weet je zeker dat je weg wilt gaan?",
"silver_sponsors": "Zilveren sponsors zijn sponsors die $40 hebben gegeven aan Lemmy.",
"post_title_too_long": "Posttitel te lang.",
"admin_settings": "Beheerderinstellingen",
"banned_users": "Verbannen gebruikers"
}

View file

@ -65,14 +65,14 @@
"remove_as_admin": "wycofaj uprawnienia administratora",
"appoint_as_admin": "przyznaj uprawnienia administratora",
"remove": "usuń",
"removed": "usunięte",
"removed": "usunięte przez moderatora",
"locked": "zablokowane",
"stickied": "przyklejone",
"reason": "Powód",
"mark_as_read": "zaznacz jako przeczytane",
"mark_as_unread": "zaznacz jako nieprzeczytane",
"delete": "usuń",
"deleted": "usunięte",
"deleted": "usunięte przez autora",
"delete_account": "Usuń Konto",
"delete_account_confirm": "Ostrzeżenie: twoje dane zostaną bezpowrotnie usunięte. Wpisz swoje hasło aby potwierdzić.",
"restore": "przywróć",
@ -189,7 +189,7 @@
"theme": "Motyw",
"sponsors": "Sponsorzy",
"sponsors_of_lemmy": "Sponsorzy projektu Lemmy",
"sponsor_message": "Lemmy jest wolnym, <1>otwartoźródłowym</1> oprogramowaniem, co oznacza zero reklam, opłat, czy innych form kapitalizacji, od zawsze na zawsze. Twoje darowizny idą bezpośrednio na rozwój projektu w pełno-etatowym wymiarze. Specjalne wyrazy podziękowania dla następujących osób:",
"sponsor_message": "Lemmy jest wolnym, <1>otwartoźródłowym</1> oprogramowaniem, bez reklam, opłat, czy innych form kapitalizacji, od zawsze na zawsze. Twoje darowizny idą bezpośrednio na rozwój projektu w pełno-etatowym wymiarze. Specjalne wyrazy podziękowania dla następujących osób:",
"support_on_patreon": "Wspieraj w serwisie Patreon",
"support_on_liberapay": "Wspieraj na Liberapay",
"donate_to_lemmy": "Przekaż datek na Lemmiego",
@ -209,7 +209,7 @@
"are_you_sure": "na pewno?",
"no": "nie",
"powered_by": "Powered by",
"landing": "Lemmy jest <1>agregatorem linków</1> / alternatywą dla reddita. Jest przeznaczony do działania w ramach cyfrowej przestrzeni nazywanej <2>fediverse<2>. <3></3>Opiera się na samodzielnym hostingu, posiada aktualizowane na żywo wątki z komentarzami, i zajmuje bardzo mało miejsce (<4>~80kB</4>). Federacja w ramach sieci ActivityPub jest w planach. <5></5>Ta wersja jest <6>bardzo wczesną wersją beta</6>, co oznacza, że wiele funkcji nadal nie działa tak jak powinny. <7></7><8>Pod tym adresem</8> można sugerować nową funkcjonalność i zgłaszać błędy.<9></9>Stworzono z wykorzystaniem <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"landing_0": "Lemmy jest <1>agregatorem linków</1> / alternatywą dla reddita. Jest przeznaczony do działania w ramach cyfrowej przestrzeni nazywanej <2>fediverse</2>. <3></3>Opiera się na samodzielnym hostingu, posiada aktualizowane na żywo wątki z komentarzami, i zajmuje bardzo mało miejsce (<4>~80kB</4>). Federacja w ramach sieci ActivityPub jest w planach. <5></5>Ta wersja jest <6>bardzo wczesną wersją beta</6>, co oznacza, że wiele funkcji nadal nie działa tak jak powinny. <7></7><8>Pod tym adresem</8> można sugerować nową funkcjonalność i zgłaszać błędy.<9></9>Stworzono z wykorzystaniem <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"not_logged_in": "Nie jesteś zalogowana/y.",
"logged_in": "Zalogowano.",
"community_ban": "Zostałaś/eś zbanowana/y z tej społeczności.",
@ -253,5 +253,14 @@
"time": "Czas",
"action": "Akcja",
"block_leaving": "Czy na pewno chcesz wyjść?",
"show_context": "Pokaż kontekst"
"show_context": "Pokaż kontekst",
"site_config": "Ustawienia Witryny",
"banned_users": "Zbanowani Użytkownicy",
"support_on_open_collective": "Wspieraj na OpenCollective",
"site_saved": "Witryna Zapisana.",
"admin_settings": "Ustawienia Administratora",
"emoji_picker": "Wybór Emoji",
"silver_sponsors": "Srebrni Sponsorzy to ci, którzy wpłacili co najmniej $40 na Lemmiego.",
"select_a_community": "Wybierz społeczność",
"invalid_username": "Nieprawidłowa nazwa użytkownika."
}

View file

@ -58,9 +58,15 @@
"create": "создать",
"username": "Имя пользователя",
"email_or_username": "Электронная почта или имя пользователя",
"number_of_users": "{{count}} пользователей",
"number_of_subscribers": "{{count}} подписчиков",
"number_of_points": "{{count}} баллов",
"number_of_users_0": "{{count}} пользователь",
"number_of_users_1": "{{count}} пользователя",
"number_of_users_2": "{{count}} пользователей",
"number_of_subscribers_0": "{{count}} подписчик",
"number_of_subscribers_1": "{{count}} подписчика",
"number_of_subscribers_2": "{{count}} подписчиков",
"number_of_points_0": "{{count}} балл",
"number_of_points_1": "{{count}} балла",
"number_of_points_2": "{{count}} баллов",
"name": "Имя",
"title": "Название",
"category": "Категория",
@ -170,7 +176,7 @@
"avatar": "Аватар",
"show_avatars": "Показать Аватары",
"formatting_help": "Помощь в верстке текста",
"sticky": "",
"sticky": "приклеить",
"stickied": "закрепленный пост",
"delete_account": "Удалить аккаунт",
"delete_account_confirm": "Предупреждение: это действие полностью уничтожит все данные вашего аккаунта. Введите свой пароль для подтверждения.",
@ -214,5 +220,32 @@
"number_of_communities_2": "{{count}} сообществ",
"creator": "автор",
"old": "Старое",
"to": "в"
"to": "в",
"admin_settings": "Настройки админа",
"banned_users": "Забаненные Пользователи",
"support_on_open_collective": "Поддержка на OpenCollective",
"site_saved": "Сайт Сохранен.",
"enable_nsfw": "Включить NSFW",
"donate": "Пожертвование",
"unsticky": "отклеить",
"site_config": "Конфигурация сайта",
"banned": "забаненный",
"password_change": "Смена пароля",
"no_email_setup": "Этот сервер неправильно настроил электронную почту.",
"matrix_user_id": "Матрица пользователя",
"are_you_sure": "вы уверены?",
"archive_link": "архивировать ссылку",
"logged_in": "Войти в систему.",
"couldnt_get_comments": "Не удалось получить комментарии.",
"from": "от",
"transfer_site": "трансфер сайт",
"show_context": "Показать контекст",
"email_already_exists": "E-mail уже существует.",
"couldnt_create_private_message": "Не удалось создать личное сообщение.",
"no_private_message_edit_allowed": "Не разрешается редактировать личное сообщение.",
"couldnt_update_private_message": "Не удалось обновить личное сообщение.",
"block_leaving": "Вы уверены, что хотите покинуть?",
"number_online_0": "{{count}} Пользователь онлайн",
"number_online_1": "{{count}} Пользователя онлайн",
"number_online_2": "{{count}} Пользователей онлайн"
}

1
ui/translations/tr.json vendored Normal file
View file

@ -0,0 +1 @@
{}

1
ui/translations/uk.json vendored Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -1,5 +1,5 @@
{
"post": "",
"post": "帖",
"remove_post": "移除帖子",
"no_posts": "没有帖子。",
"create_a_post": "创建新帖子",
@ -35,13 +35,13 @@
"remove_as_admin": "移除管理权限",
"appoint_as_admin": "添加管理权限",
"remove": "移除",
"removed": "已移除",
"removed": "已被管理员移除",
"locked": "已加锁",
"reason": "原因",
"mark_as_read": "标记未读",
"mark_as_unread": "标记已读",
"delete": "删除",
"deleted": "已删除",
"deleted": "作者已删除",
"restore": "恢复",
"ban": "禁止",
"ban_from_site": "禁止此站点",
@ -100,9 +100,9 @@
"url": "网址",
"body": "内容",
"copy_suggested_title": "复制建议的标题: {{title}}",
"community": "节点",
"community": "社群",
"expand_here": "展开",
"subscribe_to_communities": "订阅一些 <1>节点</1>.",
"subscribe_to_communities": "订阅一些 <1>社群</1>.",
"chat": "聊天",
"no_results": "没有结果.",
"setup": "设置",
@ -122,23 +122,23 @@
"code": "代码",
"joined": "已加入",
"powered_by": "保留所有权利",
"landing_0": "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
"landing": "Lemmy是一个 <1>链接聚合器</1> / reddit的替代选择为在<2>fediverse</2>上使用而设计。<3></3>它支持自托管, 提供实时更新的讨论串, 并且十分轻量 (<4>约80kB</4>)。对ActivityPub网络的联邦整合已经在规划之中。<5></5>这是一个<6>非常早期的beta版本</6>,许多功能还不能使用或还未开发完成。 <7></7>请到 <8>这里</8>提出新功能或报告bug。<9></9>由<10>Rust</10>、 <11>Actix</11>、<12>Inferno</12>、<13>Typescript</13>写成。",
"not_logged_in": "未登录.",
"community_ban": "你已被此社群拉黑。",
"site_ban": "你已被本站拉黑",
"couldnt_create_comment": "不能创建评论.",
"couldnt_create_comment": "无法创建评论。",
"couldnt_like_comment": "无法点赞评论。",
"couldnt_update_comment": "不能更新评论.",
"couldnt_save_comment": "不能保存评论.",
"couldnt_update_comment": "无法更新评论。",
"couldnt_save_comment": "无法保存评论。",
"no_comment_edit_allowed": "没有编辑评论的权限。",
"no_post_edit_allowed": "没有编辑帖子的权限。",
"no_community_edit_allowed": "没有编辑节点的权限。",
"couldnt_find_community": "无法找到节点。",
"couldnt_update_community": "无法更新节点。",
"community_already_exists": "节点已存在。",
"community_moderator_already_exists": "节点监管人已存在。",
"community_follower_already_exists": "节点追随者已存在。",
"community_user_already_banned": "节点用户已禁止。",
"no_community_edit_allowed": "没有编辑社群的权限。",
"couldnt_find_community": "无法找到社群。",
"couldnt_update_community": "无法更新社群。",
"community_already_exists": "社群已存在。",
"community_moderator_already_exists": "社群监管人已存在。",
"community_follower_already_exists": "社群关注者已存在。",
"community_user_already_banned": "社群用户已被禁止。",
"couldnt_create_post": "无法创建帖子。",
"couldnt_like_post": "无法点赞帖子。",
"couldnt_find_post": "无法找到帖子。",
@ -156,14 +156,14 @@
"user_already_exists": "用户已存在。",
"couldnt_update_user": "无法更新用户。",
"system_err_login": "系统错误。请尝试注销后重新登入。",
"nsfw": "少儿不宜",
"show_nsfw": "显示少儿不宜内容",
"nsfw": "工作场所不宜",
"show_nsfw": "显示工作场所不宜内容",
"theme": "主题",
"from": "来自",
"from": "",
"donate_to_lemmy": "向Lemmy捐赠",
"donate": "捐赠",
"monero": "门罗币",
"to": "",
"to": "发布到",
"are_you_sure": "你确定吗?",
"yes": "是",
"no": "否",
@ -175,7 +175,7 @@
"more": "更多",
"preview": "预览",
"upload_image": "上传图片",
"enable_nsfw": "允许少儿不宜内容",
"enable_nsfw": "允许工作场所不宜内容",
"show_avatars": "显示头像",
"avatar": "头像",
"formatting_help": "格式帮助",
@ -213,7 +213,7 @@
"private_message_disclaimer": "警告Lemmy的私信功能并不安全。想要进行安全的信息传递请在 <1>Riot.im</1>上创建账号。",
"send_notifications_to_email": "向邮箱发送通知",
"language": "语言",
"browser_default": "默认浏览器",
"browser_default": "浏览器默认语言",
"downvotes_disabled": "点踩功能已禁用",
"enable_downvotes": "启用点踩功能",
"upvote": "点赞",
@ -223,8 +223,8 @@
"open_registration": "开放注册",
"registration_closed": "注册功能已关闭",
"recent_comments": "最新评论",
"by": "来自",
"transfer_community": "节点转让",
"by": "",
"transfer_community": "转让社群",
"transfer_site": "站点转让",
"post_title_too_long": "帖子标题过长。",
"couldnt_get_comments": "无法获取评论。",
@ -235,5 +235,11 @@
"time": "时间",
"action": "行动",
"block_leaving": "确定要离开吗?",
"show_context": "显示上下文"
"show_context": "显示上下文",
"admin_settings": "管理员设置",
"site_config": "网站配置",
"banned_users": "被禁止用户",
"site_saved": "网站已保存",
"emoji_picker": "选择表情",
"invalid_username": "用户名无效"
}