summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorFelix Ableitner <me@nutomic.com>2020-06-09 14:01:26 +0200
committerFelix Ableitner <me@nutomic.com>2020-06-09 14:01:26 +0200
commit0f1a8ec928a36d73490a41a778244578f39dd626 (patch)
treee55ec80acbb258197ce899cefe6e1b24e09ad2fb /ui
parent5c6601cb2a819d20b0f0d17f3575aff006a47fd2 (diff)
parenta13e9fe3959e07f901ba0647dfd7f749865a900d (diff)
Merge branch 'master' into federation
Diffstat (limited to 'ui')
-rw-r--r--ui/assets/css/main.css5
-rw-r--r--ui/src/components/comment-node.tsx22
-rw-r--r--ui/src/components/login.tsx2
-rw-r--r--ui/src/components/main.tsx2
-rw-r--r--ui/src/components/post-form.tsx10
-rw-r--r--ui/src/components/post-listing.tsx2
-rw-r--r--ui/src/components/post.tsx12
-rw-r--r--ui/src/components/private-message-form.tsx55
-rw-r--r--ui/src/components/sidebar.tsx2
-rw-r--r--ui/src/components/sponsors.tsx29
-rw-r--r--ui/src/components/user.tsx4
-rw-r--r--ui/src/i18next.ts18
-rw-r--r--ui/src/utils.ts72
-rw-r--r--ui/src/version.ts2
-rw-r--r--ui/translations/ar.json59
-rw-r--r--ui/translations/de.json98
-rw-r--r--ui/translations/el.json1
-rw-r--r--ui/translations/en.json10
-rw-r--r--ui/translations/es.json13
-rw-r--r--ui/translations/eu.json67
-rw-r--r--ui/translations/fi.json99
-rw-r--r--ui/translations/fr.json109
-rw-r--r--ui/translations/gl.json1
-rw-r--r--ui/translations/hi.json66
-rw-r--r--ui/translations/hu.json1
-rw-r--r--ui/translations/it.json172
-rw-r--r--ui/translations/ja.json6
-rw-r--r--ui/translations/ka.json24
-rw-r--r--ui/translations/nl.json87
-rw-r--r--ui/translations/pl.json19
-rw-r--r--ui/translations/ru.json43
-rw-r--r--ui/translations/tr.json1
-rw-r--r--ui/translations/uk.json1
-rw-r--r--ui/translations/zh.json44
34 files changed, 822 insertions, 336 deletions
diff --git a/ui/assets/css/main.css b/ui/assets/css/main.css
index bf249e5b..8b51dcd9 100644
--- a/ui/assets/css/main.css
+++ b/ui/assets/css/main.css
@@ -128,10 +128,7 @@ blockquote {
.new-comments {
max-height: 50vh;
- overflow: hidden;
-}
-
-.new-comments:hover {
+ overflow-x: hidden;
overflow-y: auto;
}
diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx
index 9bc9c7bb..ca828a45 100644
--- a/ui/src/components/comment-node.tsx
+++ b/ui/src/components/comment-node.tsx
@@ -709,19 +709,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get linkBtn() {
let node = this.props.node;
return (
- <button className="btn btn-link btn-animate">
- <Link
- class="text-muted"
- to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
- title={
- this.props.showContext ? i18n.t('show_context') : i18n.t('link')
- }
- >
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-link"></use>
- </svg>
- </Link>
- </button>
+ <Link
+ class="btn btn-link btn-animate text-muted"
+ to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
+ title={this.props.showContext ? i18n.t('show_context') : i18n.t('link')}
+ >
+ <svg class="icon icon-inline">
+ <use xlinkHref="#icon-link"></use>
+ </svg>
+ </Link>
);
}
diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx
index 581c4995..84014f68 100644
--- a/ui/src/components/login.tsx
+++ b/ui/src/components/login.tsx
@@ -187,6 +187,7 @@ export class Login extends Component<any, State> {
type="password"
id="register-password"
value={this.state.registerForm.password}
+ autoComplete="new-password"
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
class="form-control"
required
@@ -206,6 +207,7 @@ export class Login extends Component<any, State> {
type="password"
id="register-verify-password"
value={this.state.registerForm.password_verify}
+ autoComplete="new-password"
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
class="form-control"
required
diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx
index 9e0c3a59..c168feb0 100644
--- a/ui/src/components/main.tsx
+++ b/ui/src/components/main.tsx
@@ -288,9 +288,11 @@ export class Main extends Component<any, MainState> {
</ul>
)}
<ul class="my-2 list-inline">
+ {/*
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_online', { count: this.state.siteRes.online })}
</li>
+ */}
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_of_users', {
count: this.state.siteRes.site.number_of_users,
diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx
index f04910be..22224c34 100644
--- a/ui/src/components/post-form.tsx
+++ b/ui/src/components/post-form.tsx
@@ -331,6 +331,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
value={this.state.postForm.community_id}
onInput={linkEvent(this, this.handlePostCommunityChange)}
>
+ <option>{i18n.t('select_a_community')}</option>
{this.state.communities.map(community => (
<option value={community.id}>
{community.local
@@ -362,7 +363,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
)}
<div class="form-group row">
<div class="col-sm-10">
- <button type="submit" class="btn btn-secondary mr-2">
+ <button
+ disabled={!this.state.postForm.community_id}
+ type="submit"
+ class="btn btn-secondary mr-2"
+ >
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
@@ -568,7 +573,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
).id;
this.state.postForm.community_id = foundCommunityId;
} else {
- this.state.postForm.community_id = data.communities[0].id;
+ // By default, the null valued 'Select a Community'
}
this.setState(this.state);
@@ -578,6 +583,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
let selector = new Selectr(selectId, { nativeDropdown: false });
selector.on('selectr.select', option => {
this.state.postForm.community_id = Number(option.value);
+ this.setState(this.state);
});
}
} else if (res.op == UserOperation.CreatePost) {
diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx
index 6ebf5400..def42e3c 100644
--- a/ui/src/components/post-listing.tsx
+++ b/ui/src/components/post-listing.tsx
@@ -165,6 +165,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
if (isImage(post.url)) {
if (post.url.includes('pictshare')) {
return pictshareImage(post.url, thumbnail);
+ } else if (post.thumbnail_url) {
+ return pictshareImage(post.thumbnail_url, thumbnail);
} else {
return post.url;
}
diff --git a/ui/src/components/post.tsx b/ui/src/components/post.tsx
index cf9e7486..297d0465 100644
--- a/ui/src/components/post.tsx
+++ b/ui/src/components/post.tsx
@@ -40,7 +40,6 @@ import {
setupTippy,
} from '../utils';
import { PostListing } from './post-listing';
-import { PostListings } from './post-listings';
import { Sidebar } from './sidebar';
import { CommentForm } from './comment-form';
import { CommentNodes } from './comment-nodes';
@@ -183,14 +182,6 @@ export class Post extends Component<any, PostState> {
moderators={this.state.moderators}
admins={this.state.admins}
/>
- {this.state.crossPosts.length > 0 && (
- <>
- <div class="my-1 text-muted small font-weight-bold">
- {i18n.t('cross_posts')}
- </div>
- <PostListings showCommunity posts={this.state.crossPosts} />
- </>
- )}
<div className="mb-2" />
<CommentForm
postId={this.state.post.id}
@@ -466,6 +457,9 @@ export class Post extends Component<any, PostState> {
this.state.crossPosts = data.posts.filter(
p => p.id != Number(this.props.match.params.id)
);
+ if (this.state.crossPosts.length) {
+ this.state.post.duplicates = this.state.crossPosts;
+ }
this.setState(this.state);
} else if (res.op == UserOperation.TransferSite) {
let data = res.data as GetSiteResponse;
diff --git a/ui/src/components/private-message-form.tsx b/ui/src/components/private-message-form.tsx
index 586b867e..64e22d61 100644
--- a/ui/src/components/private-message-form.tsx
+++ b/ui/src/components/private-message-form.tsx
@@ -163,38 +163,12 @@ export class PrivateMessageForm extends Component<
)}
/>
)}
-
- <ul class="float-right list-inline mb-1 text-muted font-weight-bold">
- <li class="list-inline-item">
- <span
- onClick={linkEvent(this, this.handleShowDisclaimer)}
- class="pointer"
- data-tippy-content={i18n.t('disclaimer')}
- >
- <svg class={`icon icon-inline`}>
- <use xlinkHref="#icon-alert-triangle"></use>
- </svg>
- </span>
- </li>
- <li class="list-inline-item">
- <a
- href={markdownHelpUrl}
- target="_blank"
- class="text-muted"
- title={i18n.t('formatting_help')}
- >
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-help-circle"></use>
- </svg>
- </a>
- </li>
- </ul>
</div>
</div>
{this.state.showDisclaimer && (
<div class="form-group row">
- <div class="col-sm-10">
+ <div class="offset-sm-2 col-sm-10">
<div class="alert alert-danger" role="alert">
<T i18nKey="private_message_disclaimer">
#
@@ -211,7 +185,7 @@ export class PrivateMessageForm extends Component<
</div>
)}
<div class="form-group row">
- <div class="col-sm-10">
+ <div class="offset-sm-2 col-sm-10">
<button type="submit" class="btn btn-secondary mr-2">
{this.state.loading ? (
<svg class="icon icon-spinner spin">
@@ -242,6 +216,31 @@ export class PrivateMessageForm extends Component<
{i18n.t('cancel')}
</button>
)}
+ <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
+ <li class="list-inline-item">
+ <span
+ onClick={linkEvent(this, this.handleShowDisclaimer)}
+ class="pointer"
+ data-tippy-content={i18n.t('disclaimer')}
+ >
+ <svg class={`icon icon-inline`}>
+ <use xlinkHref="#icon-alert-triangle"></use>
+ </svg>
+ </span>
+ </li>
+ <li class="list-inline-item">
+ <a
+ href={markdownHelpUrl}
+ target="_blank"
+ class="text-muted"
+ title={i18n.t('formatting_help')}
+ >
+ <svg class="icon icon-inline">
+ <use xlinkHref="#icon-help-circle"></use>
+ </svg>
+ </a>
+ </li>
+ </ul>
</div>
</div>
</form>
diff --git a/ui/src/components/sidebar.tsx b/ui/src/components/sidebar.tsx
index dc2376d7..17ac64b9 100644
--- a/ui/src/components/sidebar.tsx
+++ b/ui/src/components/sidebar.tsx
@@ -173,9 +173,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
</form>
)}
<ul class="my-1 list-inline">
+ {/*
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_online', { count: this.props.online })}
</li>
+ */}
<li className="list-inline-item badge badge-secondary">
{i18n.t('number_of_subscribers', {
count: community.number_of_subscribers,
diff --git a/ui/src/components/sponsors.tsx b/ui/src/components/sponsors.tsx
index cdd7bdad..35ec7635 100644
--- a/ui/src/components/sponsors.tsx
+++ b/ui/src/components/sponsors.tsx
@@ -4,14 +4,25 @@ import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
import { repoUrl } from '../utils';
+interface SilverUser {
+ name: string;
+ link: string;
+}
+
let general = [
- 'alexx henry',
- 'Nathan J. Goode',
+ 'Ernest Wiśniewski',
+ 'HN',
+ 'Forrest Weghorst',
'Andre Vallestero',
'NotTooHighToHack',
];
let highlighted = ['Oskenso Kashi', 'Alex Benishek'];
-// let silver = [];
+let silver: Array<SilverUser> = [
+ {
+ name: 'Redjoker',
+ link: 'https://iww.org',
+ },
+];
// let gold = [];
// let latinum = [];
@@ -70,6 +81,18 @@ export class Sponsors extends Component<any, any> {
return (
<div class="container">
<h5>{i18n.t('sponsors')}</h5>
+ <p>{i18n.t('silver_sponsors')}</p>
+ <div class="row card-columns">
+ {silver.map(s => (
+ <div class="card col-12 col-md-2">
+ <div>
+ <a href={s.link} target="_blank">
+ 💎 {s.name}
+ </a>
+ </div>
+ </div>
+ ))}
+ </div>
<p>{i18n.t('general_sponsors')}</p>
<div class="row card-columns">
{highlighted.map(s => (
diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx
index 51b9582f..78807153 100644
--- a/ui/src/components/user.tsx
+++ b/ui/src/components/user.tsx
@@ -614,6 +614,7 @@ export class User extends Component<any, UserState> {
id="user-password"
class="form-control"
value={this.state.userSettingsForm.new_password}
+ autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordChange
@@ -634,6 +635,7 @@ export class User extends Component<any, UserState> {
id="user-verify-password"
class="form-control"
value={this.state.userSettingsForm.new_password_verify}
+ autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsNewPasswordVerifyChange
@@ -654,6 +656,7 @@ export class User extends Component<any, UserState> {
id="user-old-password"
class="form-control"
value={this.state.userSettingsForm.old_password}
+ autoComplete="new-password"
onInput={linkEvent(
this,
this.handleUserSettingsOldPasswordChange
@@ -750,6 +753,7 @@ export class User extends Component<any, UserState> {
<input
type="password"
value={this.state.deleteAccountForm.password}
+ autoComplete="new-password"
onInput={linkEvent(
this,
this.handleDeleteAccountPasswordChange
diff --git a/ui/src/i18next.ts b/ui/src/i18next.ts
index 85d06f91..5fa8f4e8 100644
--- a/ui/src/i18next.ts
+++ b/ui/src/i18next.ts
@@ -1,6 +1,8 @@
import i18next from 'i18next';
import { getLanguage } from './utils';
import { en } from './translations/en';
+import { el } from './translations/el';
+import { eu } from './translations/eu';
import { eo } from './translations/eo';
import { es } from './translations/es';
import { de } from './translations/de';
@@ -13,14 +15,25 @@ import { it } from './translations/it';
import { fi } from './translations/fi';
import { ca } from './translations/ca';
import { fa } from './translations/fa';
+import { hi } from './translations/hi';
+import { pl } from './translations/pl';
import { pt_BR } from './translations/pt_BR';
import { ja } from './translations/ja';
+import { ka } from './translations/ka';
+import { gl } from './translations/gl';
+import { tr } from './translations/tr';
+import { hu } from './translations/hu';
+import { uk } from './translations/uk';
// https://github.com/nimbusec-oss/inferno-i18next/blob/master/tests/T.test.js#L66
const resources = {
en,
+ el,
+ eu,
eo,
es,
+ ka,
+ hi,
de,
zh,
fr,
@@ -31,8 +44,13 @@ const resources = {
fi,
ca,
fa,
+ pl,
pt_BR,
ja,
+ gl,
+ tr,
+ hu,
+ uk,
};
function format(value: any, format: any, lng: any): any {
diff --git a/ui/src/utils.ts b/ui/src/utils.ts
index 54322c8b..5ce84b39 100644
--- a/ui/src/utils.ts
+++ b/ui/src/utils.ts
@@ -1,4 +1,6 @@
import 'moment/locale/es';
+import 'moment/locale/el';
+import 'moment/locale/eu';
import 'moment/locale/eo';
import 'moment/locale/de';
import 'moment/locale/zh-cn';
@@ -10,9 +12,15 @@ import 'moment/locale/it';
import 'moment/locale/fi';
import 'moment/locale/ca';
import 'moment/locale/fa';
+import 'moment/locale/pl';
import 'moment/locale/pt-br';
import 'moment/locale/ja';
import 'moment/locale/ka';
+import 'moment/locale/hi';
+import 'moment/locale/gl';
+import 'moment/locale/tr';
+import 'moment/locale/hu';
+import 'moment/locale/uk';
import {
UserOperation,
@@ -58,17 +66,25 @@ export const mentionDropdownFetchLimit = 10;
export const languages = [
{ code: 'ca', name: 'Català' },
{ code: 'en', name: 'English' },
+ { code: 'el', name: 'Ελληνικά' },
+ { code: 'eu', name: 'Euskara' },
{ code: 'eo', name: 'Esperanto' },
{ code: 'es', name: 'Español' },
{ code: 'de', name: 'Deutsch' },
+ { code: 'gl', name: 'Galego' },
+ { code: 'hu', name: 'Magyar Nyelv' },
{ code: 'ka', name: 'ქართული ენა' },
+ { code: 'hi', name: 'मानक हिन्दी' },
{ code: 'fa', name: 'فارسی' },
{ code: 'ja', name: '日本語' },
+ { code: 'pl', name: 'Polski' },
{ code: 'pt_BR', name: 'Português Brasileiro' },
{ code: 'zh', name: '中文' },
{ code: 'fi', name: 'Suomi' },
{ code: 'fr', name: 'Français' },
{ code: 'sv', name: 'Svenska' },
+ { code: 'tr', name: 'Türkçe' },
+ { code: 'uk', name: 'українська мова' },
{ code: 'ru', name: 'Русский' },
{ code: 'nl', name: 'Nederlands' },
{ code: 'it', name: 'Italiano' },
@@ -208,7 +224,7 @@ export function isMod(modIds: Array<number>, creator_id: number): boolean {
}
const imageRegex = new RegExp(
- /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg))/
+ /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/
);
const videoRegex = new RegExp(`(http)?s?:?(\/\/[^"']*\.(?:mp4))`);
@@ -360,12 +376,28 @@ export function getMomentLanguage(): string {
lang = 'ca';
} else if (lang.startsWith('fa')) {
lang = 'fa';
+ } else if (lang.startsWith('pl')) {
+ lang = 'pl';
} else if (lang.startsWith('pt')) {
lang = 'pt-br';
} else if (lang.startsWith('ja')) {
lang = 'ja';
} else if (lang.startsWith('ka')) {
lang = 'ka';
+ } else if (lang.startsWith('hi')) {
+ lang = 'hi';
+ } else if (lang.startsWith('el')) {
+ lang = 'el';
+ } else if (lang.startsWith('eu')) {
+ lang = 'eu';
+ } else if (lang.startsWith('gl')) {
+ lang = 'gl';
+ } else if (lang.startsWith('tr')) {
+ lang = 'tr';
+ } else if (lang.startsWith('hu')) {
+ lang = 'hu';
+ } else if (lang.startsWith('uk')) {
+ lang = 'uk';
} else {
lang = 'en';
}
@@ -382,17 +414,22 @@ export function setTheme(theme: string = 'darkly') {
}
// Load the theme dynamically
- if (!document.getElementById(theme)) {
+ let cssLoc = `/static/assets/css/themes/${theme}.min.css`;
+ loadCss(theme, cssLoc);
+ document.getElementById(theme).removeAttribute('disabled');
+}
+
+export function loadCss(id: string, loc: string) {
+ if (!document.getElementById(id)) {
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
- link.id = theme;
+ link.id = id;
link.rel = 'stylesheet';
link.type = 'text/css';
- link.href = `/static/assets/css/themes/${theme}.min.css`;
+ link.href = loc;
link.media = 'all';
head.appendChild(link);
}
- document.getElementById(theme).removeAttribute('disabled');
}
export function objectFlip(obj: any) {
@@ -406,7 +443,7 @@ export function objectFlip(obj: any) {
export function pictshareAvatarThumbnail(src: string): string {
// sample url: http://localhost:8535/pictshare/gs7xuu.jpg
let split = src.split('pictshare');
- let out = `${split[0]}pictshare/96${split[1]}`;
+ let out = `${split[0]}pictshare/${canUseWebP() ? 'webp/' : ''}96${split[1]}`;
return out;
}
@@ -431,7 +468,9 @@ export function pictshareImage(
hash = split[1];
}
- let out = `${root}/${thumbnail ? '192/' : ''}${hash}`;
+ let out = `${root}/${canUseWebP() ? 'webp/' : ''}${
+ thumbnail ? '192/' : ''
+ }${hash}`;
return out;
}
@@ -462,6 +501,7 @@ export function messageToastify(
text: `${body}<br />${creator}`,
avatar: avatar,
backgroundColor: backgroundColor,
+ className: 'text-body',
close: true,
gravity: 'top',
position: 'right',
@@ -864,3 +904,21 @@ export function hostname(url: string): string {
? `${cUrl.hostname}:${cUrl.port}`
: `${cUrl.hostname}`;
}
+
+function canUseWebP() {
+ // TODO pictshare might have a webp conversion bug, try disabling this
+ return false;
+
+ // var elem = document.createElement('canvas');
+
+ // if (!!(elem.getContext && elem.getContext('2d'))) {
+ // var testString = !(window.mozInnerScreenX == null) ? 'png' : 'webp';
+ // // was able or not to get WebP representation
+ // return (
+ // elem.toDataURL('image/webp').startsWith('data:image/' + testString)
+ // );
+ // }
+
+ // // very old browser like IE 8, canvas not supported
+ // return false;
+}
diff --git a/ui/src/version.ts b/ui/src/version.ts
index eb0ecff3..9abc8bce 100644
--- a/ui/src/version.ts
+++ b/ui/src/version.ts
@@ -1 +1 @@
-export const version: string = 'v0.6.51';
+export const version: string = 'v0.6.71';
diff --git a/ui/translations/ar.json b/ui/translations/ar.json
index 44ab84f5..f963439e 100644
--- a/ui/translations/ar.json
+++ b/ui/translations/ar.json
@@ -8,7 +8,12 @@
"create_post": "إنشاء منشور",
"posts": "منشورات",
"comments": "التعليقات",
- "number_of_posts": "{{count}} منشورات",
+ "number_of_posts_0": "لا توجد منشورات",
+ "number_of_posts_1": "منشور واحد",
+ "number_of_posts_2": "منشورَيْن",
+ "number_of_posts_3": "{{count}} منشورات",
+ "number_of_posts_4": "{{count}} منشورات",
+ "number_of_posts_5": "{{count}} منشورات",
"related_posts": "يمكن لهذه المنشورات أن تكون ذات صلة",
"communities": "المجتمعات",
"users": "المستخدِمون",
@@ -50,9 +55,24 @@
"ban_from_site": "طرده مِن الموقع",
"banned": "مطرود",
"save": "حفظ",
- "number_of_users": "{{count}} مستخدِمين",
- "number_of_points": "{{count}} نقاط",
- "number_online": "{{count}} مستخدمين متّصلين",
+ "number_of_users_0": "بلا مستخدمين",
+ "number_of_users_1": "مستخدم واحد",
+ "number_of_users_2": "مستخدمَيْن",
+ "number_of_users_3": "{{count}} مستخدمين",
+ "number_of_users_4": "{{count}} مستخدمين",
+ "number_of_users_5": "{{count}} مستخدمين",
+ "number_of_points_0": "بلا نقاط",
+ "number_of_points_1": "نقطة واحدة",
+ "number_of_points_2": "نقطتَيْن",
+ "number_of_points_3": "{{count}} نقاط",
+ "number_of_points_4": "{{count}} نقاط",
+ "number_of_points_5": "{{count}} نقاط",
+ "number_online_0": "لا مستخدمين متّصلين",
+ "number_online_1": "مستخدم واحد متصل",
+ "number_online_2": "مستخدمَين متصلَيْن",
+ "number_online_3": "{{count}} مستخدمون متصلون",
+ "number_online_4": "{{count}} مستخدمون متصلون",
+ "number_online_5": "{{count}} مستخدمون متصلون",
"name": "الإسم",
"title": "العنوان",
"category": "الفئة",
@@ -114,7 +134,12 @@
"delete_account": "حذف الحساب",
"create": "إنشاء",
"email_or_username": "عنوان البريد أو اسم المستخدم",
- "number_of_subscribers": "{{count}} مُتابِعين",
+ "number_of_subscribers_0": "بلا مُتابِعين",
+ "number_of_subscribers_1": "متابِع واحد",
+ "number_of_subscribers_2": "متابِعَيْن",
+ "number_of_subscribers_3": "{{count}} متابِعون",
+ "number_of_subscribers_4": "{{count}} متابِعون",
+ "number_of_subscribers_5": "{{count}} متابِعون",
"unsubscribe": "إلغاء الإشتراك",
"week": "أسبوع",
"reply_sent": "تم إرسال الرد",
@@ -127,7 +152,12 @@
"are_you_sure": "هل أنت متأكّد؟",
"logged_in": "إنّك متّصل.",
"user_already_exists": "هذا المستخدِم موجود بالفعل.",
- "number_of_communities": "{{count}} مجتمعات",
+ "number_of_communities_0": "لا توجد مجتمعات",
+ "number_of_communities_1": "مجتمع واحد",
+ "number_of_communities_2": "مجتمعَيْن",
+ "number_of_communities_3": "{{count}} مجتمعات",
+ "number_of_communities_4": "{{count}} مجتمعات",
+ "number_of_communities_5": "{{count}} مجتمعات",
"subscribed": "مُتابِعون",
"url": "الرابط",
"nsfw": "محتوى حساس",
@@ -145,7 +175,12 @@
"support_on_patreon": "ساندنا على Patreon",
"support_on_liberapay": "ساندنا عبر Liberapay",
"crypto": "العملات الرقمية",
- "number_of_comments": "{{count}} تعليقات",
+ "number_of_comments_0": "لا توجد تعليقات",
+ "number_of_comments_1": "تعليق واحد",
+ "number_of_comments_2": "تعليقَيْن",
+ "number_of_comments_3": "{{count}} تعليقات",
+ "number_of_comments_4": "{{count}} تعليقات",
+ "number_of_comments_5": "{{count}} تعليقات",
"cross_posts": "لقد تم نشر هذا الرابط كذلك على:",
"cross_post": "منشور نُشِر تبادليا",
"cross_posted_to": "نشر تبادلي إلى: ",
@@ -176,5 +211,13 @@
"lemmy_instance_setup": "تنصيب مثيل خادم Lemmy",
"show_nsfw": "إظهار المحتوى الحساس",
"sponsors": "الرعاة",
- "sponsors_of_lemmy": "رعاة مشروع Lemmy"
+ "sponsors_of_lemmy": "رعاة مشروع Lemmy",
+ "inbox_for": "صندوق الواردات لـ <1>{{user}}</1>",
+ "show_context": "اظهر السياق",
+ "admin_settings": "الإعدادات الإدارية",
+ "site_config": "إعدادات الموقع",
+ "banned_users": "المستخدمون المحظورون",
+ "reset_password_mail_sent": "لقد أرسِلت إليك رسالة إلكترونية لتصفير كلمتك السرية.",
+ "upvote": "صوّت إيجابيا",
+ "downvote": "صوّت سلبيا"
}
diff --git a/ui/translations/de.json b/ui/translations/de.json
index 46719c18..ef42d418 100644
--- a/ui/translations/de.json
+++ b/ui/translations/de.json
@@ -1,5 +1,5 @@
{
- "post": "post",
+ "post":