Merge pull request #2 from dessalines/master

Merge upstream changes.
This commit is contained in:
Richie Zhang 2020-01-28 17:46:43 -08:00 committed by GitHub
commit 196d5d77b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 838 additions and 178 deletions

25
README.md vendored
View file

@ -161,22 +161,23 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent
If you'd like to add translations, take a look a look at the [English translation file](ui/src/translations/en.ts). If you'd like to add translations, take a look a look at the [English translation file](ui/src/translations/en.ts).
- Languages supported: English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`). - Languages supported: Catalan, (`ca`), English (`en`), Chinese (`zh`), Dutch (`nl`), Esperanto (`eo`), Finnish (`fi`), French (`fr`), Spanish (`es`), Swedish (`sv`), German (`de`), Russian (`ru`), Italian (`it`).
<!-- translations --> <!-- translations -->
lang | done | missing lang | done | missing
--- | --- | --- ---- | ---- | -------
de | 88% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message ca | 100% | old
eo | 76% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message de | 87% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,old,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
es | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message eo | 75% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message es | 100% | old
it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message fi | 100% | old
nl | 93% | create_private_message,send_secure_message,send_message,message,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message nl | 92% | create_private_message,send_secure_message,send_message,message,old,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,old,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
<!-- translationsstop --> <!-- translationsstop -->
If you'd like to update this report, run: If you'd like to update this report, run:

View file

@ -9,7 +9,7 @@ third_semver=$(echo $new_tag | cut -d "." -f 3)
# Setting the version on the front end # Setting the version on the front end
cd ../../ cd ../../
echo "export let version: string = '$(git describe --tags)';" > "ui/src/version.ts" echo "export const version: string = '$(git describe --tags)';" > "ui/src/version.ts"
git add "ui/src/version.ts" git add "ui/src/version.ts"
# Setting the version on the backend # Setting the version on the backend
echo "pub const VERSION: &str = \"$(git describe --tags)\";" > "server/src/version.rs" echo "pub const VERSION: &str = \"$(git describe --tags)\";" > "server/src/version.rs"

View file

@ -11,7 +11,7 @@ services:
- lemmy_db:/var/lib/postgresql/data - lemmy_db:/var/lib/postgresql/data
restart: always restart: always
lemmy: lemmy:
image: dessalines/lemmy:v0.6.4 image: dessalines/lemmy:v0.6.7
ports: ports:
- "127.0.0.1:8536:8536" - "127.0.0.1:8536:8536"
restart: always restart: always

View file

@ -9,7 +9,7 @@
```bash ```bash
psql -c "create user lemmy with password 'password' superuser;" -U postgres psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres psql -c 'create database lemmy with owner lemmy;' -U postgres
export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
``` ```
#### Running #### Running

2
install.sh vendored
View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
export JWT_SECRET=changeme export JWT_SECRET=changeme
export HOSTNAME=rrr export HOSTNAME=rrr

View file

@ -1 +1 @@
pub const VERSION: &str = "v0.6.4"; pub const VERSION: &str = "v0.6.7";

View file

@ -291,6 +291,7 @@ impl Handler<StandardMessage> for ChatServer {
Err(e) => e.to_string(), Err(e) => e.to_string(),
}; };
println!("Message Sent: {}", msg_out);
MessageResult(msg_out) MessageResult(msg_out)
} }
} }

3
ui/package.json vendored
View file

@ -49,6 +49,7 @@
"fuse-box": "^3.1.3", "fuse-box": "^3.1.3",
"lint-staged": "^10.0.2", "lint-staged": "^10.0.2",
"sortpack": "^2.0.1", "sortpack": "^2.0.1",
"ts-node": "^8.6.2",
"ts-transform-classcat": "^0.0.2", "ts-transform-classcat": "^0.0.2",
"ts-transform-inferno": "^4.0.2", "ts-transform-inferno": "^4.0.2",
"typescript": "^3.7.5" "typescript": "^3.7.5"
@ -59,7 +60,7 @@
"engineStrict": true, "engineStrict": true,
"husky": { "husky": {
"hooks": { "hooks": {
"pre-commit": "ts-node translation_report.ts && git add ../README.md && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged" "pre-commit": "yarn run ts-node translation_report.ts && git add ../README.md && cargo clippy --manifest-path ../server/Cargo.toml --all-targets --all-features -- -D warnings && lint-staged"
} }
}, },
"lint-staged": { "lint-staged": {

View file

@ -96,6 +96,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
className={`form-control ${this.state.previewMode && 'd-none'}`} className={`form-control ${this.state.previewMode && 'd-none'}`}
value={this.state.commentForm.content} value={this.state.commentForm.content}
onInput={linkEvent(this, this.handleCommentContentChange)} onInput={linkEvent(this, this.handleCommentContentChange)}
onPaste={linkEvent(this, this.handleImageUploadPaste)}
required required
disabled={this.props.disabled} disabled={this.props.disabled}
rows={2} rows={2}
@ -208,9 +209,22 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
i.props.onReplyCancel(); i.props.onReplyCancel();
} }
handleImageUploadPaste(i: CommentForm, event: any) {
let image = event.clipboardData.files[0];
if (image) {
i.handleImageUpload(i, image);
}
}
handleImageUpload(i: CommentForm, event: any) { handleImageUpload(i: CommentForm, event: any) {
let file: any;
if (event.target) {
event.preventDefault(); event.preventDefault();
let file = event.target.files[0]; file = event.target.files[0];
} else {
file = event;
}
const imageUploadUrl = `/pictshare/api/upload.php`; const imageUploadUrl = `/pictshare/api/upload.php`;
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);

View file

@ -117,7 +117,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
.viewOnly && 'no-click'}`} .viewOnly && 'no-click'}`}
> >
<button <button
disabled={!UserService.Instance.user}
className={`btn p-0 ${ className={`btn p-0 ${
node.comment.my_vote == 1 ? 'text-info' : 'text-muted' node.comment.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
@ -138,7 +137,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</div> </div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
<button <button
disabled={!UserService.Instance.user}
className={`btn p-0 ${ className={`btn p-0 ${
node.comment.my_vote == -1 ? 'text-danger' : 'text-muted' node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
}`} }`}
@ -761,9 +759,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
} }
handleCommentUpvote(i: CommentNodeI) { handleCommentUpvote(i: CommentNodeI) {
if (UserService.Instance.user) {
this.setState({ this.setState({
upvoteLoading: true, upvoteLoading: true,
}); });
}
let form: CommentLikeForm = { let form: CommentLikeForm = {
comment_id: i.comment.id, comment_id: i.comment.id,
post_id: i.comment.post_id, post_id: i.comment.post_id,
@ -773,9 +773,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
} }
handleCommentDownvote(i: CommentNodeI) { handleCommentDownvote(i: CommentNodeI) {
if (UserService.Instance.user) {
this.setState({ this.setState({
downvoteLoading: true, downvoteLoading: true,
}); });
}
let form: CommentLikeForm = { let form: CommentLikeForm = {
comment_id: i.comment.id, comment_id: i.comment.id,
post_id: i.comment.post_id, post_id: i.comment.post_id,

View file

@ -235,7 +235,7 @@ export class Communities extends Component<any, CommunitiesState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.ListCommunities) { } else if (res.op == UserOperation.ListCommunities) {

View file

@ -261,7 +261,7 @@ export class CommunityForm extends Component<
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
console.log(msg); console.log(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);

View file

@ -11,6 +11,7 @@ import {
SortType, SortType,
Post, Post,
GetPostsForm, GetPostsForm,
GetCommunityForm,
ListingType, ListingType,
GetPostsResponse, GetPostsResponse,
CreatePostLikeResponse, CreatePostLikeResponse,
@ -98,11 +99,11 @@ export class Community extends Component<any, State> {
() => console.log('complete') () => console.log('complete')
); );
if (this.state.communityId) { let form: GetCommunityForm = {
WebSocketService.Instance.getCommunity(this.state.communityId); id: this.state.communityId ? this.state.communityId : null,
} else if (this.state.communityName) { name: this.state.communityName ? this.state.communityName : null,
WebSocketService.Instance.getCommunityByName(this.state.communityName); };
} WebSocketService.Instance.getCommunity(form);
} }
componentWillUnmount() { componentWillUnmount() {
@ -258,7 +259,7 @@ export class Community extends Component<any, State> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.context.router.history.push('/'); this.context.router.history.push('/');
return; return;

View file

@ -38,6 +38,8 @@ enum UnreadType {
Messages, Messages,
} }
type ReplyType = Comment | PrivateMessageI;
interface InboxState { interface InboxState {
unreadOrAll: UnreadOrAll; unreadOrAll: UnreadOrAll;
unreadType: UnreadType; unreadType: UnreadType;
@ -186,7 +188,7 @@ export class Inbox extends Component<any, InboxState> {
} }
all() { all() {
let combined: Array<Comment | PrivateMessageI> = []; let combined: Array<ReplyType> = [];
combined.push(...this.state.replies); combined.push(...this.state.replies);
combined.push(...this.state.mentions); combined.push(...this.state.mentions);
@ -324,7 +326,7 @@ export class Inbox extends Component<any, InboxState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.GetReplies) { } else if (res.op == UserOperation.GetReplies) {

View file

@ -295,7 +295,7 @@ export class Login extends Component<any, State> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);

View file

@ -566,7 +566,7 @@ export class Main extends Component<any, MainState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.GetFollowedCommunities) { } else if (res.op == UserOperation.GetFollowedCommunities) {

View file

@ -425,7 +425,7 @@ export class Modlog extends Component<any, ModlogState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.GetModlog) { } else if (res.op == UserOperation.GetModlog) {

View file

@ -138,7 +138,7 @@ export class Navbar extends Component<any, NavbarState> {
</li> </li>
<li className="nav-item"> <li className="nav-item">
<Link <Link
class="nav-link ml-2" class="nav-link"
to="/sponsors" to="/sponsors"
title={i18n.t('donate_to_lemmy')} title={i18n.t('donate_to_lemmy')}
> >
@ -202,8 +202,8 @@ export class Navbar extends Component<any, NavbarState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
if (res.error == 'not_logged_in') { if (msg.error == 'not_logged_in') {
UserService.Instance.logout(); UserService.Instance.logout();
location.reload(); location.reload();
} }

View file

@ -160,6 +160,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
class="form-control" class="form-control"
value={this.state.postForm.url} value={this.state.postForm.url}
onInput={linkEvent(this, this.handlePostUrlChange)} onInput={linkEvent(this, this.handlePostUrlChange)}
onPaste={linkEvent(this, this.handleImageUploadPaste)}
/> />
{this.state.suggestedTitle && ( {this.state.suggestedTitle && (
<div <div
@ -442,9 +443,22 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
i.setState(i.state); i.setState(i.state);
} }
handleImageUploadPaste(i: PostForm, event: any) {
let image = event.clipboardData.files[0];
if (image) {
i.handleImageUpload(i, image);
}
}
handleImageUpload(i: PostForm, event: any) { handleImageUpload(i: PostForm, event: any) {
let file: any;
if (event.target) {
event.preventDefault(); event.preventDefault();
let file = event.target.files[0]; file = event.target.files[0];
} else {
file = event;
}
const imageUploadUrl = `/pictshare/api/upload.php`; const imageUploadUrl = `/pictshare/api/upload.php`;
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
@ -475,7 +489,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);

View file

@ -119,7 +119,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div class="listing col-12"> <div class="listing col-12">
<div className={`vote-bar mr-2 float-left small text-center`}> <div className={`vote-bar mr-2 float-left small text-center`}>
<button <button
disabled={!UserService.Instance.user}
className={`btn p-0 ${ className={`btn p-0 ${
post.my_vote == 1 ? 'text-info' : 'text-muted' post.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
@ -138,7 +137,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<div class={`font-weight-bold text-muted`}>{post.score}</div> <div class={`font-weight-bold text-muted`}>{post.score}</div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
<button <button
disabled={!UserService.Instance.user}
className={`btn p-0 ${ className={`btn p-0 ${
post.my_vote == -1 ? 'text-danger' : 'text-muted' post.my_vote == -1 ? 'text-danger' : 'text-muted'
}`} }`}
@ -740,17 +738,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
} }
handlePostLike(i: PostListing) { handlePostLike(i: PostListing) {
if (UserService.Instance.user) {
i.setState({ upvoteLoading: true }); i.setState({ upvoteLoading: true });
}
let form: CreatePostLikeForm = { let form: CreatePostLikeForm = {
post_id: i.props.post.id, post_id: i.props.post.id,
score: i.props.post.my_vote == 1 ? 0 : 1, score: i.props.post.my_vote == 1 ? 0 : 1,
}; };
WebSocketService.Instance.likePost(form); WebSocketService.Instance.likePost(form);
} }
handlePostDisLike(i: PostListing) { handlePostDisLike(i: PostListing) {
if (UserService.Instance.user) {
i.setState({ downvoteLoading: true }); i.setState({ downvoteLoading: true });
}
let form: CreatePostLikeForm = { let form: CreatePostLikeForm = {
post_id: i.props.post.id, post_id: i.props.post.id,

View file

@ -23,6 +23,7 @@ import {
SearchType, SearchType,
SortType, SortType,
SearchForm, SearchForm,
GetPostForm,
SearchResponse, SearchResponse,
GetSiteResponse, GetSiteResponse,
GetCommunityResponse, GetCommunityResponse,
@ -84,7 +85,10 @@ export class Post extends Component<any, PostState> {
() => console.log('complete') () => console.log('complete')
); );
WebSocketService.Instance.getPost(postId); let form: GetPostForm = {
id: postId,
};
WebSocketService.Instance.getPost(form);
} }
componentWillUnmount() { componentWillUnmount() {
@ -231,6 +235,18 @@ export class Post extends Component<any, PostState> {
onChange={linkEvent(this, this.handleCommentSortChange)} onChange={linkEvent(this, this.handleCommentSortChange)}
/> />
</label> </label>
<label
className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.Old && 'active'}`}
>
{i18n.t('old')}
<input
type="radio"
value={CommentSortType.Old}
checked={this.state.commentSort === CommentSortType.Old}
onChange={linkEvent(this, this.handleCommentSortChange)}
/>
</label>
</div> </div>
); );
} }
@ -313,6 +329,13 @@ export class Post extends Component<any, PostState> {
+a.comment.deleted - +b.comment.deleted || +a.comment.deleted - +b.comment.deleted ||
b.comment.published.localeCompare(a.comment.published) b.comment.published.localeCompare(a.comment.published)
); );
} else if (this.state.commentSort == CommentSortType.Old) {
tree.sort(
(a, b) =>
+a.comment.removed - +b.comment.removed ||
+a.comment.deleted - +b.comment.deleted ||
a.comment.published.localeCompare(b.comment.published)
);
} else if (this.state.commentSort == CommentSortType.Hot) { } else if (this.state.commentSort == CommentSortType.Hot) {
tree.sort( tree.sort(
(a, b) => (a, b) =>
@ -345,7 +368,7 @@ export class Post extends Component<any, PostState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.GetPost) { } else if (res.op == UserOperation.GetPost) {

View file

@ -283,7 +283,7 @@ export class PrivateMessageForm extends Component<
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);

View file

@ -479,7 +479,7 @@ export class Search extends Component<any, SearchState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (res.op == UserOperation.Search) { } else if (res.op == UserOperation.Search) {

View file

@ -188,7 +188,7 @@ export class Setup extends Component<any, State> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.userLoading = false; this.state.userLoading = false;
this.setState(this.state); this.setState(this.state);

View file

@ -1016,12 +1016,12 @@ export class User extends Component<any, UserState> {
parseMessage(msg: WebSocketJsonResponse) { parseMessage(msg: WebSocketJsonResponse) {
console.log(msg); console.log(msg);
let res = wsJsonToRes(msg); let res = wsJsonToRes(msg);
if (res.error) { if (msg.error) {
toast(i18n.t(msg.error), 'danger'); toast(i18n.t(msg.error), 'danger');
this.state.deleteAccountLoading = false; this.state.deleteAccountLoading = false;
this.state.avatarLoading = false; this.state.avatarLoading = false;
this.state.userSettingsLoading = false; this.state.userSettingsLoading = false;
if (res.error == 'couldnt_find_that_username_or_email') { if (msg.error == 'couldnt_find_that_username_or_email') {
this.context.router.history.push('/'); this.context.router.history.push('/');
} }
this.setState(this.state); this.setState(this.state);

11
ui/src/env.ts vendored
View file

@ -1,6 +1,5 @@
let host = `${window.location.hostname}`; const host = `${window.location.hostname}`;
let port = `${window.location.port == '4444' ? '8536' : window.location.port}`; const port = `${window.location.port == '4444' ? '8536' : window.location.port}`;
let endpoint = `${host}:${port}`; const endpoint = `${host}:${port}`;
export let wsUri = `${
window.location.protocol == 'https:' ? 'wss://' : 'ws://' export const wsUri = `${window.location.protocol == 'https:' ? 'wss://' : 'ws://'}${endpoint}/api/v1/ws`;
}${endpoint}/api/v1/ws`;

13
ui/src/i18next.ts vendored
View file

@ -10,6 +10,8 @@ import { ru } from './translations/ru';
import { zh } from './translations/zh'; import { zh } from './translations/zh';
import { nl } from './translations/nl'; import { nl } from './translations/nl';
import { it } from './translations/it'; import { it } from './translations/it';
import { fi } from './translations/fi';
import { ca } from './translations/ca';
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66 // https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
const resources = { const resources = {
@ -23,11 +25,12 @@ const resources = {
ru, ru,
nl, nl,
it, it,
fi,
ca,
}; };
function format(value: any, format: any, lng: any) { function format(value: any, format: any, lng: any): any {
if (format === 'uppercase') return value.toUpperCase(); return format === 'uppercase' ? value.toUpperCase() : value;
return value;
} }
i18next.init({ i18next.init({
@ -38,9 +41,7 @@ i18next.init({
lng: getLanguage(), lng: getLanguage(),
fallbackLng: 'en', fallbackLng: 'en',
resources, resources,
interpolation: { interpolation: { format },
format: format,
},
}); });
export { i18next as i18n, resources }; export { i18next as i18n, resources };

11
ui/src/index.html vendored
View file

@ -1,7 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head>
<!-- Required meta tags --> <!-- Required meta tags -->
<meta name="Description" content="Lemmy"> <meta name="Description" content="Lemmy">
<meta charset="utf-8"> <meta charset="utf-8">
@ -19,11 +18,11 @@
<!-- Scripts --> <!-- Scripts -->
<script async src="/static/assets/libs/sortable/sortable.min.js"></script> <script async src="/static/assets/libs/sortable/sortable.min.js"></script>
</head> </head>
<body> <body>
<noscript>JavaScript is required for this page.</noscript>
<div id="app"></div> <div id="app"></div>
$bundles $bundles
</body> </body>
</html> </html>

60
ui/src/interfaces.ts vendored
View file

@ -47,6 +47,7 @@ export enum CommentSortType {
Hot, Hot,
Top, Top,
New, New,
Old,
} }
export enum ListingType { export enum ListingType {
@ -248,6 +249,10 @@ export interface FollowCommunityForm {
auth?: string; auth?: string;
} }
export interface GetFollowedCommunitiesForm {
auth: string;
}
export interface GetFollowedCommunitiesResponse { export interface GetFollowedCommunitiesResponse {
communities: Array<CommunityUser>; communities: Array<CommunityUser>;
} }
@ -523,6 +528,12 @@ export interface CommunityForm {
auth?: string; auth?: string;
} }
export interface GetCommunityForm {
id?: number;
name?: string;
auth?: string;
}
export interface GetCommunityResponse { export interface GetCommunityResponse {
community: Community; community: Community;
moderators: Array<CommunityUser>; moderators: Array<CommunityUser>;
@ -572,6 +583,11 @@ export interface PostFormParams {
community?: string; community?: string;
} }
export interface GetPostForm {
id: number;
auth?: string;
}
export interface GetPostResponse { export interface GetPostResponse {
post: Post; post: Post;
comments: Array<Comment>; comments: Array<Comment>;
@ -759,6 +775,45 @@ export interface PrivateMessageResponse {
message: PrivateMessage; message: PrivateMessage;
} }
export type MessageType =
| EditPrivateMessageForm
| LoginForm
| RegisterForm
| CommunityForm
| FollowCommunityForm
| ListCommunitiesForm
| GetFollowedCommunitiesForm
| PostForm
| GetPostForm
| GetPostsForm
| GetCommunityForm
| CommentForm
| CommentLikeForm
| SaveCommentForm
| CreatePostLikeForm
| BanFromCommunityForm
| AddAdminForm
| AddModToCommunityForm
| TransferCommunityForm
| TransferSiteForm
| SaveCommentForm
| BanUserForm
| AddAdminForm
| GetUserDetailsForm
| GetRepliesForm
| GetUserMentionsForm
| EditUserMentionForm
| GetModlogForm
| SiteForm
| SearchForm
| UserSettingsForm
| DeleteAccountForm
| PasswordResetForm
| PasswordChangeForm
| PrivateMessageForm
| EditPrivateMessageForm
| GetPrivateMessagesForm;
type ResponseType = type ResponseType =
| SiteResponse | SiteResponse
| GetFollowedCommunitiesResponse | GetFollowedCommunitiesResponse
@ -784,11 +839,10 @@ type ResponseType =
export interface WebSocketResponse { export interface WebSocketResponse {
op: UserOperation; op: UserOperation;
data: ResponseType; data: ResponseType;
error?: string;
} }
export interface WebSocketJsonResponse { export interface WebSocketJsonResponse {
op: string; op?: string;
data: ResponseType; data?: ResponseType;
error?: string; error?: string;
} }

View file

@ -9,9 +9,12 @@ import {
CommentForm, CommentForm,
SaveCommentForm, SaveCommentForm,
CommentLikeForm, CommentLikeForm,
GetPostForm,
GetPostsForm, GetPostsForm,
CreatePostLikeForm, CreatePostLikeForm,
GetCommunityForm,
FollowCommunityForm, FollowCommunityForm,
GetFollowedCommunitiesForm,
GetUserDetailsForm, GetUserDetailsForm,
ListCommunitiesForm, ListCommunitiesForm,
GetModlogForm, GetModlogForm,
@ -35,6 +38,7 @@ import {
PrivateMessageForm, PrivateMessageForm,
EditPrivateMessageForm, EditPrivateMessageForm,
GetPrivateMessagesForm, GetPrivateMessagesForm,
MessageType,
} from '../interfaces'; } from '../interfaces';
import { webSocket } from 'rxjs/webSocket'; import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -108,9 +112,9 @@ export class WebSocketService {
} }
public getFollowedCommunities() { public getFollowedCommunities() {
let data = { auth: UserService.Instance.auth }; let form: GetFollowedCommunitiesForm = { auth: UserService.Instance.auth };
this.subject.next( this.subject.next(
this.wsSendWrapper(UserOperation.GetFollowedCommunities, data) this.wsSendWrapper(UserOperation.GetFollowedCommunities, form)
); );
} }
@ -125,19 +129,14 @@ export class WebSocketService {
this.subject.next(this.wsSendWrapper(UserOperation.CreatePost, postForm)); this.subject.next(this.wsSendWrapper(UserOperation.CreatePost, postForm));
} }
public getPost(postId: number) { public getPost(form: GetPostForm) {
let data = { id: postId, auth: UserService.Instance.auth }; this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.GetPost, data)); this.subject.next(this.wsSendWrapper(UserOperation.GetPost, form));
} }
public getCommunity(communityId: number) { public getCommunity(form: GetCommunityForm) {
let data = { id: communityId, auth: UserService.Instance.auth }; this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data)); this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, form));
}
public getCommunityByName(name: string) {
let data = { name: name, auth: UserService.Instance.auth };
this.subject.next(this.wsSendWrapper(UserOperation.GetCommunity, data));
} }
public createComment(commentForm: CommentForm) { public createComment(commentForm: CommentForm) {
@ -310,7 +309,7 @@ export class WebSocketService {
); );
} }
private wsSendWrapper(op: UserOperation, data: any) { private wsSendWrapper(op: UserOperation, data: MessageType) {
let send = { op: UserOperation[op], data: data }; let send = { op: UserOperation[op], data: data };
console.log(send); console.log(send);
return send; return send;

239
ui/src/translations/ca.ts vendored Normal file
View file

@ -0,0 +1,239 @@
export const ca = {
translation: {
post: 'Publicar',
remove_post: 'Eliminar publicació',
no_posts: 'Sense publicacions.',
create_a_post: 'Crear una publicació',
create_post: 'Crear Publicació',
number_of_posts: '{{count}} Publicacions',
posts: 'Publicacions',
related_posts: 'Aquestes publicacions podrien estar relacionades',
cross_posts: 'Aquest link també ha sigut publicat en:',
cross_post: 'cross-post',
comments: 'Comentaris',
number_of_comments: '{{count}} Comentaris',
remove_comment: 'Eliminar Comentaris',
communities: 'Comunitats',
users: 'Usuaris',
create_a_community: 'Crear una comunitat',
create_community: 'Crear Comunitat',
remove_community: 'Eliminar Comunitat',
subscribed_to_communities: 'Subscrit a <1>comunitats</1>',
trending_communities: '<1>Comunitats</1> en tendència',
list_of_communities: 'Llista de comunitats',
number_of_communities: '{{count}} Comunitats',
community_reqs: 'minúscules, guió baix, i sense espais.',
create_private_message: 'Crear Missatge Privat',
send_secure_message: 'Enviar Missatge Segur',
send_message: 'Enviar Missatge',
message: 'Missatge',
edit: 'editar',
reply: 'respondre',
cancel: 'Cancelar',
preview: 'Previsualitzar',
upload_image: 'pujar imatge',
avatar: 'Avatar',
upload_avatar: 'Pujar Avatar',
show_avatars: 'Veure Avatares',
formatting_help: 'Ajuda de format',
view_source: 'veure font',
unlock: 'desbloquejar',
lock: 'bloquejar',
sticky: 'fijat',
unsticky: 'no fijat',
link: 'link',
archive_link: 'arxivar link',
mod: 'moderador',
mods: 'moderadores',
moderates: 'Modera',
settings: 'Configuració',
remove_as_mod: 'eliminar com moderador',
appoint_as_mod: 'designar com moderador',
modlog: 'Historial de moderació',
admin: 'administrador',
admins: 'administradors',
remove_as_admin: 'eliminar com administrador',
appoint_as_admin: 'designar com administrador',
remove: 'eliminar',
removed: 'eliminat',
locked: 'bloquejat',
stickied: 'fijat',
reason: 'Raó',
mark_as_read: 'marcar com llegit',
mark_as_unread: 'marcar com no llegit',
delete: 'eliminar',
deleted: 'eliminat',
delete_account: 'Eliminar Compte',
delete_account_confirm:
'Avís: aquesta acció eliminarà permanentment la teva informació. Introdueix la teva contrasenya per a continuar',
restore: 'restaurar',
ban: 'expulsar',
ban_from_site: 'expulsar del lloc',
unban: 'admetre',
unban_from_site: 'admetre al lloc',
banned: 'expulsat',
save: 'guardar',
unsave: 'descartar',
create: 'crear',
creator: 'creador',
username: "Nom d'Usuari",
email_or_username: 'Correu o Usuari',
number_of_users: '{{count}} Usuaris',
number_of_subscribers: '{{count}} Subscriptors',
number_of_points: '{{count}} Punts',
number_online: '{{count}} Usauris En Línia',
name: 'Nom',
title: 'Titol',
category: 'Categoria',
subscribers: 'Suscriptors',
both: 'Ambdos',
saved: 'Guardat',
unsubscribe: "Desubscriure's",
subscribe: "Subscriure's",
subscribed: 'Subscrit',
prev: 'Anterior',
next: 'Següent',
sidebar: 'Descripció de la comunitat',
sort_type: "Tipus d'orden",
hot: 'Popular',
new: 'Nou',
top_day: 'El millor del dia',
week: 'Setmana',
month: 'Mes',
year: 'Any',
all: 'Tot',
top: 'Millor',
api: 'API',
docs: 'Docs',
inbox: "Bústia d'entrada",
inbox_for: "Bústia d'entrada per a <1>{{user}}</1>",
mark_all_as_read: 'marcar tot com llegit',
type: 'Tipus',
unread: 'No llegit',
replies: 'Respostes',
mentions: 'Menciones',
reply_sent: 'Resposta enviada',
message_sent: 'Missatge enviado',
search: 'Buscar',
overview: 'Resum',
view: 'Vista',
logout: 'Tancar sessió',
login_sign_up: 'Iniciar sessió / Crear compte',
login: 'Iniciar sessió',
sign_up: 'Crear compte',
notifications_error:
"Notificacions d'escriptori no disponibles al teu navegador. Prova amb Firefox o Chrome.",
unread_messages: 'Missatges no llegits',
messages: 'Missatges',
password: 'Contrasenya',
verify_password: 'Verificar Contrasenya',
old_password: 'Antiga Contrasenya',
forgot_password: 'oblidí la meva contrasenya',
reset_password_mail_sent: 'Enviar correu per a restablir la contrasenya.',
password_change: 'Canvi de Contrasenya',
new_password: 'Nueva Contrasenya',
no_email_setup: 'Aquest servidor no ha activat correctament el correu.',
email: 'Correu electrònic',
matrix_user_id: 'Usuari Matricial',
private_message_disclaimer:
'Avís: Els missatges privats en Lemmy no són segurs. Sisplau creu un compte en <1>Riot.im</1> per a mensajeria segura.',
send_notifications_to_email: 'Enviar notificacions al correu',
optional: 'Opcional',
expires: 'Expira',
language: 'Llenguatge',
browser_default: 'Per defecte del navegador',
downvotes_disabled: 'Vots negatius deshabilitats',
enable_downvotes: 'Habilitar vots negatius',
open_registration: 'Obrir registre',
registration_closed: 'Registre tancat',
enable_nsfw: 'Habilitar NSFW',
url: 'URL',
body: 'Descripció',
copy_suggested_title: 'Copiar el títol sugerido: {{title}}',
community: 'Comunitat',
expand_here: 'Expandir ací',
subscribe_to_communities: "Subscriure's a algunes <1>comunitats</1>.",
chat: 'Chat',
recent_comments: 'Comentaris recients',
no_results: 'Sense resultats.',
setup: 'Configurar',
lemmy_instance_setup: "Configuració d'instancia de Lemmy",
setup_admin: 'Configurar administrador del Lloc',
your_site: 'el teu lloc',
modified: 'modificat',
nsfw: 'NSFW',
show_nsfw: 'Mostrar contingut NSFW',
theme: 'Tema',
sponsors: 'Patrocinadors',
sponsors_of_lemmy: 'Patrocinadors de Lemmy',
sponsor_message:
'Lemmy és programari lliure i de <1>codi obert</1>, la qual cosa significa que no tindrà publicitats, monetització, ni capitals emprenedors, mai. Les teves donacions secunden directament el desenvolupament a temps complet del projecte. Moltes gràcies a les següents persones:',
support_on_patreon: 'Suport a Patreon',
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',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
monero: 'Monero',
code: 'Codi',
joined: 'Es va unir',
by: 'per',
to: 'a',
from: 'des de',
transfer_community: 'transferir comunitat',
transfer_site: 'transferir lloc',
are_you_sure: 'Ets segur?',
yes: 'sí',
no: 'no',
powered_by: 'Impulsat per',
landing_0:
'Lemmy és un <1>agregador de links</1> / alternativa a reddit, amb la intenció de funcionar al <2>fedivers</2>.<3></3>És allotjable per un mateix (sense necessitat de grans companyies), té actualització en directe de cadenes de comentaris, i és petit (<4>~80kB</4>). Federar amb el sistema de xarxes ActivityPub forma part dels objectius del projecte. <5></5>Aquesta és una <6>versió beta molt prematura</6>, i actualment moltes de les característiques són trencades o falten. <7></7>Suggereix noves característiques o reporta errors <8>aquí</8>.<9></9>Fet amb <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'No has iniciat sessió.',
logged_in: 'Has iniciat sessió.',
community_ban: "Has sigut expulsat d'aquesta comunitat.",
site_ban: "Has sigut expulsat d'aquest lloc.",
couldnt_create_comment: "No s'ha pogut crear el comentari.",
couldnt_like_comment: "No s'ha pogut donar m'agrada al comentari.",
couldnt_update_comment: "No s'ha pogut actualitzar el comentari.",
couldnt_save_comment: "No s'ha pogut guardar el comentari.",
no_comment_edit_allowed: 'No tens permisos per a editar el comentari.',
no_post_edit_allowed: 'No tens permisos per a editar la publicació.',
no_community_edit_allowed: 'No tens permisos per a editar la comunitat.',
couldnt_find_community: "No s'ha pogut trobar la comunitat.",
couldnt_update_community: "No s'ha pogut actualitzar la comunitat.",
community_already_exists: 'Aquesta comunitat ja existeix.',
community_moderator_already_exists:
'Aquest moderador de la comunitat ja existeix.',
community_follower_already_exists:
'Aquest seguidor de la comunitat ja existeix.',
community_user_already_banned:
'Aquest usuari de la comunitat ja fou expulsat.',
couldnt_create_post: "No s'ha pogut crear la publicació.",
couldnt_like_post: "No s'ha pogut donar m'agrada a la publicació.",
couldnt_find_post: "No s'ha pogut trobar la publicació.",
couldnt_get_posts: "No s'han pogut obtindre les publicacions.",
couldnt_update_post: "No s'ha pogut actualitzar la publicació.",
couldnt_save_post: "No s'ha pogut guardar la publicació.",
no_slurs: 'Prohibit insultar.',
not_an_admin: 'No és un administrador.',
site_already_exists: 'El lloc ja existeix.',
couldnt_update_site: "No s'ha pogut actualitzar el lloc.",
couldnt_find_that_username_or_email:
"No s'ha pogut trobar aquest nom de usuari o correu electrònic.",
password_incorrect: 'Contrasenya incorrecta.',
passwords_dont_match: 'Les contrasenyes no coincideixen.',
admin_already_created: 'Ho sentim, ja hi ha un adminisitrador.',
user_already_exists: "L'usuari ja existeix.",
email_already_exists: 'El correu ja és en ús.',
couldnt_update_user: "No s'ha pogut actualitzar l'usuari.",
system_err_login:
'Error del sistema. Intenti tancar sessió i ingressar de nou.',
couldnt_create_private_message: "No s'ha pogut crear el missatge privat.",
no_private_message_edit_allowed:
'Sense permisos per a editar el missatge privat.',
couldnt_update_private_message:
"No s'ha pogut actualitzar el missatge privat.",
},
};

View file

@ -97,6 +97,7 @@ export const en = {
sort_type: 'Sort type', sort_type: 'Sort type',
hot: 'Hot', hot: 'Hot',
new: 'New', new: 'New',
old: 'Old',
top_day: 'Top day', top_day: 'Top day',
week: 'Week', week: 'Week',
month: 'Month', month: 'Month',

View file

@ -1,7 +1,7 @@
export const es = { export const es = {
translation: { translation: {
post: 'Publicar', post: 'Publicar',
remove_post: 'Remover publicación', remove_post: 'Eliminar publicación',
no_posts: 'Sin publicaciones.', no_posts: 'Sin publicaciones.',
create_a_post: 'Crear una publicación', create_a_post: 'Crear una publicación',
create_post: 'Crear Publicación', create_post: 'Crear Publicación',
@ -12,22 +12,29 @@ export const es = {
cross_post: 'cross-post', cross_post: 'cross-post',
comments: 'Comentarios', comments: 'Comentarios',
number_of_comments: '{{count}} Comentarios', number_of_comments: '{{count}} Comentarios',
remove_comment: 'Remover Comentarios', remove_comment: 'Eliminar Comentarios',
communities: 'Comunidades', communities: 'Comunidades',
users: 'Usuarios', users: 'Usuarios',
create_a_community: 'Crear una comunidad', create_a_community: 'Crear una comunidad',
create_community: 'Crear Comunidad', create_community: 'Crear Comunidad',
remove_community: 'Remover Comunidad', remove_community: 'Eliminar Comunidad',
subscribed_to_communities: 'Suscrito a <1>comunidades</1>', subscribed_to_communities: 'Suscrito a <1>comunidades</1>',
trending_communities: '<1>Comunidades</1> en tendencia', trending_communities: '<1>Comunidades</1> en tendencia',
list_of_communities: 'Lista de comunidades', list_of_communities: 'Lista de comunidades',
number_of_communities: '{{count}} Comunidades', number_of_communities: '{{count}} Comunidades',
community_reqs: 'minúsculas, guión bajo, y sin espacios.', community_reqs: 'minúsculas, guión bajo, y sin espacios.',
create_private_message: 'Crear Mensaje Privado',
send_secure_message: 'Enviar Mensaje Seguro',
send_message: 'Enviar Mensaje',
message: 'Mensaje',
edit: 'editar', edit: 'editar',
reply: 'responder', reply: 'responder',
cancel: 'Cancelar', cancel: 'Cancelar',
preview: 'Previsualizar', preview: 'Previsualizar',
upload_image: 'Subir imagen', upload_image: 'subir imagen',
avatar: 'Avatar',
upload_avatar: 'Subir Avatar',
show_avatars: 'Ver Avatares',
formatting_help: 'Ayuda de formato', formatting_help: 'Ayuda de formato',
view_source: 'ver fuente', view_source: 'ver fuente',
unlock: 'desbloquear', unlock: 'desbloquear',
@ -35,19 +42,20 @@ export const es = {
sticky: 'fijado', sticky: 'fijado',
unsticky: 'no fijado', unsticky: 'no fijado',
link: 'link', link: 'link',
mod: 'Moderador', archive_link: 'archivar link',
mods: 'Moderadores', mod: 'moderador',
mods: 'moderadores',
moderates: 'Modera', moderates: 'Modera',
settings: 'Configuración', settings: 'Configuración',
remove_as_mod: 'remover como moderador', remove_as_mod: 'eliminar como moderador',
appoint_as_mod: 'designar como moderador', appoint_as_mod: 'designar como moderador',
modlog: 'Historial de moderación', modlog: 'Historial de moderación',
admin: 'administrador', admin: 'administrador',
admins: 'administradores', admins: 'administradores',
remove_as_admin: 'remover como administrador', remove_as_admin: 'eliminar como administrador',
appoint_as_admin: 'designar como administrador', appoint_as_admin: 'designar como administrador',
remove: 'remover', remove: 'eliminar',
removed: 'removido', removed: 'eliminado',
locked: 'bloqueado', locked: 'bloqueado',
stickied: 'fijado', stickied: 'fijado',
reason: 'Razón', reason: 'Razón',
@ -57,19 +65,19 @@ export const es = {
deleted: 'eliminado', deleted: 'eliminado',
delete_account: 'Eliminar Cuenta', delete_account: 'Eliminar Cuenta',
delete_account_confirm: delete_account_confirm:
'Peligro: esta acción eliminará permanentemente tu información. ¿Estás seguro?', 'Aviso: esta acción eliminará permanentemente tu información. Introduce tu contraseña para continuar',
restore: 'restaurar', restore: 'restaurar',
ban: 'expulsar', ban: 'expulsar',
ban_from_site: 'expulsar del sitio', ban_from_site: 'expulsar del sitio',
unban: 'admitir', unban: 'admitir',
unban_from_site: 'admitir al sitio', unban_from_site: 'admitir en el sitio',
banned: 'expulsado', banned: 'expulsado',
save: 'guardar', save: 'guardar',
unsave: 'descartar', unsave: 'descartar',
create: 'crear', create: 'crear',
creator: 'creador', creator: 'creador',
username: 'Nombre de Usuario', username: 'Nombre de Usuario',
email_or_username: 'Correo electrónico o Nombre de Usuario', email_or_username: 'Correo o Usuario',
number_of_users: '{{count}} Usuarios', number_of_users: '{{count}} Usuarios',
number_of_subscribers: '{{count}} Suscriptores', number_of_subscribers: '{{count}} Suscriptores',
number_of_points: '{{count}} Puntos', number_of_points: '{{count}} Puntos',
@ -80,8 +88,8 @@ export const es = {
subscribers: 'Suscriptores', subscribers: 'Suscriptores',
both: 'Ambos', both: 'Ambos',
saved: 'Guardado', saved: 'Guardado',
unsubscribe: 'Abandonar comunidad', unsubscribe: 'Desuscribirse',
subscribe: 'Suscribir', subscribe: 'Suscribirse',
subscribed: 'Suscrito', subscribed: 'Suscrito',
prev: 'Anterior', prev: 'Anterior',
next: 'Siguiente', next: 'Siguiente',
@ -96,12 +104,16 @@ export const es = {
all: 'Todo', all: 'Todo',
top: 'Mejor', top: 'Mejor',
api: 'API', api: 'API',
docs: 'Docs',
inbox: 'Buzón de entrada', inbox: 'Buzón de entrada',
inbox_for: 'Buzón de entrada para <1>{{user}}</1>', inbox_for: 'Buzón de entrada para <1>{{user}}</1>',
mark_all_as_read: 'marcar todo como leído', mark_all_as_read: 'marcar todo como leído',
type: 'Tipo', type: 'Tipo',
unread: 'No leído', unread: 'No leído',
replies: 'Respuestas',
mentions: 'Menciones',
reply_sent: 'Respuesta enviada', reply_sent: 'Respuesta enviada',
message_sent: 'Mensaje enviado',
search: 'Buscar', search: 'Buscar',
overview: 'Resumen', overview: 'Resumen',
view: 'Vista', view: 'Vista',
@ -112,11 +124,29 @@ export const es = {
notifications_error: notifications_error:
'Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.', 'Notificaciones de escritorio no disponibles en tu navegador. Prueba Firefox o Chrome.',
unread_messages: 'Mensajes no leídos', unread_messages: 'Mensajes no leídos',
messages: 'Mensajes',
password: 'Contraseña', password: 'Contraseña',
verify_password: 'Verificar contraseña', verify_password: 'Verificar contraseña',
old_password: 'Antigua Contraseña',
forgot_password: 'olvidé mi contraseña',
reset_password_mail_sent: 'Enviar correo para reestablecer la contraseña.',
password_change: 'Cambio de Contraseña',
new_password: 'Nueva Contraseña',
no_email_setup: "Este servidor no ha activado correctamente el correo.",
email: 'Correo electrónico', email: 'Correo electrónico',
matrix_user_id: 'Usuario Matricial',
private_message_disclaimer:
'Aviso: Los mensajes privados en Lemmy no son seguros. Por favor cree una cuenta en <1>Riot.im</1> para mensajeria segura.',
send_notifications_to_email: 'Enviar notificaciones al correo',
optional: 'Opcional', optional: 'Opcional',
expires: 'Expira', expires: 'Expira',
language: 'Idioma',
browser_default: 'Por defecto del navegador',
downvotes_disabled: 'Votos negativos deshabilitados',
enable_downvotes: 'Habilitar votos negativos',
open_registration: 'Abrir registro',
registration_closed: 'Registro cerrado',
enable_nsfw: 'Habilitar NSFW',
url: 'URL', url: 'URL',
body: 'Descripción', body: 'Descripción',
copy_suggested_title: 'Copiar el título sugerido: {{title}}', copy_suggested_title: 'Copiar el título sugerido: {{title}}',
@ -139,8 +169,10 @@ export const es = {
sponsor_message: 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:', '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:',
support_on_patreon: 'Apoyo en Patreon', support_on_patreon: 'Apoyo en Patreon',
donate_to_lemmy: 'Donar a Lemmy',
donate: 'Donar',
general_sponsors: general_sponsors:
'Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.', 'Los Patrocinadores Generales son aquellos que señaron entre $10 y $39 a Lemmy.',
crypto: 'Crypto', crypto: 'Crypto',
bitcoin: 'Bitcoin', bitcoin: 'Bitcoin',
ethereum: 'Ethereum', ethereum: 'Ethereum',
@ -148,7 +180,8 @@ export const es = {
code: 'Código', code: 'Código',
joined: 'Se unió', joined: 'Se unió',
by: 'por', by: 'por',
to: 'en', to: 'a',
from: 'desde',
transfer_community: 'transferir comunidad', transfer_community: 'transferir comunidad',
transfer_site: 'transferir sitio', transfer_site: 'transferir sitio',
are_you_sure: '¿Estás seguro?', are_you_sure: '¿Estás seguro?',
@ -158,24 +191,22 @@ export const es = {
landing_0: landing_0:
'Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.', 'Lemmy es un <1>agregador de links</1> / alternativa a reddit, con la intención de funcionar en el <2>fediverso</2>.<3></3>Es alojable por uno mismo (sin necesidad de grandes compañías), tiene actualización en vivo de cadenas de comentarios, y es pequeño (<4>~80kB</4>). Federar con el sistema de redes ActivityPub forma parte de los objetivos del proyecto. <5></5>Esta es una <6>version beta muy prematura</6>, y actualmente muchas de las características están rotas o faltan. <7></7>Sugiere nuevas características o reporta errores <8>aquí</8>.<9></9>Hecho con <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.',
not_logged_in: 'No has iniciado sesión.', not_logged_in: 'No has iniciado sesión.',
logged_in: 'Has iniciado sesión.',
community_ban: 'Has sido expulsado de esta comunidad.', community_ban: 'Has sido expulsado de esta comunidad.',
site_ban: 'Has sido expulsado del sitio', site_ban: 'Has sido expulsado del sitio',
couldnt_create_comment: 'No se pudo crear el comentario.', couldnt_create_comment: 'No se pudo crear el comentario.',
couldnt_like_comment: 'No se pudo gustar el comentario.', couldnt_like_comment: 'No se pudo dar me gusta al comentario.',
couldnt_update_comment: 'No se pudo actualizar el comentario.', couldnt_update_comment: 'No se pudo actualizar el comentario.',
couldnt_save_comment: 'No se pudo guardar el comentario.', couldnt_save_comment: 'No se pudo guardar el comentario.',
no_comment_edit_allowed: 'No tiene permitido editar el comentario.', no_comment_edit_allowed: 'No tiene permisos para editar el comentario.',
no_post_edit_allowed: 'No tiene permitido editar la publicación.', no_post_edit_allowed: 'No tiene permisos para editar la publicación.',
no_community_edit_allowed: 'No tiene permitido editar la comunidad.', no_community_edit_allowed: 'No tiene permisos para editar la comunidad.',
couldnt_find_community: 'No se pudo encontrar la comunidad.', couldnt_find_community: 'No se pudo encontrar la comunidad.',
couldnt_update_community: 'No se pudo actualizar la comunidad.', couldnt_update_community: 'No se pudo actualizar la comunidad.',
community_already_exists: 'Esta comunidad ya existe.', community_already_exists: 'Esta comunidad ya existe.',
community_moderator_already_exists: community_moderator_already_exists: 'Este moderador de la comunidad ya existe.',
'Este moderador de la comunidad ya existe.', community_follower_already_exists: 'Este seguidor de la comunidad ya existe.',
community_follower_already_exists: community_user_already_banned: 'Este usuario de la comunidad ya fue expulsado.',
'Este seguidor de la comunidad ya existe.',
community_user_already_banned:
'Este usuario de la comunidad ya fue expulsado.',
couldnt_create_post: 'No se pudo crear la publicación.', couldnt_create_post: 'No se pudo crear la publicación.',
couldnt_like_post: 'No se pudo gustar la publicación.', couldnt_like_post: 'No se pudo gustar la publicación.',
couldnt_find_post: 'No se pudo encontrar la publicación.', couldnt_find_post: 'No se pudo encontrar la publicación.',
@ -192,8 +223,11 @@ export const es = {
passwords_dont_match: 'Las contraseñas no coinciden.', passwords_dont_match: 'Las contraseñas no coinciden.',
admin_already_created: 'Lo sentimos, ya hay un adminisitrador.', admin_already_created: 'Lo sentimos, ya hay un adminisitrador.',
user_already_exists: 'El usuario ya existe.', user_already_exists: 'El usuario ya existe.',
email_already_exists: 'El correo ya está en uso.',
couldnt_update_user: 'No se pudo actualizar el usuario.', couldnt_update_user: 'No se pudo actualizar el usuario.',
system_err_login: system_err_login: 'Error del sistema. Intente cerrar sesión e ingresar de nuevo.',
'Error del sistema. Intente cerrar sesión e ingresar de nuevo.', couldnt_create_private_message: "No se pudo crear el mensaje privado.",
no_private_message_edit_allowed: 'Sin permisos para editar el mensaje privado.',
couldnt_update_private_message: "No se pudo actualizar el mensaje privado.",
}, },
}; };

233
ui/src/translations/fi.ts vendored Normal file
View file

@ -0,0 +1,233 @@
export const fi = {
translation: {
post: 'viesti',
remove_post: 'Poista viesti',
no_posts: 'Ei viestjä.',
create_a_post: 'Luo viesti',
create_post: 'Luo viesti',
number_of_posts: '{{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',
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>',
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ä.',
create_private_message: 'Luo yksityisviesti',
send_secure_message: 'Lähetä suojattu viesti',
send_message: 'Lähetä viesti',
message: 'Viesti',
edit: 'muokkaa',
reply: 'vastaa',
cancel: 'Peru',
preview: 'Esikatselu',
upload_image: 'lataa kuva',
avatar: 'avatar',
upload_avatar: 'Lähetä avatar',
show_avatars: 'Näytä avatarit',
formatting_help: 'apua muotoiluun',
view_source: 'näytä lähde',
unlock: 'avaa',
lock: 'lukitse',
sticky: 'kiinnitä',
unsticky: 'poista kiinnitys',
link: 'linkitä',
archive_link: 'arkistoi linkki',
mod: 'moderaattori',
mods: 'moderaattorit',
moderates: 'Moderoi',
settings: 'Asetukset',
remove_as_mod: 'Poista moderaattorina',
appoint_as_mod: 'Nimitä moderaattoriksi',
modlog: 'Moderoinnin loki',
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',
remove: 'poista',
removed: 'poistettu',
locked: 'lukittu',
stickied: 'kiinnitetty',
reason: 'Syy',
mark_as_read: 'merkitse luetuksi',
mark_as_unread: 'merkitse lukemattomaksi',
delete: 'poista',
deleted: 'deleted',
delete_account: 'Poista tili',
delete_account_confirm:
'Varoitus: tämä poistaa pysyvästi kaiken datasi. Anna salasanasi varmistukseksi.',
restore: 'palauta',
ban: 'porttikielto',
ban_from_site: 'aseta porttikielto sivulle',
unban: 'poista porttikielto',
unban_from_site: 'poista porttikielto sivulta',
banned: 'asetettu porttikieltoon',
save: 'tallenna',
unsave: 'jätä tallentamatta',
create: 'luo',
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',
name: 'Nimi',
title: 'Kuvaus',
category: 'Luokka',
subscribers: 'Tilaajat',
both: 'Molemmat',
saved: 'Tallennettu',
unsubscribe: 'Poista tilaus',
subscribe: 'Tilaa',
subscribed: 'Tilattu',
prev: 'Edellinen',
next: 'Seuraava',
sidebar: 'Sivupalkki',
sort_type: 'Lajittele tyypin mukaan',
hot: 'Kuumat',
new: 'Uudet',
top_day: 'Päivän parhaimmat',
week: 'Viikko',
month: 'Kuukausi',
year: 'Vuosi',
all: 'Kaikki',
top: 'Parhaimmat',
api: 'API',
docs: 'Dokumentaatio',
inbox: 'Postilaatikko',
inbox_for: 'Postilaatikko käyttäjällä <1>{{user}}</1>',
mark_all_as_read: 'aseta kaikki luetuiksi',
type: 'Tyyppi',
unread: 'Lukematon',
replies: 'Vastaukset',
mentions: 'Maininnat',
reply_sent: 'Vastaus lähetetty',
message_sent: 'Viesti lähetetty',
search: 'Etsi',
overview: 'Yleiskatsaus',
view: 'Katso',
logout: 'Kirjaudu ulos',
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.',
unread_messages: 'Lukemattomat viestit',
messages: 'Viestit',
password: 'Salasana',
verify_password: 'Vahvista salasana',
old_password: 'Vanha salasana',
forgot_password: 'unohdin salasanani',
reset_password_mail_sent: 'Sähköposti lähetettiin salasanan nollaamiseksi.',
password_change: 'Salasanan muutos',
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.',
send_notifications_to_email: 'Lähetä ilmoitukset sähköpostiin',
optional: 'Valinnainen',
expires: 'Umpeutuu',
language: 'Kieli',
browser_default: 'Selaimen oletus',
downvotes_disabled: 'Alaäänet otettu pois päältä',
enable_downvotes: 'Salli alaäänet',
open_registration: 'Avaa rekisteröityminen',
registration_closed: 'Rekisteröityminen suljettu',
enable_nsfw: 'Salli NSFW',
url: 'URL',
body: 'Body',
copy_suggested_title: 'kopioi ehdotettu otsikko: {{title}}',
community: 'Yhteisö',
expand_here: 'Laajenna tässä',
subscribe_to_communities: 'Tilaa joitakin <1>yhteisöjä</1>.',
chat: 'Chat',
recent_comments: 'Viimeaikaiset kommentit',
no_results: 'Ei tuloksia.',
setup: 'Asetus',
lemmy_instance_setup: 'Lemmy-instanssin asetus',
setup_admin: 'Aseta sivuston ylläpitäjä',
your_site: 'sivustosi',
modified: 'muokattu',
nsfw: 'NSFW',
show_nsfw: 'Näytä NSFW-sisältö',
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:',
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',
bitcoin: 'Bitcoin',
ethereum: 'Ethereum',
monero: 'Monero',
code: 'Code',
joined: 'Liittyi',
by: 'käyttäjältä',
to: 'yhteisössä',
from: 'paikasta',
transfer_community: 'siirron yhteisö',
transfer_site: 'siirron määrä',
are_you_sure: 'oletko varma?',
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>.",
not_logged_in: 'Ei kirjautunut sisään.',
logged_in: 'Kirjautunut sisään.',
community_ban: 'Sinulle on asetettu porttikielto tähän yhteisöön.',
site_ban: 'Sinut on asetettu porttikieltoon tältä sivustolta',
couldnt_create_comment: "Kommenttia ei pystytty luomaan.",
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öä.',
couldnt_find_community: "Yhteisöä ei voitu löytää.",
couldnt_update_community: "Yhteisöä ei voitu päivittää.",
community_already_exists: 'Yhteisö on jo olemassa.',
community_moderator_already_exists: 'Yhteisön moderaattori on jo olemassa.',
community_follower_already_exists: 'Yhteisön seuraaja on jo olemassa.',
community_user_already_banned: 'Yhteisön käyttäjä on jo porttikiellossa.',
couldnt_create_post: "Ei voitu luoda viestiä.",
couldnt_like_post: "Viestistä ei voitu tykätä.",
couldnt_find_post: "Viestiä ei löytynyt.",
couldnt_get_posts: "Viestejä ei saatu",
couldnt_update_post: "Viestiä ei voitu päivittää",
couldnt_save_post: "Viestiä ei voitu tallentaa.",
no_slurs: 'Ei loukkauksia.',
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.",
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.',
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ää.",
},
};

8
ui/src/utils.ts vendored
View file

@ -7,6 +7,8 @@ import 'moment/locale/sv';
import 'moment/locale/ru'; import 'moment/locale/ru';
import 'moment/locale/nl'; import 'moment/locale/nl';
import 'moment/locale/it'; import 'moment/locale/it';
import 'moment/locale/fi';
import 'moment/locale/ca';
import { import {
UserOperation, UserOperation,
@ -251,11 +253,13 @@ export function debounce(
} }
export const languages = [ export const languages = [
{ code: 'ca', name: 'Català' },
{ code: 'en', name: 'English' }, { code: 'en', name: 'English' },
{ code: 'eo', name: 'Esperanto' }, { code: 'eo', name: 'Esperanto' },
{ code: 'es', name: 'Español' }, { code: 'es', name: 'Español' },
{ code: 'de', name: 'Deutsch' }, { code: 'de', name: 'Deutsch' },
{ code: 'zh', name: '中文' }, { code: 'zh', name: '中文' },
{ code: 'fi', name: 'Suomi' },
{ code: 'fr', name: 'Français' }, { code: 'fr', name: 'Français' },
{ code: 'sv', name: 'Svenska' }, { code: 'sv', name: 'Svenska' },
{ code: 'ru', name: 'Русский' }, { code: 'ru', name: 'Русский' },
@ -298,6 +302,10 @@ export function getMomentLanguage(): string {
lang = 'nl'; lang = 'nl';
} else if (lang.startsWith('it')) { } else if (lang.startsWith('it')) {
lang = 'it'; lang = 'it';
} else if (lang.startsWith('fi')) {
lang = 'fi';
} else if (lang.startsWith('ca')) {
lang = 'ca';
} else { } else {
lang = 'en'; lang = 'en';
} }

2
ui/src/version.ts vendored
View file

@ -1 +1 @@
export let version: string = 'v0.6.4'; export const version: string = 'v0.6.7';

View file

@ -8,51 +8,51 @@ import { sv } from './src/translations/sv';
import { ru } from './src/translations/ru'; import { ru } from './src/translations/ru';
import { nl } from './src/translations/nl'; import { nl } from './src/translations/nl';
import { it } from './src/translations/it'; import { it } from './src/translations/it';
import { fi } from './src/translations/fi';
import { ca } from './src/translations/ca';
import fs from 'fs'; import fs from 'fs';
let readmePath = '../README.md'; const files = [
{ t: ca, n: 'ca' },
let open = '<!-- translations -->';
let close = '<!-- translationsstop -->';
let readmeTxt = fs.readFileSync(readmePath, { encoding: 'utf8' });
let before = readmeTxt.split(open)[0];
let after = readmeTxt.split(close)[1];
let report = buildReport();
let alteredReadmeTxt = `${before}${open}\n\n${report}\n${close}${after}`;
fs.writeFileSync(readmePath, alteredReadmeTxt);
function buildReport(): string {
let files = [
{ t: de, n: 'de' }, { t: de, n: 'de' },
{ t: eo, n: 'eo' }, { t: eo, n: 'eo' },
{ t: es, n: 'es' }, { t: es, n: 'es' },
{ t: fi, n: 'fi' },
{ t: fr, n: 'fr' }, { t: fr, n: 'fr' },
{ t: it, n: 'it' }, { t: it, n: 'it' },
{ t: nl, n: 'nl' }, { t: nl, n: 'nl' },
{ t: ru, n: 'ru' }, { t: ru, n: 'ru' },
{ t: sv, n: 'sv' }, { t: sv, n: 'sv' },
{ t: zh, n: 'zh' }, { t: zh, n: 'zh' },
]; ];
let masterKeys = Object.keys(en.translation); const masterKeys = Object.keys(en.translation);
let report = 'lang | done | missing\n'; const readmePath = '../README.md';
report += '--- | --- | ---\n';
for (let file of files) { const open = '<!-- translations -->';
let keys = Object.keys(file.t.translation); const close = '<!-- translationsstop -->';
let pct: number = (keys.length / masterKeys.length) * 100;
let missing = difference(masterKeys, keys);
report += `${file.n} | ${pct.toFixed(0)}% | ${missing} \n`;
}
return report; const readmeTxt = fs.readFileSync(readmePath, { encoding: 'utf8' });
}
const before = readmeTxt.split(open)[0];
const after = readmeTxt.split(close)[1];
function difference(a: Array<string>, b: Array<string>): Array<string> { function difference(a: Array<string>, b: Array<string>): Array<string> {
return a.filter(x => !b.includes(x)); return a.filter(x => !b.includes(x));
} }
const report =
'lang | done | missing\n' +
'---- | ---- | -------\n' +
files
.map(file => {
const keys = Object.keys(file.t.translation);
const pct: number = (keys.length / masterKeys.length) * 100;
const missing = difference(masterKeys, keys);
return `${file.n} | ${pct.toFixed(0)}% | ${missing}`;
})
.join('\n');
const alteredReadmeTxt = `${before}${open}\n\n${report}\n${close}${after}`;
fs.writeFileSync(readmePath, alteredReadmeTxt);

33
ui/yarn.lock vendored
View file

@ -382,6 +382,11 @@ app-root-path@^2.0.1:
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==
arg@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.2.tgz#e70c90579e02c63d80e3ad4e31d8bfdb8bd50064"
integrity sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==
argparse@^1.0.7: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -1036,6 +1041,11 @@ destroy@~1.0.4:
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
doctrine@1.5.0: doctrine@1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
@ -2910,6 +2920,11 @@ loose-envify@^1.2.0, loose-envify@^1.4.0:
dependencies: dependencies:
js-tokens "^3.0.0 || ^4.0.0" js-tokens "^3.0.0 || ^4.0.0"
make-error@^1.1.1:
version "1.3.5"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==
map-cache@^0.2.2: map-cache@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@ -4123,7 +4138,7 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0" source-map-url "^0.4.0"
urix "^0.1.0" urix "^0.1.0"
source-map-support@~0.5.12: source-map-support@^0.5.6, source-map-support@~0.5.12:
version "0.5.16" version "0.5.16"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
@ -4477,6 +4492,17 @@ tributejs@^4.1.1:
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c" resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c"
integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw== integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw==
ts-node@^8.6.2:
version "8.6.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35"
integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==
dependencies:
arg "^4.1.0"
diff "^4.0.1"
make-error "^1.1.1"
source-map-support "^0.5.6"
yn "3.1.1"
ts-transform-classcat@^0.0.2: ts-transform-classcat@^0.0.2:
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/ts-transform-classcat/-/ts-transform-classcat-0.0.2.tgz#2386c9418f3a7c1f03261ff51225b70d0a7664fb" resolved "https://registry.yarnpkg.com/ts-transform-classcat/-/ts-transform-classcat-0.0.2.tgz#2386c9418f3a7c1f03261ff51225b70d0a7664fb"
@ -4769,3 +4795,8 @@ yaml@^1.7.2:
integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==
dependencies: dependencies:
"@babel/runtime" "^7.6.3" "@babel/runtime" "^7.6.3"
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==