mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-30 00:01:25 +00:00
i18n translations first pass.
This commit is contained in:
parent
411e7ef16e
commit
75e014ef5a
24 changed files with 378 additions and 213 deletions
|
@ -1,7 +1,10 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import { CommentNode as CommentNodeI, CommentForm as CommentFormI } from '../interfaces';
|
||||
import { capitalizeFirstLetter } from '../utils';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import * as autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface CommentFormProps {
|
||||
postId?: number;
|
||||
|
@ -25,12 +28,13 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
|||
post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId,
|
||||
creator_id: UserService.Instance.user ? UserService.Instance.user.id : null,
|
||||
},
|
||||
buttonTitle: !this.props.node ? "Post" : this.props.edit ? "Edit" : "Reply",
|
||||
buttonTitle: !this.props.node ? capitalizeFirstLetter(i18n.t('post')) : this.props.edit ? capitalizeFirstLetter(i18n.t('edit')) : capitalizeFirstLetter(i18n.t('reply')),
|
||||
}
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
|
||||
this.state = this.emptyState;
|
||||
|
||||
if (this.props.node) {
|
||||
|
@ -62,7 +66,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
|||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-sm btn-secondary mr-2" disabled={this.props.disabled}>{this.state.buttonTitle}</button>
|
||||
{this.props.node && <button type="button" class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.handleReplyCancel)}>Cancel</button>}
|
||||
{this.props.node && <button type="button" class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.handleReplyCancel)}><T i18nKey="cancel">#</T></button>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -7,6 +7,8 @@ import * as moment from 'moment';
|
|||
import { MomentTime } from './moment-time';
|
||||
import { CommentForm } from './comment-form';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
enum BanType {Community, Site};
|
||||
|
||||
|
@ -74,10 +76,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
<Link className="text-info" to={`/u/${node.comment.creator_name}`}>{node.comment.creator_name}</Link>
|
||||
</li>
|
||||
{this.isMod &&
|
||||
<li className="list-inline-item badge badge-light">mod</li>
|
||||
<li className="list-inline-item badge badge-light"><T i18nKey="mod">#</T></li>
|
||||
}
|
||||
{this.isAdmin &&
|
||||
<li className="list-inline-item badge badge-light">admin</li>
|
||||
<li className="list-inline-item badge badge-light"><T i18nKey="admin">#</T></li>
|
||||
}
|
||||
<li className="list-inline-item">
|
||||
<span>(
|
||||
|
@ -97,24 +99,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
{this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />}
|
||||
{!this.state.showEdit && !this.state.collapsed &&
|
||||
<div>
|
||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.deleted ? '*deleted*' : node.comment.content)} />
|
||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? `*${i18n.t('removed')}*` : node.comment.deleted ? `*${i18n.t('deleted')}*` : node.comment.content)} />
|
||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||
{UserService.Instance.user && !this.props.viewOnly &&
|
||||
<>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}>reply</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleReplyClick)}><T i18nKey="reply">#</T></span>
|
||||
</li>
|
||||
<li className="list-inline-item mr-2">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleSaveCommentClick)}>{node.comment.saved ? 'unsave' : 'save'}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleSaveCommentClick)}>{node.comment.saved ? i18n.t('unsave') : i18n.t('save')}</span>
|
||||
</li>
|
||||
{this.myComment &&
|
||||
<>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
|
||||
{!this.props.node.comment.deleted ? 'delete' : 'restore'}
|
||||
{!this.props.node.comment.deleted ? i18n.t('delete') : i18n.t('restore')}
|
||||
</span>
|
||||
</li>
|
||||
</>
|
||||
|
@ -123,8 +125,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
{this.canMod &&
|
||||
<li className="list-inline-item">
|
||||
{!this.props.node.comment.removed ?
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
|
@ -134,14 +136,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
{!this.isMod &&
|
||||
<li className="list-inline-item">
|
||||
{!this.props.node.comment.banned_from_community ?
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunityShow)}>ban</span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunitySubmit)}>unban</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunityShow)}><T i18nKey="ban">#</T></span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanFromCommunitySubmit)}><T i18nKey="unban">#</T></span>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
{!this.props.node.comment.banned_from_community &&
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{`${this.isMod ? 'remove' : 'appoint'} as mod`}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleAddModToCommunity)}>{this.isMod ? i18n.t('remove_as_mod') : i18n.t('appoint_as_mod')}</span>
|
||||
</li>
|
||||
}
|
||||
</>
|
||||
|
@ -152,14 +154,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
{!this.isAdmin &&
|
||||
<li className="list-inline-item">
|
||||
{!this.props.node.comment.banned ?
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}>ban from site</span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}>unban from site</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanShow)}><T i18nKey="ban_from_site">#</T></span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModBanSubmit)}><T i18nKey="unban_from_site">#</T></span>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
{!this.props.node.comment.banned &&
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleAddAdmin)}>{`${this.isAdmin ? 'remove' : 'appoint'} as admin`}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleAddAdmin)}>{this.isAdmin ? i18n.t('remove_as_admin') : i18n.t('appoint_as_admin')}</span>
|
||||
</li>
|
||||
}
|
||||
</>
|
||||
|
@ -167,11 +169,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
</>
|
||||
}
|
||||
<li className="list-inline-item">
|
||||
<Link className="text-muted" to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}>link</Link>
|
||||
<Link className="text-muted" to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}><T i18nKey="link">#</T></Link>
|
||||
</li>
|
||||
{this.props.markable &&
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleMarkRead)}>{`mark as ${node.comment.read ? 'unread' : 'read'}`}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleMarkRead)}>{node.comment.read ? i18n.t('mark_as_unread') : i18n.t('mark_as_read')}</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
@ -180,15 +182,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
</div>
|
||||
{this.state.showRemoveDialog &&
|
||||
<form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||
<input type="text" class="form-control mr-2" placeholder="Reason" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
|
||||
<button type="submit" class="btn btn-secondary">Remove Comment</button>
|
||||
<input type="text" class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
|
||||
<button type="submit" class="btn btn-secondary"><T i18nKey="remove_comment">#</T></button>
|
||||
</form>
|
||||
}
|
||||
{this.state.showBanDialog &&
|
||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label">Reason</label>
|
||||
<input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
|
||||
<label class="col-form-label"><T i18nKey="reason">#</T></label>
|
||||
<input type="text" class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.banReason} onInput={linkEvent(this, this.handleModBanReasonChange)} />
|
||||
</div>
|
||||
{/* TODO hold off on expires until later */}
|
||||
{/* <div class="form-group row"> */}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { retryWhen, delay, take } from 'rxjs/operators';
|
|||
import { UserOperation, Community, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm, ListCommunitiesForm, SortType } from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { msgOp } from '../utils';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
declare const Sortable: any;
|
||||
|
||||
|
@ -64,17 +65,17 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
{this.state.loading ?
|
||||
<h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> :
|
||||
<div>
|
||||
<h5>List of communities</h5>
|
||||
<h5><T i18nKey="list_of_communities">#</T></h5>
|
||||
<div class="table-responsive">
|
||||
<table id="community_table" class="table table-sm table-hover">
|
||||
<thead class="pointer">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th class="d-none d-lg-table-cell">Title</th>
|
||||
<th>Category</th>
|
||||
<th class="text-right">Subscribers</th>
|
||||
<th class="text-right d-none d-lg-table-cell">Posts</th>
|
||||
<th class="text-right d-none d-lg-table-cell">Comments</th>
|
||||
<th><T i18nKey="name">#</T></th>
|
||||
<th class="d-none d-lg-table-cell"><T i18nKey="title">#</T></th>
|
||||
<th><T i18nKey="category">#</T></th>
|
||||
<th class="text-right"><T i18nKey="subscribers">#</T></th>
|
||||
<th class="text-right d-none d-lg-table-cell"><T i18nKey="posts">#</T></th>
|
||||
<th class="text-right d-none d-lg-table-cell"><T i18nKey="comments">#</T></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -89,8 +90,8 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
<td class="text-right d-none d-lg-table-cell">{community.number_of_comments}</td>
|
||||
<td class="text-right">
|
||||
{community.subscribed ?
|
||||
<span class="pointer btn-link" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</span> :
|
||||
<span class="pointer btn-link" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</span>
|
||||
<span class="pointer btn-link" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></span> :
|
||||
<span class="pointer btn-link" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -109,9 +110,9 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
return (
|
||||
<div class="mt-2">
|
||||
{this.state.page > 1 &&
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
|
||||
}
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ import { Subscription } from "rxjs";
|
|||
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||
import { CommunityForm as CommunityFormI, UserOperation, Category, ListCategoriesResponse, CommunityResponse } from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { msgOp } from '../utils';
|
||||
import { msgOp, capitalizeFirstLetter } from '../utils';
|
||||
import * as autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
import { Community } from '../interfaces';
|
||||
|
||||
|
@ -74,25 +76,25 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
|||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Name</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="name">#</T></label>
|
||||
<div class="col-12">
|
||||
<input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} maxLength={20} pattern="[a-z0-9_]+" title="lowercase, underscores, and no spaces."/>
|
||||
<input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} maxLength={20} pattern="[a-z0-9_]+" title={i18n.t('community_reqs')}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Title</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="title">#</T></label>
|
||||
<div class="col-12">
|
||||
<input type="text" value={this.state.communityForm.title} onInput={linkEvent(this, this.handleCommunityTitleChange)} class="form-control" required minLength={3} maxLength={100} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Sidebar</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="sidebar">#</T></label>
|
||||
<div class="col-12">
|
||||
<textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={3} maxLength={10000} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Category</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="category">#</T></label>
|
||||
<div class="col-12">
|
||||
<select class="form-control" value={this.state.communityForm.category_id} onInput={linkEvent(this, this.handleCommunityCategoryChange)}>
|
||||
{this.state.categories.map(category =>
|
||||
|
@ -106,8 +108,8 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt
|
|||
<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.community ? 'Save' : 'Create'}</button>
|
||||
{this.props.community && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
|
||||
this.props.community ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('create'))}</button>
|
||||
{this.props.community && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { WebSocketService } from '../services';
|
|||
import { PostListings } from './post-listings';
|
||||
import { Sidebar } from './sidebar';
|
||||
import { msgOp, routeSortTypeToEnum, fetchLimit } from '../utils';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
community: CommunityI;
|
||||
|
@ -102,7 +103,7 @@ export class Community extends Component<any, State> {
|
|||
<div class="col-12 col-md-8">
|
||||
<h5>{this.state.community.title}
|
||||
{this.state.community.removed &&
|
||||
<small className="ml-2 text-muted font-italic">removed</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
|
||||
}
|
||||
</h5>
|
||||
{this.selects()}
|
||||
|
@ -126,15 +127,15 @@ export class Community extends Component<any, State> {
|
|||
return (
|
||||
<div className="mb-2">
|
||||
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto">
|
||||
<option disabled>Sort Type</option>
|
||||
<option value={SortType.Hot}>Hot</option>
|
||||
<option value={SortType.New}>New</option>
|
||||
<option disabled><T i18nKey="sort_type">#</T></option>
|
||||
<option value={SortType.Hot}><T i18nKey="hot">#</T></option>
|
||||
<option value={SortType.New}><T i18nKey="new">#</T></option>
|
||||
<option disabled>──────────</option>
|
||||
<option value={SortType.TopDay}>Top Day</option>
|
||||
<option value={SortType.TopWeek}>Week</option>
|
||||
<option value={SortType.TopMonth}>Month</option>
|
||||
<option value={SortType.TopYear}>Year</option>
|
||||
<option value={SortType.TopAll}>All</option>
|
||||
<option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
|
||||
<option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
|
||||
<option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
|
||||
<option value={SortType.TopYear}><T i18nKey="year">#</T></option>
|
||||
<option value={SortType.TopAll}><T i18nKey="all">#</T></option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
|
@ -144,9 +145,9 @@ export class Community extends Component<any, State> {
|
|||
return (
|
||||
<div class="mt-2">
|
||||
{this.state.page > 1 &&
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
|
||||
}
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { Component } from 'inferno';
|
|||
import { CommunityForm } from './community-form';
|
||||
import { Community } from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
export class CreateCommunity extends Component<any, any> {
|
||||
|
||||
|
@ -11,7 +13,7 @@ export class CreateCommunity extends Component<any, any> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.title = `Create Community - ${WebSocketService.Instance.site.name}`;
|
||||
document.title = `${i18n.t('create_community')} - ${WebSocketService.Instance.site.name}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -19,7 +21,7 @@ export class CreateCommunity extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>Create Community</h5>
|
||||
<h5><T i18nKey="create_community">#</T></h5>
|
||||
<CommunityForm onCreate={this.handleCommunityCreate}/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Component } from 'inferno';
|
||||
import { PostForm } from './post-form';
|
||||
import { WebSocketService } from '../services';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
export class CreatePost extends Component<any, any> {
|
||||
|
||||
|
@ -10,7 +12,7 @@ export class CreatePost extends Component<any, any> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.title = `Create Post - ${WebSocketService.Instance.site.name}`;
|
||||
document.title = `${i18n.t('create_post')} - ${WebSocketService.Instance.site.name}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -18,7 +20,7 @@ export class CreatePost extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>Create a Post</h5>
|
||||
<h5><T i18nKey="create_post">#</T></h5>
|
||||
<PostForm onCreate={this.handlePostCreate} prevCommunityName={this.prevCommunityName} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from 'inferno';
|
|||
import { Link } from 'inferno-router';
|
||||
import { repoUrl } from '../utils';
|
||||
import { version } from '../version';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
export class Footer extends Component<any, any> {
|
||||
|
||||
|
@ -19,16 +20,16 @@ export class Footer extends Component<any, any> {
|
|||
<span class="navbar-text">{version}</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/modlog">Modlog</Link>
|
||||
<Link class="nav-link" to="/modlog"><T i18nKey="modlog">#</T></Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href={`${repoUrl}/blob/master/docs/api.md`}>API</a>
|
||||
<a class="nav-link" href={`${repoUrl}/blob/master/docs/api.md`}><T i18nKey="api">#</T></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/sponsors">Sponsors</Link>
|
||||
<Link class="nav-link" to="/sponsors"><T i18nKey="sponsors">#</T></Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href={repoUrl}>Code</a>
|
||||
<a class="nav-link" href={repoUrl}><T i18nKey="code">#</T></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,8 @@ import { UserOperation, Comment, SortType, GetRepliesForm, GetRepliesResponse, C
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp } from '../utils';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
enum UnreadType {
|
||||
Unread, All
|
||||
|
@ -49,7 +51,7 @@ export class Inbox extends Component<any, InboxState> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.title = `/u/${UserService.Instance.user.username} Inbox - ${WebSocketService.Instance.site.name}`;
|
||||
document.title = `/u/${UserService.Instance.user.username} ${i18n.t('inbox')} - ${WebSocketService.Instance.site.name}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -59,12 +61,12 @@ export class Inbox extends Component<any, InboxState> {
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h5 class="mb-0">
|
||||
<span>Inbox for <Link to={`/u/${user.username}`}>{user.username}</Link></span>
|
||||
<span><T i18nKey="inbox_for" interpolation={{user: user.username}}>#<Link to={`/u/${user.username}`}>#</Link></T></span>
|
||||
</h5>
|
||||
{this.state.replies.length > 0 && this.state.unreadType == UnreadType.Unread &&
|
||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={this.markAllAsRead}>mark all as read</span>
|
||||
<span class="pointer" onClick={this.markAllAsRead}><T i18nKey="mark_all_as_read">#</T></span>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
@ -81,18 +83,18 @@ export class Inbox extends Component<any, InboxState> {
|
|||
return (
|
||||
<div className="mb-2">
|
||||
<select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select custom-select-sm w-auto">
|
||||
<option disabled>Type</option>
|
||||
<option value={UnreadType.Unread}>Unread</option>
|
||||
<option value={UnreadType.All}>All</option>
|
||||
<option disabled><T i18nKey="type">#</T></option>
|
||||
<option value={UnreadType.Unread}><T i18nKey="unread">#</T></option>
|
||||
<option value={UnreadType.All}><T i18nKey="all">#</T></option>
|
||||
</select>
|
||||
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
|
||||
<option disabled>Sort Type</option>
|
||||
<option value={SortType.New}>New</option>
|
||||
<option value={SortType.TopDay}>Top Day</option>
|
||||
<option value={SortType.TopWeek}>Week</option>
|
||||
<option value={SortType.TopMonth}>Month</option>
|
||||
<option value={SortType.TopYear}>Year</option>
|
||||
<option value={SortType.TopAll}>All</option>
|
||||
<option disabled><T i18nKey="sort_type">#</T></option>
|
||||
<option value={SortType.New}><T i18nKey="new">#</T></option>
|
||||
<option value={SortType.TopDay}><T i18nKey="top_day">top_day</T></option>
|
||||
<option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
|
||||
<option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
|
||||
<option value={SortType.TopYear}><T i18nKey="year">#</T></option>
|
||||
<option value={SortType.TopAll}><T i18nKey="all">#</T></option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
|
@ -113,9 +115,9 @@ export class Inbox extends Component<any, InboxState> {
|
|||
return (
|
||||
<div class="mt-2">
|
||||
{this.state.page > 1 &&
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
|
||||
}
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -196,7 +198,7 @@ export class Inbox extends Component<any, InboxState> {
|
|||
this.setState(this.state);
|
||||
} else if (op == UserOperation.CreateComment) {
|
||||
// let res: CommentResponse = msg;
|
||||
alert('Reply sent');
|
||||
alert(i18n.t('reply_sent'));
|
||||
// this.state.replies.unshift(res.comment); // TODO do this right
|
||||
// this.setState(this.state);
|
||||
} else if (op == UserOperation.SaveComment) {
|
||||
|
|
|
@ -4,6 +4,8 @@ import { retryWhen, delay, take } from 'rxjs/operators';
|
|||
import { LoginForm, RegisterForm, LoginResponse, UserOperation } from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
loginForm: LoginForm;
|
||||
|
@ -74,13 +76,13 @@ export class Login extends Component<any, State> {
|
|||
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
||||
<h5>Login</h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Email or Username</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="email_or_username">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" value={this.state.loginForm.username_or_email} onInput={linkEvent(this, this.handleLoginUsernameChange)} required minLength={3} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Password</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" value={this.state.loginForm.password} onInput={linkEvent(this, this.handleLoginPasswordChange)} class="form-control" required />
|
||||
</div>
|
||||
|
@ -88,38 +90,37 @@ export class Login extends Component<any, State> {
|
|||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<button type="submit" class="btn btn-secondary">{this.state.loginLoading ?
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : 'Login'}</button>
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('login')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/* Forgot your password or deleted your account? Reset your password. TODO */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
registerForm() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h5>Sign Up</h5>
|
||||
<h5><T i18nKey="sign_up">#</T></h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Username</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="username">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" value={this.state.registerForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} maxLength={20} pattern="[a-zA-Z0-9_]+" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Email</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="email">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" placeholder="Optional" value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
||||
<input type="email" class="form-control" placeholder={i18n.t('optional')} value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Password</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" value={this.state.registerForm.password} onInput={linkEvent(this, this.handleRegisterPasswordChange)} class="form-control" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Verify Password</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="verify_password">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" value={this.state.registerForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
|
||||
</div>
|
||||
|
@ -127,7 +128,7 @@ export class Login extends Component<any, State> {
|
|||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<button type="submit" class="btn btn-secondary">{this.state.registerLoading ?
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : 'Sign Up'}</button>
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component } from 'inferno';
|
||||
import * as moment from 'moment';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
interface MomentTimeProps {
|
||||
data: {
|
||||
|
@ -18,7 +19,7 @@ export class MomentTime extends Component<MomentTimeProps, any> {
|
|||
render() {
|
||||
if (this.props.data.updated) {
|
||||
return (
|
||||
<span title={this.props.data.updated} className="font-italics">modified {moment.utc(this.props.data.updated).fromNow()}</span>
|
||||
<span title={this.props.data.updated} className="font-italics">{i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()}</span>
|
||||
)
|
||||
} else {
|
||||
let str = this.props.data.published || this.props.data.when_;
|
||||
|
|
|
@ -6,6 +6,8 @@ import { WebSocketService, UserService } from '../services';
|
|||
import { UserOperation, GetRepliesForm, GetRepliesResponse, SortType, GetSiteResponse, Comment} from '../interfaces';
|
||||
import { msgOp } from '../utils';
|
||||
import { version } from '../version';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface NavbarState {
|
||||
isLoggedIn: boolean;
|
||||
|
@ -85,16 +87,16 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
<div className={`${!this.state.expanded && 'collapse'} navbar-collapse`}>
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/communities">Communities</Link>
|
||||
<Link class="nav-link" to="/communities"><T i18nKey="communities">#</T></Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/search">Search</Link>
|
||||
<Link class="nav-link" to="/search"><T i18nKey="search">#</T></Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to={{pathname: '/create_post', state: { prevPath: this.currentLocation }}}>Create Post</Link>
|
||||
<Link class="nav-link" to={{pathname: '/create_post', state: { prevPath: this.currentLocation }}}><T i18nKey="create_post">#</T></Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/create_community">Create Community</Link>
|
||||
<Link class="nav-link" to="/create_community"><T i18nKey="create_community">#</T></Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto mr-2">
|
||||
|
@ -113,13 +115,13 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
{UserService.Instance.user.username}
|
||||
</a>
|
||||
<div className={`dropdown-menu dropdown-menu-right ${this.state.expandUserDropdown && 'show'}`}>
|
||||
<a role="button" class="dropdown-item pointer" onClick={linkEvent(this, this.handleOverviewClick)}>Overview</a>
|
||||
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a>
|
||||
<a role="button" class="dropdown-item pointer" onClick={linkEvent(this, this.handleOverviewClick)}><T i18nKey="overview">#</T></a>
|
||||
<a role="button" class="dropdown-item pointer" onClick={ linkEvent(this, this.handleLogoutClick) }><T i18nKey="logout">#</T></a>
|
||||
</div>
|
||||
</li>
|
||||
</>
|
||||
:
|
||||
<Link class="nav-link" to="/login">Login / Sign up</Link>
|
||||
<Link class="nav-link" to="/login"><T i18nKey="login_sign_up">#</T></Link>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -209,7 +211,7 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
if (UserService.Instance.user) {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
if (!Notification) {
|
||||
alert('Desktop notifications not available in your browser. Try Chromium.');
|
||||
alert(i18n.t('notifications_error'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -224,7 +226,7 @@ export class Navbar extends Component<any, NavbarState> {
|
|||
if (Notification.permission !== 'granted')
|
||||
Notification.requestPermission();
|
||||
else {
|
||||
var notification = new Notification(`${replies.length} Unread Messages`, {
|
||||
var notification = new Notification(`${replies.length} ${i18n.t('unread_messages')}`, {
|
||||
icon: `${window.location.protocol}//${window.location.host}/static/assets/apple-touch-icon.png`,
|
||||
body: `${recentReply.creator_name}: ${recentReply.content}`
|
||||
});
|
||||
|
|
|
@ -4,8 +4,10 @@ import { Subscription } from "rxjs";
|
|||
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse, ListCommunitiesForm, SortType, SearchForm, SearchType, SearchResponse } from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp, getPageTitle, debounce } from '../utils';
|
||||
import { msgOp, getPageTitle, debounce, capitalizeFirstLetter } from '../utils';
|
||||
import * as autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostFormProps {
|
||||
post?: Post; // If a post is given, that means this is an edit
|
||||
|
@ -85,28 +87,28 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
<div>
|
||||
<form onSubmit={linkEvent(this, this.handlePostSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">URL</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="url">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="url" class="form-control" value={this.state.postForm.url} onInput={linkEvent(this, debounce(this.handlePostUrlChange))} />
|
||||
{this.state.suggestedTitle &&
|
||||
<div class="mt-1 text-muted small font-weight-bold pointer" onClick={linkEvent(this, this.copySuggestedTitle)}>copy suggested title: {this.state.suggestedTitle}</div>
|
||||
<div class="mt-1 text-muted small font-weight-bold pointer" onClick={linkEvent(this, this.copySuggestedTitle)}><T i18nKey="copy_suggested_title" interpolation={{title: this.state.suggestedTitle}}>#</T></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Title</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="title">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<textarea value={this.state.postForm.name} onInput={linkEvent(this, debounce(this.handlePostNameChange))} class="form-control" required rows={2} minLength={3} maxLength={100} />
|
||||
{this.state.suggestedPosts.length > 0 &&
|
||||
<>
|
||||
<div class="my-1 text-muted small font-weight-bold">These posts might be related</div>
|
||||
<div class="my-1 text-muted small font-weight-bold"><T i18nKey="related_posts">#</T></div>
|
||||
<PostListings posts={this.state.suggestedPosts} />
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Body</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="body">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<textarea value={this.state.postForm.body} onInput={linkEvent(this, this.handlePostBodyChange)} class="form-control" rows={4} maxLength={10000} />
|
||||
</div>
|
||||
|
@ -114,7 +116,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
{/* Cant change a community from an edit */}
|
||||
{!this.props.post &&
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Community</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="community">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" value={this.state.postForm.community_id} onInput={linkEvent(this, this.handlePostCommunityChange)}>
|
||||
{this.state.communities.map(community =>
|
||||
|
@ -129,8 +131,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
<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.post ? 'Save' : 'Create'}</button>
|
||||
{this.props.post && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
|
||||
this.props.post ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('Create'))}</button>
|
||||
{this.props.post && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -5,6 +5,8 @@ import { Post, CreatePostLikeForm, PostForm as PostFormI, SavePostForm, Communit
|
|||
import { MomentTime } from './moment-time';
|
||||
import { PostForm } from './post-form';
|
||||
import { mdToHtml, canMod, isMod, isImage } from '../utils';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostListingState {
|
||||
showEdit: boolean;
|
||||
|
@ -67,7 +69,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</div>
|
||||
</div>
|
||||
{post.url && isImage(post.url) &&
|
||||
<span title="Expand here" class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)}><img class="mx-2 float-left img-fluid thumbnail rounded" src={post.url} /></span>
|
||||
<span title={i18n.t('expand_here')} class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)}><img class="mx-2 float-left img-fluid thumbnail rounded" src={post.url} /></span>
|
||||
}
|
||||
<div className="ml-4">
|
||||
<div>
|
||||
|
@ -83,18 +85,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</small>
|
||||
}
|
||||
{post.removed &&
|
||||
<small className="ml-2 text-muted font-italic">removed</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
|
||||
}
|
||||
{post.deleted &&
|
||||
<small className="ml-2 text-muted font-italic">deleted</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
|
||||
}
|
||||
{post.locked &&
|
||||
<small className="ml-2 text-muted font-italic">locked</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="locked">#</T></small>
|
||||
}
|
||||
{ post.url && isImage(post.url) &&
|
||||
<>
|
||||
{ !this.state.imageExpanded
|
||||
? <span class="text-monospace pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleImageExpandClick)}>[+]</span>
|
||||
? <span class="text-monospace pointer ml-2 text-muted small" title={i18n.t('expand_here')} onClick={linkEvent(this, this.handleImageExpandClick)}>[+]</span>
|
||||
:
|
||||
<span>
|
||||
<span class="text-monospace pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleImageExpandClick)}>[-]</span>
|
||||
|
@ -113,10 +115,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
<span>by </span>
|
||||
<Link className="text-info" to={`/u/${post.creator_name}`}>{post.creator_name}</Link>
|
||||
{this.isMod &&
|
||||
<span className="mx-1 badge badge-light">mod</span>
|
||||
<span className="mx-1 badge badge-light"><T i18nKey="mod">#</T></span>
|
||||
}
|
||||
{this.isAdmin &&
|
||||
<span className="mx-1 badge badge-light">admin</span>
|
||||
<span className="mx-1 badge badge-light"><T i18nKey="admin">#</T></span>
|
||||
}
|
||||
{this.props.showCommunity &&
|
||||
<span>
|
||||
|
@ -137,22 +139,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</span>
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<Link className="text-muted" to={`/post/${post.id}`}>{post.number_of_comments} Comments</Link>
|
||||
<Link className="text-muted" to={`/post/${post.id}`}><T i18nKey="number_of_comments" interpolation={{count: post.number_of_comments}}>#</T></Link>
|
||||
</li>
|
||||
</ul>
|
||||
{UserService.Instance.user && this.props.editable &&
|
||||
<ul class="list-inline mb-1 text-muted small font-weight-bold">
|
||||
<li className="list-inline-item mr-2">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? 'unsave' : 'save'}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleSavePostClick)}>{post.saved ? i18n.t('unsave') : i18n.t('save')}</span>
|
||||
</li>
|
||||
{this.myPost &&
|
||||
<>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
|
||||
</li>
|
||||
<li className="list-inline-item mr-2">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
|
||||
{!post.deleted ? 'delete' : 'restore'}
|
||||
{!post.deleted ? i18n.t('delete') : i18n.t('restore')}
|
||||
</span>
|
||||
</li>
|
||||
</>
|
||||
|
@ -161,12 +163,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
<span>
|
||||
<li className="list-inline-item">
|
||||
{!this.props.post.removed ?
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
|
||||
}
|
||||
</li>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{this.props.post.locked ? 'unlock' : 'lock'}</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModLock)}>{this.props.post.locked ? i18n.t('unlock') : i18n.t('lock')}</span>
|
||||
</li>
|
||||
</span>
|
||||
}
|
||||
|
@ -175,7 +177,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
{this.state.showRemoveDialog &&
|
||||
<form class="form-inline" onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||
<input type="text" class="form-control mr-2" placeholder="Reason" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
|
||||
<button type="submit" class="btn btn-secondary">Remove Post</button>
|
||||
<button type="submit" class="btn btn-secondary"><T i18nKey="remove_post">#</T></button>
|
||||
</form>
|
||||
}
|
||||
{this.props.showBody && this.props.post.body && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(post.body)} />}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from 'inferno';
|
|||
import { Link } from 'inferno-router';
|
||||
import { Post } from '../interfaces';
|
||||
import { PostListing } from './post-listing';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostListingsProps {
|
||||
posts: Array<Post>;
|
||||
|
@ -19,8 +20,10 @@ export class PostListings extends Component<PostListingsProps, any> {
|
|||
<div>
|
||||
{this.props.posts.length > 0 ? this.props.posts.map(post =>
|
||||
<PostListing post={post} showCommunity={this.props.showCommunity} />) :
|
||||
<div>No posts. {this.props.showCommunity !== undefined && <span>Subscribe to some <Link to="/communities">communities</Link>.</span>}
|
||||
</div>
|
||||
<>
|
||||
<div><T i18nKey="no_posts">#</T></div>
|
||||
{this.props.showCommunity !== undefined && <div><T i18nKey="subscribe_to_communities">#<Link to="/communities">#</Link></T></div>}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -9,6 +9,8 @@ import { Sidebar } from './sidebar';
|
|||
import { CommentForm } from './comment-form';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import * as autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface PostState {
|
||||
post: PostI;
|
||||
|
@ -130,17 +132,17 @@ export class Post extends Component<any, PostState> {
|
|||
sortRadios() {
|
||||
return (
|
||||
<div class="btn-group btn-group-toggle mb-3">
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Hot && 'active'}`}>Hot
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Hot && 'active'}`}>{i18n.t('hot')}
|
||||
<input type="radio" value={CommentSortType.Hot}
|
||||
checked={this.state.commentSort === CommentSortType.Hot}
|
||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||
</label>
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Top && 'active'}`}>Top
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.Top && 'active'}`}>{i18n.t('top')}
|
||||
<input type="radio" value={CommentSortType.Top}
|
||||
checked={this.state.commentSort === CommentSortType.Top}
|
||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||
</label>
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.New && 'active'}`}>New
|
||||
<label className={`btn btn-sm btn-secondary pointer ${this.state.commentSort === CommentSortType.New && 'active'}`}>{i18n.t('new')}
|
||||
<input type="radio" value={CommentSortType.New}
|
||||
checked={this.state.commentSort === CommentSortType.New}
|
||||
onChange={linkEvent(this, this.handleCommentSortChange)} />
|
||||
|
@ -152,7 +154,7 @@ export class Post extends Component<any, PostState> {
|
|||
newComments() {
|
||||
return (
|
||||
<div class="container-fluid sticky-top new-comments">
|
||||
<h5>Chat</h5>
|
||||
<h5><T i18nKey="chat">#</T></h5>
|
||||
<CommentForm postId={this.state.post.id} disabled={this.state.post.locked} />
|
||||
{this.state.comments.map(comment =>
|
||||
<CommentNodes
|
||||
|
|
|
@ -6,6 +6,8 @@ import { WebSocketService } from '../services';
|
|||
import { msgOp, fetchLimit } from '../utils';
|
||||
import { PostListing } from './post-listing';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SearchState {
|
||||
q: string,
|
||||
|
@ -87,7 +89,7 @@ export class Search extends Component<any, SearchState> {
|
|||
<button type="submit" class="btn btn-secondary mr-2">
|
||||
{this.state.loading ?
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> :
|
||||
<span>Search</span>
|
||||
<span><T i18nKey="search">#</T></span>
|
||||
}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -98,19 +100,19 @@ export class Search extends Component<any, SearchState> {
|
|||
return (
|
||||
<div className="mb-2">
|
||||
<select value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select custom-select-sm w-auto">
|
||||
<option disabled>Type</option>
|
||||
<option value={SearchType.Both}>Both</option>
|
||||
<option value={SearchType.Comments}>Comments</option>
|
||||
<option value={SearchType.Posts}>Posts</option>
|
||||
<option disabled><T i18nKey="type">#</T></option>
|
||||
<option value={SearchType.Both}><T i18nKey="both">#</T></option>
|
||||
<option value={SearchType.Comments}><T i18nKey="comments">#</T></option>
|
||||
<option value={SearchType.Posts}><T i18nKey="posts">#</T></option>
|
||||
</select>
|
||||
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
|
||||
<option disabled>Sort Type</option>
|
||||
<option value={SortType.New}>New</option>
|
||||
<option value={SortType.TopDay}>Top Day</option>
|
||||
<option value={SortType.TopWeek}>Week</option>
|
||||
<option value={SortType.TopMonth}>Month</option>
|
||||
<option value={SortType.TopYear}>Year</option>
|
||||
<option value={SortType.TopAll}>All</option>
|
||||
<option disabled><T i18nKey="sort_type">#</T></option>
|
||||
<option value={SortType.New}><T i18nKey="new">#</T></option>
|
||||
<option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
|
||||
<option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
|
||||
<option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
|
||||
<option value={SortType.TopYear}><T i18nKey="year">#</T></option>
|
||||
<option value={SortType.TopAll}><T i18nKey="all">#</T></option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
|
@ -171,9 +173,9 @@ export class Search extends Component<any, SearchState> {
|
|||
return (
|
||||
<div class="mt-2">
|
||||
{this.state.page > 1 &&
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
|
||||
}
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ export class Search extends Component<any, SearchState> {
|
|||
return (
|
||||
<div>
|
||||
{res && res.op && res.posts.length == 0 && res.comments.length == 0 &&
|
||||
<span>No Results</span>
|
||||
<span><T i18nKey="no_results">#</T></span>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
@ -250,7 +252,7 @@ export class Search extends Component<any, SearchState> {
|
|||
let res: SearchResponse = msg;
|
||||
this.state.searchResponse = res;
|
||||
this.state.loading = false;
|
||||
document.title = `Search - ${this.state.q} - ${WebSocketService.Instance.site.name}`;
|
||||
document.title = `${i18n.t('search')} - ${this.state.q} - ${WebSocketService.Instance.site.name}`;
|
||||
window.scrollTo(0,0);
|
||||
this.setState(this.state);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import { RegisterForm, LoginResponse, UserOperation } from '../interfaces';
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp } from '../utils';
|
||||
import { SiteForm } from './site-form';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface State {
|
||||
userForm: RegisterForm;
|
||||
|
@ -46,7 +48,7 @@ export class Setup extends Component<any, State> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.title = "Setup - Lemmy";
|
||||
document.title = `${i18n.t('setup')} - Lemmy`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -54,7 +56,7 @@ export class Setup extends Component<any, State> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 offset-lg-3 col-lg-6">
|
||||
<h3>Lemmy Instance Setup</h3>
|
||||
<h3><T i18nKey="lemmy_instance_setup">#</T></h3>
|
||||
{!this.state.doneRegisteringUser ? this.registerUser() : <SiteForm />}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,27 +67,27 @@ export class Setup extends Component<any, State> {
|
|||
registerUser() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h5>Set up Site Administrator</h5>
|
||||
<h5><T i18nKey="setup_admin">#</T></h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Username</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="username">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" value={this.state.userForm.username} onInput={linkEvent(this, this.handleRegisterUsernameChange)} required minLength={3} maxLength={20} pattern="[a-zA-Z0-9_]+" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Email</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="email">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" class="form-control" placeholder="Optional" value={this.state.userForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
||||
<input type="email" class="form-control" placeholder={i18n.t('optional')} value={this.state.userForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Password</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="password">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" value={this.state.userForm.password} onInput={linkEvent(this, this.handleRegisterPasswordChange)} class="form-control" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Verify Password</label>
|
||||
<label class="col-sm-2 col-form-label"><T i18nKey="verify_password">#</T></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" value={this.state.userForm.password_verify} onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)} class="form-control" required />
|
||||
</div>
|
||||
|
@ -93,7 +95,7 @@ export class Setup extends Component<any, State> {
|
|||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<button type="submit" class="btn btn-secondary">{this.state.userLoading ?
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : 'Sign Up'}</button>
|
||||
<svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg> : i18n.t('sign_up')}</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,8 @@ import { Community, CommunityUser, FollowCommunityForm, CommunityForm as Communi
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { mdToHtml, getUnixTime } from '../utils';
|
||||
import { CommunityForm } from './community-form';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SidebarProps {
|
||||
community: Community;
|
||||
|
@ -54,10 +56,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
<div>
|
||||
<h5 className="mb-0">{community.title}
|
||||
{community.removed &&
|
||||
<small className="ml-2 text-muted font-italic">removed</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="removed">#</T></small>
|
||||
}
|
||||
{community.deleted &&
|
||||
<small className="ml-2 text-muted font-italic">deleted</small>
|
||||
<small className="ml-2 text-muted font-italic"><T i18nKey="deleted">#</T></small>
|
||||
}
|
||||
</h5>
|
||||
<Link className="text-muted" to={`/c/${community.name}`}>/c/{community.name}</Link>
|
||||
|
@ -65,12 +67,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{this.canMod &&
|
||||
<>
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleEditClick)}><T i18nKey="edit">#</T></span>
|
||||
</li>
|
||||
{this.amCreator &&
|
||||
<li className="list-inline-item">
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>
|
||||
{!community.deleted ? 'delete' : 'restore'}
|
||||
{!community.deleted ? i18n.t('delete') : i18n.t('restore')}
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
|
@ -79,8 +81,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{this.canAdmin &&
|
||||
<li className="list-inline-item">
|
||||
{!this.props.community.removed ?
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}>remove</span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}>restore</span>
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)}><T i18nKey="remove">#</T></span> :
|
||||
<span class="pointer" onClick={linkEvent(this, this.handleModRemoveSubmit)}><T i18nKey="restore">#</T></span>
|
||||
}
|
||||
</li>
|
||||
|
||||
|
@ -89,8 +91,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{this.state.showRemoveDialog &&
|
||||
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||
<div class="form-group row">
|
||||
<label class="col-form-label">Reason</label>
|
||||
<input type="text" class="form-control mr-2" placeholder="Optional" value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
|
||||
<label class="col-form-label"><T i18nKey="reason">#</T></label>
|
||||
<input type="text" class="form-control mr-2" placeholder={i18n.t('optional')} value={this.state.removeReason} onInput={linkEvent(this, this.handleModRemoveReasonChange)} />
|
||||
</div>
|
||||
{/* TODO hold off on expires for now */}
|
||||
{/* <div class="form-group row"> */}
|
||||
|
@ -98,29 +100,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
{/* <input type="date" class="form-control mr-2" placeholder="Expires" value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
|
||||
{/* </div> */}
|
||||
<div class="form-group row">
|
||||
<button type="submit" class="btn btn-secondary">Remove Community</button>
|
||||
<button type="submit" class="btn btn-secondary"><T i18nKey="remove_community">#</T></button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
<ul class="my-1 list-inline">
|
||||
<li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
|
||||
<li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
|
||||
<li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
|
||||
<li className="list-inline-item badge badge-light">{community.number_of_comments} Comments</li>
|
||||
<li className="list-inline-item"><Link className="badge badge-light" to={`/modlog/community/${this.props.community.id}`}>Modlog</Link></li>
|
||||
<li className="list-inline-item badge badge-light"><T i18nKey="number_of_subscribers" interpolation={{count: community.number_of_subscribers}}>#</T></li>
|
||||
<li className="list-inline-item badge badge-light"><T i18nKey="number_of_posts" interpolation={{count: community.number_of_posts}}>#</T></li>
|
||||
<li className="list-inline-item badge badge-light"><T i18nKey="number_of_comments" interpolation={{count: community.number_of_comments}}>#</T></li>
|
||||
<li className="list-inline-item"><Link className="badge badge-light" to={`/modlog/community/${this.props.community.id}`}><T i18nKey="modlog">#</T></Link></li>
|
||||
</ul>
|
||||
<ul class="list-inline small">
|
||||
<li class="list-inline-item">mods: </li>
|
||||
<li class="list-inline-item">{i18n.t('mods')}: </li>
|
||||
{this.props.moderators.map(mod =>
|
||||
<li class="list-inline-item"><Link class="text-info" to={`/u/${mod.user_name}`}>{mod.user_name}</Link></li>
|
||||
)}
|
||||
</ul>
|
||||
<Link class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted || community.removed) && 'no-click'}`}
|
||||
to={`/create_post/c/${community.name}`}>Create a Post</Link>
|
||||
to={`/create_post/c/${community.name}`}><T i18nKey="create_a_post">#</T></Link>
|
||||
<div>
|
||||
{community.subscribed
|
||||
? <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</button>
|
||||
: <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleSubscribe)}>Subscribe</button>
|
||||
? <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleUnsubscribe)}><T i18nKey="unsubscribe">#</T></button>
|
||||
: <button class="btn btn-sm btn-secondary btn-block mb-3" onClick={linkEvent(community.id, this.handleSubscribe)}><T i18nKey="subscribe">#</T></button>
|
||||
}
|
||||
</div>
|
||||
{community.description &&
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import { Site, SiteForm as SiteFormI } from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { capitalizeFirstLetter } from '../utils';
|
||||
import * as autosize from 'autosize';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
interface SiteFormProps {
|
||||
site?: Site; // If a site is given, that means this is an edit
|
||||
|
@ -39,15 +42,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
render() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
|
||||
<h5>{`${this.props.site ? 'Edit' : 'Name'} your Site`}</h5>
|
||||
<h5>{`${this.props.site ? capitalizeFirstLetter(i18n.t('edit')) : capitalizeFirstLetter(i18n.t('name'))} ${i18n.t('your_site')}`}</h5>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Name</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="name">#</T></label>
|
||||
<div class="col-12">
|
||||
<input type="text" class="form-control" value={this.state.siteForm.name} onInput={linkEvent(this, this.handleSiteNameChange)} required minLength={3} maxLength={20} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">Sidebar</label>
|
||||
<label class="col-12 col-form-label"><T i18nKey="sidebar">#</T></label>
|
||||
<div class="col-12">
|
||||
<textarea value={this.state.siteForm.description} onInput={linkEvent(this, this.handleSiteDescriptionChange)} class="form-control" rows={3} maxLength={10000} />
|
||||
</div>
|
||||
|
@ -57,8 +60,8 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
<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.site ? 'Save' : 'Create'}</button>
|
||||
{this.props.site && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}>Cancel</button>}
|
||||
this.props.site ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('create'))}</button>
|
||||
{this.props.site && <button type="button" class="btn btn-secondary" onClick={linkEvent(this, this.handleCancel)}><T i18nKey="cancel">#</T></button>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Component } from 'inferno';
|
||||
import { WebSocketService } from '../services';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
let general =
|
||||
[
|
||||
|
@ -18,7 +20,7 @@ export class Sponsors extends Component<any, any> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.title = `Sponsors - ${WebSocketService.Instance.site.name}`;
|
||||
document.title = `${i18n.t('sponsors')} - ${WebSocketService.Instance.site.name}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -36,19 +38,19 @@ export class Sponsors extends Component<any, any> {
|
|||
topMessage() {
|
||||
return (
|
||||
<div>
|
||||
<h5>Sponsors of Lemmy</h5>
|
||||
<h5><T i18nKey="sponsors_of_lemmy">#</T></h5>
|
||||
<p>
|
||||
Lemmy is free, <a href="https://github.com/dessalines/lemmy">open-source</a> 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:
|
||||
<T i18nKey="sponsor_message">#<a href="https://github.com/dessalines/lemmy">#</a></T>
|
||||
</p>
|
||||
<a class="btn btn-secondary" href="https://www.patreon.com/dessalines">Support on Patreon</a>
|
||||
<a class="btn btn-secondary" href="https://www.patreon.com/dessalines"><T i18nKey="support_on_patreon">#</T></a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
sponsors() {
|
||||
return (
|
||||
<div class="container">
|
||||
<h5>Sponsors</h5>
|
||||
<p>General Sponsors are those that pledged $10 to $39 to Lemmy.</p>
|
||||
<h5><T i18nKey="sponsors">#</T></h5>
|
||||
<p><T i18nKey="general_sponsors">#</T></p>
|
||||
<div class="row card-columns">
|
||||
{general.map(s =>
|
||||
<div class="card col-12 col-md-2">
|
||||
|
@ -68,11 +70,11 @@ export class Sponsors extends Component<any, any> {
|
|||
<table class="table table-hover text-center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Bitcoin</td>
|
||||
<td><T i18nKey="bitcoin">#</T></td>
|
||||
<td><code>1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ethereum</td>
|
||||
<td><T i18nKey="ethereum">#</T></td>
|
||||
<td><code>0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -8,6 +8,8 @@ import { msgOp, fetchLimit, routeSortTypeToEnum, capitalizeFirstLetter } from '.
|
|||
import { PostListing } from './post-listing';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
import { MomentTime } from './moment-time';
|
||||
import { i18n } from '../i18next';
|
||||
import { T } from 'inferno-i18next';
|
||||
|
||||
enum View {
|
||||
Overview, Comments, Posts, Saved
|
||||
|
@ -142,20 +144,20 @@ export class User extends Component<any, UserState> {
|
|||
return (
|
||||
<div className="mb-2">
|
||||
<select value={this.state.view} onChange={linkEvent(this, this.handleViewChange)} class="custom-select custom-select-sm w-auto">
|
||||
<option disabled>View</option>
|
||||
<option value={View.Overview}>Overview</option>
|
||||
<option value={View.Comments}>Comments</option>
|
||||
<option value={View.Posts}>Posts</option>
|
||||
<option value={View.Saved}>Saved</option>
|
||||
<option disabled><T i18nKey="view">#</T></option>
|
||||
<option value={View.Overview}><T i18nKey="overview">#</T></option>
|
||||
<option value={View.Comments}><T i18nKey="comments">#</T></option>
|
||||
<option value={View.Posts}><T i18nKey="posts">#</T></option>
|
||||
<option value={View.Saved}><T i18nKey="saved">#</T></option>
|
||||
</select>
|
||||
<select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2">
|
||||
<option disabled>Sort Type</option>
|
||||
<option value={SortType.New}>New</option>
|
||||
<option value={SortType.TopDay}>Top Day</option>
|
||||
<option value={SortType.TopWeek}>Week</option>
|
||||
<option value={SortType.TopMonth}>Month</option>
|
||||
<option value={SortType.TopYear}>Year</option>
|
||||
<option value={SortType.TopAll}>All</option>
|
||||
<option disabled><T i18nKey="sort_type">#</T></option>
|
||||
<option value={SortType.New}><T i18nKey="new">#</T></option>
|
||||
<option value={SortType.TopDay}><T i18nKey="top_day">#</T></option>
|
||||
<option value={SortType.TopWeek}><T i18nKey="week">#</T></option>
|
||||
<option value={SortType.TopMonth}><T i18nKey="month">#</T></option>
|
||||
<option value={SortType.TopYear}><T i18nKey="year">#</T></option>
|
||||
<option value={SortType.TopAll}><T i18nKey="all">#</T></option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
|
@ -220,12 +222,12 @@ export class User extends Component<any, UserState> {
|
|||
<div>Joined <MomentTime data={user} /></div>
|
||||
<table class="table table-bordered table-sm mt-2">
|
||||
<tr>
|
||||
<td>{user.post_score} points</td>
|
||||
<td>{user.number_of_posts} posts</td>
|
||||
<td><T i18nKey="number_of_points" interpolation={{count: user.post_score}}>#</T></td>
|
||||
<td><T i18nKey="number_of_posts" interpolation={{count: user.number_of_posts}}>#</T></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{user.comment_score} points</td>
|
||||
<td>{user.number_of_comments} comments</td>
|
||||
<td><T i18nKey="number_of_points" interpolation={{count: user.comment_score}}>#</T></td>
|
||||
<td><T i18nKey="number_of_comments" interpolation={{count: user.number_of_comments}}>#</T></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
|
@ -238,7 +240,7 @@ export class User extends Component<any, UserState> {
|
|||
<div>
|
||||
{this.state.moderates.length > 0 &&
|
||||
<div>
|
||||
<h5>Moderates</h5>
|
||||
<h5><T i18nKey="moderates">#</T></h5>
|
||||
<ul class="list-unstyled">
|
||||
{this.state.moderates.map(community =>
|
||||
<li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
|
||||
|
@ -256,7 +258,7 @@ export class User extends Component<any, UserState> {
|
|||
{this.state.follows.length > 0 &&
|
||||
<div>
|
||||
<hr />
|
||||
<h5>Subscribed</h5>
|
||||
<h5><T i18nKey="subscribed">#</T></h5>
|
||||
<ul class="list-unstyled">
|
||||
{this.state.follows.map(community =>
|
||||
<li><Link to={`/c/${community.community_name}`}>{community.community_name}</Link></li>
|
||||
|
@ -272,9 +274,9 @@ export class User extends Component<any, UserState> {
|
|||
return (
|
||||
<div class="mt-2">
|
||||
{this.state.page > 1 &&
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
|
||||
<button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}><T i18nKey="prev">#</T></button>
|
||||
}
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
|
||||
<button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}><T i18nKey="next">#</T></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -359,7 +361,7 @@ export class User extends Component<any, UserState> {
|
|||
this.setState(this.state);
|
||||
} else if (op == UserOperation.CreateComment) {
|
||||
// let res: CommentResponse = msg;
|
||||
alert('Reply sent');
|
||||
alert(i18n.t('reply_sent'));
|
||||
// this.state.comments.unshift(res.comment); // TODO do this right
|
||||
// this.setState(this.state);
|
||||
} else if (op == UserOperation.SaveComment) {
|
||||
|
|
|
@ -1,20 +1,141 @@
|
|||
import * as i18next from 'i18next';
|
||||
import * as i18n from 'i18next';
|
||||
|
||||
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
|
||||
//
|
||||
// TODO don't forget to add moment locales for new languages.
|
||||
const resources = {
|
||||
en: {
|
||||
translation: {
|
||||
post: 'post',
|
||||
edit: 'edit',
|
||||
reply: 'reply',
|
||||
cancel: 'Cancel',
|
||||
unlock: 'unlock',
|
||||
lock: 'lock',
|
||||
link: 'link',
|
||||
mod: 'mod',
|
||||
mods: 'mods',
|
||||
moderates: 'Moderates',
|
||||
admin: 'admin',
|
||||
admins: 'admins',
|
||||
modlog: 'Modlog',
|
||||
remove: 'remove',
|
||||
removed: 'removed',
|
||||
locked: 'locked',
|
||||
reason: 'Reason',
|
||||
remove_as_mod: 'remove as mod',
|
||||
appoint_as_mod: 'appoint as mod',
|
||||
remove_as_admin: 'remove as admin',
|
||||
appoint_as_admin: 'appoint as admin',
|
||||
mark_as_read: 'mark as read',
|
||||
mark_as_unread: 'mark as unread',
|
||||
remove_comment: 'Remove Comment',
|
||||
remove_community: 'Remove Community',
|
||||
delete: 'delete',
|
||||
deleted: 'deleted',
|
||||
restore: 'restore',
|
||||
ban: 'ban',
|
||||
unban: 'unban',
|
||||
ban_from_site: 'ban from site',
|
||||
unban_from_site: 'unban from site',
|
||||
save: 'save',
|
||||
unsave: 'unsave',
|
||||
create: 'create',
|
||||
subscribed_to_communities:'Subscribed to <1>communities</1>',
|
||||
create_a_community: 'Create a community',
|
||||
create_community: 'Create Community',
|
||||
create_a_post: 'Create a post',
|
||||
create_post: 'Create Post',
|
||||
trending_communities:'Trending <1>communities</1>',
|
||||
edit: 'edit',
|
||||
number_of_users:'{{count}} Users',
|
||||
number_of_subscribers:'{{count}} Subscribers',
|
||||
number_of_posts:'{{count}} Posts',
|
||||
number_of_comments:'{{count}} Comments',
|
||||
modlog: 'Modlog',
|
||||
admins: 'admins',
|
||||
number_of_points:'{{count}} Points',
|
||||
powered_by: 'Powered by',
|
||||
landing_0: 'Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>Its 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>.',
|
||||
list_of_communities: 'List of communities',
|
||||
name: 'Name',
|
||||
title: 'Title',
|
||||
category: 'Category',
|
||||
subscribers: 'Subscribers',
|
||||
both: 'Both',
|
||||
posts: 'Posts',
|
||||
comments: 'Comments',
|
||||
saved: 'Saved',
|
||||
unsubscribe: 'Unsubscribe',
|
||||
subscribe: 'Subscribe',
|
||||
prev: 'Prev',
|
||||
next: 'Next',
|
||||
sidebar: 'Sidebar',
|
||||
community_reqs: 'lowercase, underscores, and no spaces.',
|
||||
sort_type: 'Sort type',
|
||||
hot: 'Hot',
|
||||
new: 'New',
|
||||
top_day: 'Top day',
|
||||
week: 'Week',
|
||||
month: 'Month',
|
||||
year: 'Year',
|
||||
all: 'All',
|
||||
top: 'Top',
|
||||
|
||||
api: 'API',
|
||||
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:',
|
||||
support_on_patreon: 'Support on Patreon',
|
||||
general_sponsors:'General Sponsors are those that pledged $10 to $39 to Lemmy.',
|
||||
bitcoin: 'Bitcoin',
|
||||
ethereum: 'Ethereum',
|
||||
code: 'Code',
|
||||
|
||||
inbox: 'Inbox',
|
||||
inbox_for: 'Inbox for <1>{{user}}</1>',
|
||||
mark_all_as_read: 'mark all as read',
|
||||
type: 'Type',
|
||||
unread: 'Unread',
|
||||
reply_sent: 'Reply sent',
|
||||
|
||||
communities: 'Communities',
|
||||
search: 'Search',
|
||||
overview: 'Overview',
|
||||
view: 'View',
|
||||
logout: 'Logout',
|
||||
login_sign_up: 'Login / Sign up',
|
||||
notifications_error: 'Desktop notifications not available in your browser. Try Firefox or Chrome.',
|
||||
unread_messages: 'Unread Messages',
|
||||
|
||||
email_or_username: 'Email or Username',
|
||||
password: 'Password',
|
||||
verify_password: 'Verify Password',
|
||||
login: 'Login',
|
||||
sign_up: 'Sign Up',
|
||||
username: 'Username',
|
||||
email: 'Email',
|
||||
optional: 'Optional',
|
||||
|
||||
url: 'URL',
|
||||
body: 'Body',
|
||||
copy_suggested_title: 'copy suggested title: {{title}}',
|
||||
related_posts: 'These posts might be related',
|
||||
community: 'Community',
|
||||
|
||||
expand_here: 'Expand here',
|
||||
remove_post: 'Remove Post',
|
||||
|
||||
no_posts: 'No Posts.',
|
||||
subscribe_to_communities: 'Subscribe to some <1>communities</1>.',
|
||||
|
||||
chat: 'Chat',
|
||||
|
||||
no_results: 'No results.',
|
||||
|
||||
setup: 'Setup',
|
||||
lemmy_instance_setup: 'Lemmy Instance Setup',
|
||||
setup_admin: 'Set Up Site Administrator',
|
||||
|
||||
your_site: 'your site',
|
||||
modified: 'modified',
|
||||
|
||||
|
||||
foo: 'foo',
|
||||
|
@ -34,12 +155,13 @@ function format(value: any, format: any, lng: any) {
|
|||
return value;
|
||||
}
|
||||
|
||||
i18next.init({
|
||||
lng: 'en',
|
||||
i18n
|
||||
.init({
|
||||
fallbackLng: 'en',
|
||||
resources,
|
||||
interpolation: {
|
||||
format: format
|
||||
}
|
||||
});
|
||||
|
||||
export { i18next, resources };
|
||||
export { i18n, resources };
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Inbox } from './components/inbox';
|
|||
import { Search } from './components/search';
|
||||
import { Sponsors } from './components/sponsors';
|
||||
import { Symbols } from './components/symbols';
|
||||
import { i18next } from './i18next';
|
||||
import { i18n } from './i18next';
|
||||
|
||||
import './css/bootstrap.min.css';
|
||||
import './css/main.css';
|
||||
|
@ -36,7 +36,7 @@ class Index extends Component<any, any> {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<Provider i18next={i18next}>
|
||||
<Provider i18next={i18n}>
|
||||
<BrowserRouter>
|
||||
<Navbar />
|
||||
<div class="mt-1 p-0">
|
||||
|
|
Loading…
Reference in a new issue