Merge branch 'develop' into socket-notifications-refactor

This commit is contained in:
Barış Uşaklı
2026-03-30 11:00:47 -04:00
committed by GitHub
3663 changed files with 96355 additions and 34389 deletions

0
.docker/.gitkeep Normal file
View File

0
.docker/build/.gitkeep Normal file
View File

0
.docker/config/.gitkeep Normal file
View File

View File

View File

View File

View File

@@ -1,21 +0,0 @@
node_modules/
*.sublime-project
*.sublime-workspace
.project
.vagrant
.DS_Store
logs/
/public/templates
/public/uploads
/public/vendor
/public/src/modules/string.js
.idea/
.vscode/
*.ipr
*.iws
/coverage
/build
.eslintrc
test/files
*.min.js
install/docker/

View File

@@ -1,3 +0,0 @@
{
"extends": "nodebb"
}

View File

@@ -16,46 +16,116 @@ permissions:
packages: write
jobs:
release:
runs-on: ubuntu-latest
build:
strategy:
matrix:
include:
- os: ubuntu-latest
platforms: linux/amd64
required: true
- os: ubuntu-24.04-arm
platforms: linux/arm64
required: true
continue-on-error: ${{ !matrix.required }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Prepare
run: |
platform=${{ matrix.platforms }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY@L}" >> $GITHUB_ENV
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
images: ${{ env.IMAGE }}
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@v5
with:
path: var-cache-node-modules
key: var-cache-node-modules-${{ hashFiles('Dockerfile', 'install/package.json') }}
- name: Build and push Docker images
id: build
uses: docker/build-push-action@v7
with:
cache-from: type=gha
cache-to: type=gha,mode=min
context: .
file: ./Dockerfile
platforms: ${{ matrix.platforms }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ env.IMAGE }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Prepare
run: |
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY@L}" >> $GITHUB_ENV
echo "CURRENT_DATE_NST=$(date +'%Y%m%d-%H%M%S' -d '-3 hours -30 minutes')" >> $GITHUB_ENV
- name: Download digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.IMAGE }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}.x
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }}
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
type=raw,value=${{ env.CURRENT_DATE_NST }}
flavor: |
latest=true
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.meta.outputs.version }}

View File

@@ -27,19 +27,19 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [18, 20]
node: [20, 22]
database: [mongo-dev, mongo, redis, postgres]
include:
# only run coverage once
- os: ubuntu-latest
node: 18
node: 22
coverage: true
# test under development once
- database: mongo-dev
test_env: development
# only run eslint once
- os: ubuntu-latest
node: 18
node: 22
database: mongo-dev
lint: true
runs-on: ${{ matrix.os }}
@@ -48,7 +48,7 @@ jobs:
services:
postgres:
image: 'postgres:16-alpine'
image: 'postgres:18-alpine'
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -63,7 +63,7 @@ jobs:
- 5432:5432
redis:
image: 'redis:7.2.3'
image: 'redis:8.6.2'
# Set health checks to wait until redis has started
options: >-
--health-cmd "redis-cli ping"
@@ -75,18 +75,18 @@ jobs:
- 6379:6379
mongo:
image: 'mongo:7.0'
image: 'mongo:8.2'
ports:
# Maps port 27017 on service container to the host
- 27017:27017
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- run: cp install/package.json package.json
- name: Install Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
@@ -192,12 +192,13 @@ jobs:
run: npm run coverage
- name: Test coverage
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6
if: matrix.coverage
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: ${{ matrix.os }}-node-${{ matrix.node }}-db-${{ matrix.database }}
parallel: true
fail-on-error: false
finish:
permissions:
@@ -206,7 +207,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
fail-on-error: false

3
.gitignore vendored
View File

@@ -71,4 +71,5 @@ package-lock.json
link-plugins.sh
test.sh
.docker/
.docker/**
!**/.gitkeep

5876
.tx/config

File diff suppressed because it is too large Load Diff

22
.tx/fix.js Normal file
View File

@@ -0,0 +1,22 @@
'use strict';
const { readFile, writeFile } = require('fs').promises;
(async () => {
const contents = await readFile('./config', { encoding: 'utf-8' });
const blocks = contents
.split('\n\n')
.map((block) => {
block = block.split('\n').sort((a, b) => {
if (!a.startsWith('trans') || !b.startsWith('trans')) {
return 0;
}
return a.localeCompare(b);
})
return block.join('\n');
});
await writeFile('./config', blocks.join('\n\n'), { encoding: 'utf-8' });
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +1,76 @@
FROM --platform=$BUILDPLATFORM node:lts as npm
FROM node:lts as build
RUN mkdir -p /usr/src/build && \
chown -R node:node /usr/src/build
WORKDIR /usr/src/build
ENV NODE_ENV=production \
DAEMON=false \
SILENT=false \
USER=nodebb \
UID=1001 \
GID=1001
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
WORKDIR /usr/src/app/
COPY --chown=node:node install/package.json /usr/src/build/package.json
COPY . /usr/src/app/
USER node
# Install corepack to allow usage of other package managers
RUN corepack enable
RUN npm install --omit=dev
# Removing unnecessary files for us
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
FROM node:lts as rebuild
# Prepage package.json
RUN cp /usr/src/app/install/package.json /usr/src/app/
ARG BUILDPLATFORM
ARG TARGETPLATFORM
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get -y --no-install-recommends install \
tini
RUN mkdir -p /usr/src/build && \
chown -R node:node /usr/src/build
RUN groupadd --gid ${GID} ${USER} \
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
&& chown -R ${USER}:${USER} /usr/src/app/
COPY --from=npm /usr/src/build /usr/src/build
USER ${USER}
RUN if [ $BUILDPLATFORM != $TARGETPLATFORM ]; then \
npm rebuild && \
npm cache clean --force; fi
RUN npm install --omit=dev \
&& rm -rf .npm
# TODO: generate lockfiles for each package manager
## pnpm import \
FROM node:lts-slim as run
FROM node:lts-slim AS final
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV \
daemon=false \
silent=false
ENV NODE_ENV=production \
DAEMON=false \
SILENT=false \
USER=nodebb \
UID=1001 \
GID=1001
RUN mkdir -p /usr/src/app && \
chown -R node:node /usr/src/app
WORKDIR /usr/src/app/
COPY --chown=node:node --from=rebuild /usr/src/build /usr/src/app
RUN corepack enable \
&& groupadd --gid ${GID} ${USER} \
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
WORKDIR /usr/src/app
RUN chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/tini
USER node
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
COPY --chown=node:node . /usr/src/app
USER ${USER}
EXPOSE 4567
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config"]
ENTRYPOINT ["./install/docker/entrypoint.sh"]
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
# Utilising tini as our init system within the Docker container for graceful start-up and termination.
# Tini serves as an uncomplicated init system, adept at managing the reaping of zombie processes and forwarding signals.
# This approach is crucial to circumvent issues with unmanaged subprocesses and signal handling in containerised environments.
# By integrating tini, we enhance the reliability and stability of our Docker containers.
# Ensures smooth start-up and shutdown processes, and reliable, safe handling of signal processing.
ENTRYPOINT ["tini", "--", "entrypoint.sh"]

View File

@@ -173,7 +173,10 @@ module.exports = function (grunt) {
winston.error(err.stack);
}
if (worker) {
worker.send({ compiling: compiling });
worker.send({
compiling: compiling,
livereload: true, // Send livereload event via Socket.IO for instant browser refresh
});
}
});
});

View File

@@ -3,7 +3,7 @@
[![Workflow](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml/badge.svg)](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml)
[![Coverage Status](https://coveralls.io/repos/github/NodeBB/NodeBB/badge.svg?branch=master)](https://coveralls.io/github/NodeBB/NodeBB?branch=master)
[![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB)
[![](https://dcbadge.vercel.app/api/server/p6YKPXu7er?style=flat)](https://discord.gg/p6YKPXu7er)
[![](https://dcbadge.limes.pink/api/server/S2aAweHwDc?style=flat)](https://discord.gg/S2aAweHwDc)
[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and supports either Redis, MongoDB, or a PostgreSQL database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB takes the best of the modern web: real-time streaming discussions, mobile responsiveness, and rich RESTful read/write APIs, while staying true to the original bulletin board/forum format &rarr; categorical hierarchies, local user accounts, and asynchronous messaging.
@@ -39,8 +39,8 @@ Our minimalist "Harmony" theme gets you going right away, no coding experience r
NodeBB requires the following software to be installed:
* A version of Node.js at least 16 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
* MongoDB, version 3.6 or greater **or** Redis, version 2.8.9 or greater
* A version of Node.js at least 20 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
* MongoDB, version 5 or greater **or** Redis, version 7.2 or greater
* If you are using [clustering](https://docs.nodebb.org/configuring/scaling/) you need Redis installed and configured.
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)

1
app.js
View File

@@ -33,7 +33,6 @@ const path = require('path');
const file = require('./src/file');
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
global.env = process.env.NODE_ENV || 'production';
// Alternate configuration file support
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');

78
dev.Dockerfile Normal file
View File

@@ -0,0 +1,78 @@
FROM node:lts AS git
ENV USER=nodebb \
UID=1001 \
GID=1001
WORKDIR /usr/src/app/
RUN groupadd --gid ${GID} ${USER} \
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
&& chown -R ${USER}:${USER} /usr/src/app/
RUN apt-get update \
&& apt-get -y --no-install-recommends install tini
USER ${USER}
# Change to the git branch you want to test
RUN git clone --recurse-submodules -j8 --depth 1 https://github.com/NodeBB/NodeBB.git .
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
FROM node:lts AS node_modules_touch
ENV NODE_ENV=development \
USER=nodebb \
UID=1001 \
GID=1001
WORKDIR /usr/src/app/
RUN corepack enable \
&& groupadd --gid ${GID} ${USER} \
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
&& chown -R ${USER}:${USER} /usr/src/app/
COPY --from=git --chown=${USER}:${USER} /usr/src/app/install/package.json /usr/src/app/
USER ${USER}
RUN npm install \
&& rm -rf .npm
FROM node:lts-slim AS final
ENV NODE_ENV=development \
DAEMON=false \
SILENT=false \
USER=nodebb \
UID=1001 \
GID=1001
WORKDIR /usr/src/app/
RUN corepack enable \
&& groupadd --gid ${GID} ${USER} \
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
COPY --from=git --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
COPY --from=node_modules_touch --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
RUN chmod +x /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/tini
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
USER ${USER}
EXPOSE 4567
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
ENTRYPOINT ["tini", "--", "entrypoint.sh"]

70
docker-compose-pgsql.yml Normal file
View File

@@ -0,0 +1,70 @@
version: '3.8'
services:
nodebb:
build: .
# image: ghcr.io/nodebb/nodebb:latest
restart: unless-stopped
ports:
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
volumes:
- nodebb-build:/usr/src/app/build
- nodebb-uploads:/usr/src/app/public/uploads
- nodebb-config:/opt/config
- ./install/docker/setup.json:/usr/src/app/setup.json
postgres:
image: postgres:18.3-alpine
restart: unless-stopped
environment:
POSTGRES_USER: nodebb
POSTGRES_PASSWORD: nodebb
POSTGRES_DB: nodebb
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:8.6.2-alpine
restart: unless-stopped
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
volumes:
- redis-data:/data
profiles:
- redis
volumes:
postgres-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/postgresql/data
redis-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/redis
nodebb-build:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/build
nodebb-uploads:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/public/uploads
nodebb-config:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/config

51
docker-compose-redis.yml Normal file
View File

@@ -0,0 +1,51 @@
version: '3.8'
services:
nodebb:
build: .
# image: ghcr.io/nodebb/nodebb:latest
restart: unless-stopped
ports:
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
volumes:
- nodebb-build:/usr/src/app/build
- nodebb-uploads:/usr/src/app/public/uploads
- nodebb-config:/opt/config
- ./install/docker/setup.json:/usr/src/app/setup.json
redis:
image: redis:8.6.2-alpine
restart: unless-stopped
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
volumes:
- redis-data:/data
volumes:
redis-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/redis
nodebb-build:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/build
nodebb-uploads:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/public/uploads
nodebb-config:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/config

View File

@@ -1,55 +1,89 @@
version: '3.8'
services:
nodebb:
build: .
# image: ghcr.io/nodebb/nodebb:latest
restart: unless-stopped
ports:
- "4567:4567/tcp" # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
# uncomment if you want to use another container as a reverse proxy
# expose:
# - 4567
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
volumes:
- ./.docker/build:/usr/src/app/build
- ./.docker/public/uploads:/usr/src/app/public/uploads
- ./.docker:/opt/config
- nodebb-build:/usr/src/app/build
- nodebb-uploads:/usr/src/app/public/uploads
- nodebb-config:/opt/config
- ./install/docker/setup.json:/usr/src/app/setup.json
mongo:
image: "mongo:7-jammy"
image: 'mongo:7-jammy'
restart: unless-stopped
expose:
- "27017"
ports:
- '27017:27017'
environment:
MONGO_INITDB_ROOT_USERNAME: nodebb
MONGO_INITDB_ROOT_PASSWORD: nodebb
MONGO_INITDB_DATABASE: nodebb
volumes:
- ./.docker/database/mongo/config:/etc/mongo
- ./.docker/database/mongo/data:/data/db
- mongo-data:/data/db
- ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js
profiles:
- mongo
postgres:
image: postgres:16.1-alpine
redis:
image: redis:8.6.2-alpine
restart: unless-stopped
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
# command: ['redis-server', '--save', '60', '1', '--loglevel', 'warning'] # uncomment if you want to use snapshotting instead of AOF
volumes:
- redis-data:/data
profiles:
- redis
postgres:
image: postgres:18.3-alpine
restart: unless-stopped
expose:
- "5432"
environment:
POSTGRES_USER: nodebb
POSTGRES_PASSWORD: nodebb
POSTGRES_DB: nodebb
volumes:
- ./.docker/database/postgresql/data:/var/lib/postgresql/data
- postgres-data:/var/lib/postgresql/data
profiles:
- postgres
redis:
image: redis:7.2.3-alpine
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--loglevel", "warning"]
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
expose:
- "6379"
volumes:
- ./.docker/database/redis:/data
profiles:
- redis
volumes:
mongo-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/mongo/data
redis-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/redis
postgres-data:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/database/postgresql/data
nodebb-build:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/build
nodebb-uploads:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/public/uploads
nodebb-config:
driver: local
driver_opts:
o: bind
type: none
device: ./.docker/config

70
eslint.config.mjs Normal file
View File

@@ -0,0 +1,70 @@
'use strict';
import serverConfig from 'eslint-config-nodebb';
import publicConfig from 'eslint-config-nodebb/public';
import commonRules from 'eslint-config-nodebb/common';
import { defineConfig } from 'eslint/config';
import stylisticJs from '@stylistic/eslint-plugin'
import js from '@eslint/js';
import globals from 'globals';
export default defineConfig([
{
ignores: [
'node_modules/',
'.project',
'.vagrant',
'.DS_Store',
'.tx',
'logs/',
'public/uploads/',
'public/vendor/',
'.idea/',
'.vscode/',
'*.ipr',
'*.iws',
'coverage/',
'build/',
'test/files/',
'*.min.js',
'install/docker/',
],
},
// tests
{
plugins: {
js,
'@stylistic/js': stylisticJs,
},
extends: ['js/recommended'],
files: ['test/**/*.js'],
languageOptions: {
ecmaVersion: 2020,
sourceType: 'commonjs',
globals: {
...globals.node,
...globals.browser,
it: 'readonly',
describe: 'readonly',
before: 'readonly',
beforeEach: 'readonly',
after: 'readonly',
afterEach: 'readonly',
},
},
rules: {
...commonRules,
'no-unused-vars': 'off',
'no-prototype-builtins': 'off',
}
},
...publicConfig,
...serverConfig,
{
rules: {
'preserve-caught-error': 'off'
}
}
]);

View File

@@ -24,8 +24,8 @@
"newbieChatMessageDelay": 120000,
"notificationSendDelay": 60,
"newbieReputationThreshold": 3,
"postQueue": 0,
"postQueueReputationThreshold": 0,
"postQueue": 1,
"postQueueReputationThreshold": 1,
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
"groupsExemptFromNewUserRestrictions": ["administrators", "Global Moderators"],
"groupsExemptFromMaintenanceMode": ["administrators", "Global Moderators"],
@@ -36,27 +36,30 @@
"maximumTagsPerTopic": 5,
"minimumTagLength": 3,
"maximumTagLength": 15,
"undoTimeout": 10000,
"undoTimeout": 0,
"allowTopicsThumbnail": 1,
"showPostUploadsAsThumbnails": 1,
"registrationType": "normal",
"registrationApprovalType": "normal",
"allowAccountDelete": 1,
"privateUploads": 0,
"allowedFileExtensions": "png,jpg,bmp,txt",
"allowedFileExtensions": "png,jpg,bmp,txt,webp,webm,mp4,gif",
"uploadRateLimitThreshold": 10,
"uploadRateLimitCooldown": 60,
"allowUserHomePage": 1,
"allowMultipleBadges": 0,
"allowMultipleBadges": 1,
"maximumFileSize": 2048,
"stripEXIFData": 1,
"orphanExpiryDays": 0,
"resizeImageWidthThreshold": 2000,
"resizeImageWidth": 760,
"resizeImageKeepOriginal": 1,
"rejectImageWidth": 5000,
"rejectImageHeight": 5000,
"resizeImageQuality": 80,
"convertPastedImageTo": "image/jpeg",
"topicThumbSize": 512,
"minimumTitleLength": 3,
"minimumTitleLength": 0,
"maximumTitleLength": 255,
"minimumUsernameLength": 2,
"maximumUsernameLength": 16,
@@ -66,15 +69,16 @@
"maximumAboutMeLength": 1000,
"maximumUsersInChatRoom": 0,
"maximumChatMessageLength": 1000,
"maximumRemoteChatMessageLength": 5000,
"maximumChatRoomNameLength": 50,
"maximumProfileImageSize": 256,
"maximumProfileImageSize": 2048,
"maximumCoverImageSize": 2048,
"profileImageDimension": 200,
"profile:convertProfileImageToPNG": 0,
"profile:keepAllUserImages": 0,
"gdpr_enabled": 1,
"allowProfileImageUploads": 1,
"teaserPost": "last-reply",
"teaserPost": "last-post",
"showPostPreviewsOnHover": 1,
"allowPrivateGroups": 1,
"unreadCutoff": 2,
@@ -99,16 +103,21 @@
"min:rep:aboutme": 0,
"min:rep:signature": 0,
"flags:limitPerTarget": 0,
"flags:postFlagsPerDay": 10,
"flags:userFlagsPerDay": 10,
"flags:autoFlagOnDownvoteThreshold": 0,
"flags:actionOnResolve": "rescind",
"flags:actionOnReject": "rescind",
"notificationType_upvote": "notification",
"notificationType_new-topic": "notification",
"notificationType_new-topic-with-tag": "notification",
"notificationType_new-topic-in-category": "notification",
"notificationType_new-reply": "notification",
"notificationType_post-edit": "notification",
"notificationType_follow": "notification",
"notificationType_new-chat": "notification",
"notificationType_new-group-chat": "notification",
"notificationType_new-public-chat": "none",
"notificationType_group-invite": "notification",
"notificationType_group-leave": "notification",
"notificationType_group-request-membership": "notification",
@@ -133,13 +142,16 @@
"feeds:disableSitemap": 0,
"feeds:disableRSS": 0,
"sitemapTopics": 500,
"sitemapCacheDurationHours": 24,
"maintenanceMode": 0,
"maintenanceModeStatus": 503,
"votesArePublic": 0,
"upvoteVisibility": "all",
"downvoteVisibility": "privileged",
"maximumInvites": 0,
"username:disableEdit": 0,
"email:disableEdit": 0,
"email:smtpTransport:pool": 0,
"email:smtpTransport:allow-self-signed": 0,
"hideFullname": 0,
"hideEmail": 0,
"showFullnameAsDisplayName": 0,
@@ -176,7 +188,7 @@
"onlineCutoff": 30,
"timeagoCutoff": 30,
"necroThreshold": 7,
"categoryWatchState": "watching",
"categoryWatchState": "tracking",
"submitPluginUsage": 1,
"showAverageApprovalTime": 1,
"autoApproveTime": 0,
@@ -187,5 +199,15 @@
"composer:allowPluginHelp": 1,
"maxReconnectionAttempts": 5,
"reconnectionDelay": 1500,
"disableCustomUserSkins": 0
}
"disableCustomUserSkins": 0,
"activitypubEnabled": 1,
"activitypubAllowLoopback": 0,
"activitypubProbe": 1,
"activitypubProbeTimeout": 2000,
"activitypubContentPruneDays": 30,
"activitypubUserPruneDays": 7,
"activitypubFilter": 0,
"activitypubSummaryLimit": 500,
"activitypubBreakString": "[...]",
"activitypubWorldDefaultCid": -1
}

View File

@@ -41,6 +41,14 @@
"textClass": "d-lg-none",
"text": "[[global:header.popular]]"
},
{
"route": "/world",
"title": "[[global:header.world]]",
"enabled": true,
"iconClass": "fa-globe",
"textClass": "d-lg-none",
"text": "[[global:header.world]]"
},
{
"route": "/users",
"title": "[[global:header.users]]",

View File

@@ -1,46 +1,238 @@
#!/bin/bash
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
export CONFIG=$CONFIG_DIR/config.json
export FORCE_BUILD_BEFORE_START="${FORCE_BUILD_BEFORE_START:-false}"
set -e
# Supported verbs: install (web install), setup (interactive CLI session). Default: web install
# TODO: constraint it using a hash set (or hash table)
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
# Setup variable for backward compatibility, default: <empty>
export SETUP="${SETUP:-}"
# Function to set default values for environment variables
set_defaults() {
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
export CONFIG="$CONFIG_DIR/config.json"
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
export NODEBB_BUILD_VERB="${NODEBB_BUILD_VERB:-build}"
export START_BUILD="${START_BUILD:-${FORCE_BUILD_BEFORE_START:-false}}"
export SETUP="${SETUP:-}"
export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}"
export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}"
export NODEBB_ADDITIONAL_PLUGINS="${NODEBB_ADDITIONAL_PLUGINS:-}"
}
mkdir -p $CONFIG_DIR
# if the folder is mounted as a volume this can fail, the check below is to ensure there is still write access
chmod -fR 760 $CONFIG_DIR 2> /dev/null
if [[ ! -w $CONFIG_DIR ]]; then
echo "panic: no write permission for $CONFIG_DIR"
exit 1
fi
[[ -f $CONFIG_DIR/package.json ]] || cp install/package.json $CONFIG_DIR/package.json
[[ -f $CONFIG_DIR/package-lock.json ]] || touch $CONFIG_DIR/package-lock.json
ln -fs $CONFIG_DIR/package.json package.json
ln -fs $CONFIG_DIR/package-lock.json package-lock.json
npm install --omit=dev
if [[ -n $SETUP ]]; then
echo "Setup environmental variable detected"
echo "Starting setup session"
./nodebb setup --config=$CONFIG
elif [ -f $CONFIG ]; then
echo "Config file exist at $CONFIG, assuming it is a valid config"
echo "Starting forum"
if [ "$FORCE_BUILD_BEFORE_START" = true ]; then
./nodebb build --config=$CONFIG
# Function to check if a directory exists and is writable
check_directory() {
local dir="$1"
if [ ! -d "$dir" ]; then
echo "Error: Directory $dir does not exist. Creating..."
mkdir -p "$dir" || {
echo "Error: Failed to create directory $dir"
exit 1
}
fi
./nodebb start --config=$CONFIG
else
echo "Config file not found at $CONFIG"
if [ ! -w "$dir" ]; then
echo "Warning: No write permission for directory $dir, attempting to fix..."
chown -R $USER:$USER "$dir" || true # attempt to change ownership, do not exit on failure
chmod -R 760 "$dir" || true # attempt to change permissions, do not exit on failure
if [ ! -w "$dir" ]; then
echo "Error: No write permission for directory $dir. Exiting..."
exit 1
fi
fi
}
# Function to copy or link package.json and lock files based on package manager
copy_or_link_files() {
local src_dir="$1"
local dest_dir="$2"
local package_manager="$3"
local lock_file
case "$package_manager" in
yarn) lock_file="yarn.lock" ;;
npm) lock_file="package-lock.json" ;;
pnpm) lock_file="pnpm-lock.yaml" ;;
*)
echo "Unknown package manager: $package_manager"
exit 1
;;
esac
# Check if source and destination files are the same
if [ "$(realpath "$src_dir/package.json")" != "$(realpath "$dest_dir/package.json")" ] || [ "$OVERRIDE_UPDATE_LOCK" = true ]; then
cp "$src_dir/package.json" "$dest_dir/package.json"
fi
if [ "$(realpath "$src_dir/$lock_file")" != "$(realpath "$dest_dir/$lock_file")" ] || [ "$OVERRIDE_UPDATE_LOCK" = true ]; then
cp "$src_dir/$lock_file" "$dest_dir/$lock_file"
fi
# Remove unnecessary lock files in src_dir
rm -f "$src_dir/"{yarn.lock,package-lock.json,pnpm-lock.yaml}
# Symbolically link the copied files in src_dir to dest_dir
ln -fs "$dest_dir/package.json" "$src_dir/package.json"
ln -fs "$dest_dir/$lock_file" "$src_dir/$lock_file"
}
# Function to install dependencies using npm/yarn/pnpm
install_dependencies() {
case "$PACKAGE_MANAGER" in
yarn) yarn install || {
echo "Failed to install dependencies with yarn"
exit 1
} ;;
npm) npm install || {
echo "Failed to install dependencies with npm"
exit 1
} ;;
pnpm) pnpm install || {
echo "Failed to install dependencies with pnpm"
exit 1
} ;;
*)
echo "Unknown package manager: $PACKAGE_MANAGER"
exit 1
;;
esac
}
# Function to start setup session
start_setup_session() {
local config="$1"
echo "Starting setup session"
exec /usr/src/app/nodebb setup --config="$config"
}
# Handle building and upgrading NodeBB
build_forum() {
local config="$1"
local start_build="$2"
local package_hash=$(md5sum install/package.json | head -c 32)
if [ "$package_hash" != "$(cat $CONFIG_DIR/install_hash.md5 || true)" ]; then
echo "package.json was updated. Upgrading..."
/usr/src/app/nodebb upgrade --config="$config" || {
echo "Failed to build NodeBB. Exiting..."
exit 1
}
elif [ "$start_build" = true ]; then
echo "Build before start is enabled. Building..."
/usr/src/app/nodebb "${NODEBB_BUILD_VERB}" --config="$config" || {
echo "Failed to build NodeBB. Exiting..."
exit 1
}
else
echo "No changes in package.json. Skipping build..."
return
fi
echo -n $package_hash > $CONFIG_DIR/install_hash.md5
}
# Function to start forum
start_forum() {
local config="$1"
local start_build="$2"
build_forum "$config" "$start_build"
case "$PACKAGE_MANAGER" in
yarn)
yarn start --config="$config" --no-silent --no-daemon || {
echo "Failed to start forum with yarn"
exit 1
}
;;
npm)
npm start -- --config="$config" --no-silent --no-daemon || {
echo "Failed to start forum with npm"
exit 1
}
;;
pnpm)
pnpm start -- --config="$config" --no-silent --no-daemon || {
echo "Failed to start forum with pnpm"
exit 1
}
;;
*)
echo "Unknown package manager: $PACKAGE_MANAGER"
exit 1
;;
esac
}
# Function to start installation session
start_installation_session() {
local nodebb_init_verb="$1"
local config="$2"
echo "Config file not found at $config"
echo "Starting installation session"
./nodebb "${NODEBB_INIT_VERB}" --config=$CONFIG
fi
exec /usr/src/app/nodebb "$nodebb_init_verb" --config="$config"
}
# Function for debugging and logging
debug_log() {
local message="$1"
echo "DEBUG: $message"
}
install_additional_plugins() {
if [[ -n ${NODEBB_ADDITIONAL_PLUGINS} ]]; then
# Create a local array to work with
local plugins_to_install=()
# check if NODEBB_ADDITIONAL_PLUGINS is an array or a space-separated string
if [[ "$(declare -p NODEBB_ADDITIONAL_PLUGINS 2>/dev/null)" == "declare -a"* ]]; then
plugins_to_install=("${NODEBB_ADDITIONAL_PLUGINS[@]}")
else
plugins_to_install=(${NODEBB_ADDITIONAL_PLUGINS})
fi
export START_BUILD="true"
for plugin in "${plugins_to_install[@]}"; do
echo "Installing additional plugin ${plugin}..."
case "$PACKAGE_MANAGER" in
yarn) yarn add "${plugin}" || {
echo "Failed to install plugin ${plugin} with yarn"
exit 1
} ;;
npm) npm install "${plugin}" || {
echo "Failed to install plugin ${plugin} with npm"
exit 1
} ;;
pnpm) pnpm add "${plugin}" || {
echo "Failed to install plugin ${plugin} with pnpm"
exit 1
} ;;
*)
echo "Unknown package manager: $PACKAGE_MANAGER"
exit 1
;;
esac
done
fi
}
# Main function
main() {
set_defaults
check_directory "$CONFIG_DIR"
copy_or_link_files /usr/src/app "$CONFIG_DIR" "$PACKAGE_MANAGER"
install_dependencies
debug_log "PACKAGE_MANAGER: $PACKAGE_MANAGER"
debug_log "CONFIG location: $CONFIG"
debug_log "START_BUILD: $START_BUILD"
debug_log "NODEBB_ADDITIONAL_PLUGINS: ${NODEBB_ADDITIONAL_PLUGINS}"
if [ -n "$SETUP" ]; then
start_setup_session "$CONFIG"
fi
if [ -f "$CONFIG" ]; then
install_additional_plugins
start_forum "$CONFIG" "$START_BUILD"
else
start_installation_session "$NODEBB_INIT_VERB" "$CONFIG"
fi
}
# Execute main function
main "$@"

View File

@@ -1,21 +1,23 @@
{
"mongo": {
"host": "mongo",
"port": 27017,
"database": "nodebb",
"username": "nodebb",
"password": "nodebb"
},
"redis": {
"host": "redis",
"port": 6379,
"database": 0
},
"postgres": {
"host": "postgres",
"port": 5432,
"database": "nodebb",
"username": "nodebb",
"password": "nodebb"
"defaults": {
"mongo": {
"host": "mongo",
"port": 27017,
"database": "nodebb",
"username": "nodebb",
"password": "nodebb"
},
"redis": {
"host": "redis",
"port": 6379,
"database": 0
},
"postgres": {
"host": "postgres",
"port": 5432,
"database": "nodebb",
"username": "nodebb",
"password": "nodebb"
}
}
}

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "3.5.1",
"version": "4.10.1",
"homepage": "https://www.nodebb.org",
"repository": {
"type": "git",
@@ -29,150 +29,160 @@
},
"dependencies": {
"@adactive/bootstrap-tagsinput": "0.8.2",
"@fontsource/inter": "5.0.15",
"@fontsource/poppins": "5.0.8",
"@fortawesome/fontawesome-free": "6.4.2",
"@isaacs/ttlcache": "1.4.1",
"@fontsource-utils/scss": "0.2.2",
"@fontsource/inter": "5.2.8",
"@fontsource/poppins": "5.2.7",
"@fortawesome/fontawesome-free": "6.7.2",
"@isaacs/ttlcache": "2.1.4",
"@nodebb/spider-detector": "2.0.3",
"@popperjs/core": "2.11.8",
"ace-builds": "1.31.2",
"archiver": "6.0.1",
"async": "3.2.5",
"autoprefixer": "10.4.16",
"bcryptjs": "2.4.3",
"benchpressjs": "2.5.1",
"body-parser": "1.20.2",
"bootbox": "6.0.0",
"bootstrap": "5.3.2",
"bootswatch": "5.3.2",
"@textcomplete/contenteditable": "0.1.13",
"@textcomplete/core": "0.1.13",
"@textcomplete/textarea": "0.1.13",
"ace-builds": "1.43.6",
"archiver": "7.0.1",
"async": "3.2.6",
"autoprefixer": "10.4.27",
"bcryptjs": "3.0.3",
"benchpressjs": "2.5.5",
"body-parser": "2.2.2",
"bootbox": "6.0.4",
"bootstrap": "5.3.8",
"bootswatch": "5.3.8",
"chalk": "4.1.2",
"chart.js": "4.4.0",
"chart.js": "4.5.1",
"cli-graph": "3.2.2",
"clipboard": "2.0.11",
"colors": "1.4.0",
"commander": "11.1.0",
"compare-versions": "6.1.0",
"compression": "1.7.4",
"commander": "14.0.3",
"compare-versions": "6.1.1",
"compression": "1.8.1",
"connect-flash": "0.1.1",
"connect-mongo": "5.1.0",
"connect-multiparty": "2.2.0",
"connect-pg-simple": "9.0.1",
"connect-redis": "7.1.0",
"cookie-parser": "1.4.6",
"cron": "3.1.6",
"cropperjs": "1.6.1",
"csrf-sync": "4.0.1",
"connect-mongo": "6.0.0",
"connect-pg-simple": "10.0.0",
"connect-redis": "9.0.0",
"cookie-parser": "1.4.7",
"cron": "4.4.0",
"cronstrue": "3.14.0",
"cropperjs": "1.6.2",
"csrf-sync": "4.2.1",
"csv-parse": "6.2.1",
"daemon": "1.1.0",
"diff": "5.1.0",
"esbuild": "0.19.5",
"express": "4.18.2",
"express-session": "1.17.3",
"express-useragent": "1.0.15",
"diff": "8.0.4",
"esbuild": "0.27.4",
"express": "4.22.1",
"express-session": "1.19.0",
"express-useragent": "2.1.0",
"fetch-cookie": "3.2.0",
"file-loader": "6.2.0",
"fs-extra": "11.1.1",
"fs-extra": "11.3.4",
"graceful-fs": "4.2.11",
"helmet": "7.1.0",
"helmet": "7.2.0",
"html-to-text": "9.0.5",
"imagesloaded": "5.0.0",
"ipaddr.js": "2.1.0",
"ipaddr.js": "2.3.0",
"jquery": "3.7.1",
"jquery-deserialize": "2.0.0",
"jquery-form": "4.3.0",
"jquery-serializeobject": "1.0.0",
"jquery-ui": "1.13.2",
"jsesc": "3.0.2",
"jquery-ui": "1.14.1",
"jsesc": "3.1.0",
"json2csv": "5.0.7",
"jsonwebtoken": "9.0.2",
"lodash": "4.17.21",
"jsonwebtoken": "9.0.3",
"lodash": "4.17.23",
"logrotate-stream": "0.2.9",
"lru-cache": "10.0.2",
"lru-cache": "11.2.7",
"mime": "3.0.0",
"mkdirp": "3.0.1",
"mongodb": "6.3.0",
"morgan": "1.10.0",
"mongodb": "7.1.1",
"morgan": "1.10.1",
"mousetrap": "1.6.5",
"multiparty": "4.2.3",
"nconf": "0.12.1",
"nodebb-plugin-2factor": "7.4.0",
"nodebb-plugin-composer-default": "10.2.27",
"nodebb-plugin-dbsearch": "6.2.3",
"nodebb-plugin-emoji": "5.1.13",
"nodebb-plugin-emoji-android": "4.0.0",
"nodebb-plugin-markdown": "12.2.5",
"nodebb-plugin-mentions": "4.3.7",
"nodebb-plugin-ntfy": "1.7.3",
"nodebb-plugin-spam-be-gone": "2.2.0",
"nodebb-rewards-essentials": "1.0.0",
"nodebb-theme-harmony": "1.1.99",
"nodebb-theme-lavender": "7.1.5",
"nodebb-theme-peace": "2.1.25",
"nodebb-theme-persona": "13.2.47",
"nodebb-widget-essentials": "7.0.14",
"nodemailer": "6.9.7",
"multer": "2.1.1",
"nconf": "0.13.0",
"nodebb-plugin-2factor": "7.6.1",
"nodebb-plugin-composer-default": "10.3.28",
"nodebb-plugin-dbsearch": "6.4.1",
"nodebb-plugin-emoji": "6.0.6",
"nodebb-plugin-emoji-android": "4.1.1",
"nodebb-plugin-link-preview": "2.2.3",
"nodebb-plugin-markdown": "13.2.4",
"nodebb-plugin-mentions": "4.8.18",
"nodebb-plugin-spam-be-gone": "2.3.2",
"nodebb-plugin-web-push": "0.7.7",
"nodebb-rewards-essentials": "1.0.2",
"nodebb-theme-harmony": "2.2.63",
"nodebb-theme-lavender": "7.1.21",
"nodebb-theme-peace": "2.2.58",
"nodebb-theme-persona": "14.2.34",
"nodebb-widget-essentials": "7.0.43",
"nodemailer": "8.0.4",
"nprogress": "0.2.0",
"passport": "0.6.0",
"passport": "0.7.0",
"passport-http-bearer": "1.0.1",
"passport-local": "1.0.0",
"pg": "8.11.3",
"pg-cursor": "2.10.3",
"postcss": "8.4.31",
"pg": "8.20.0",
"pg-cursor": "2.19.0",
"postcss": "8.5.8",
"postcss-clean": "1.2.0",
"pretty": "^2.0.0",
"progress-webpack-plugin": "1.0.16",
"prompt": "1.3.0",
"ioredis": "5.3.2",
"request": "2.88.2",
"request-promise-native": "1.0.9",
"rimraf": "5.0.5",
"qs": "6.15.0",
"redis": "5.11.0",
"rimraf": "6.1.3",
"rss": "1.2.2",
"rtlcss": "4.1.1",
"sanitize-html": "2.11.0",
"sass": "1.69.5",
"semver": "7.5.4",
"serve-favicon": "2.5.0",
"sharp": "0.32.6",
"sitemap": "7.1.1",
"socket.io": "4.7.2",
"socket.io-client": "4.7.2",
"@socket.io/redis-adapter": "8.2.1",
"sortablejs": "1.15.0",
"spdx-license-list": "6.8.0",
"spider-detector": "2.0.1",
"terser-webpack-plugin": "5.3.9",
"rtlcss": "4.3.0",
"sanitize-html": "2.17.2",
"sass": "1.98.0",
"satori": "0.26.0",
"sbd": "^1.0.19",
"semver": "7.7.4",
"serve-favicon": "2.5.1",
"sharp": "0.34.5",
"sitemap": "9.0.1",
"socket.io": "4.8.3",
"socket.io-client": "4.8.3",
"@socket.io/redis-adapter": "8.3.0",
"sortablejs": "1.15.7",
"spdx-license-list": "6.11.0",
"terser-webpack-plugin": "5.4.0",
"textcomplete": "0.18.2",
"textcomplete.contenteditable": "0.1.1",
"timeago": "1.6.7",
"tinycon": "0.6.8",
"toobusy-js": "0.5.1",
"validator": "13.11.0",
"webpack": "5.89.0",
"webpack-merge": "5.10.0",
"winston": "3.11.0",
"workerpool": "8.0.0",
"tough-cookie": "6.0.1",
"undici": "^7.10.0",
"validator": "13.15.26",
"webpack": "5.105.4",
"webpack-merge": "6.0.1",
"winston": "3.19.0",
"workerpool": "10.0.1",
"xml": "1.0.1",
"xregexp": "5.1.1",
"yargs": "17.7.2",
"zxcvbn": "4.4.2"
},
"devDependencies": {
"@apidevtools/swagger-parser": "10.1.0",
"@commitlint/cli": "18.4.2",
"@commitlint/config-angular": "18.4.2",
"@commitlint/cli": "20.5.0",
"@commitlint/config-angular": "20.5.0",
"coveralls": "3.1.1",
"eslint": "8.54.0",
"eslint-config-nodebb": "0.2.1",
"eslint-plugin-import": "2.29.0",
"@eslint/js": "10.0.1",
"@stylistic/eslint-plugin": "5.10.0",
"eslint-config-nodebb": "2.0.1",
"globals": "17.4.0",
"grunt": "1.6.1",
"grunt-contrib-watch": "1.1.0",
"husky": "8.0.3",
"jsdom": "22.1.0",
"lint-staged": "15.1.0",
"mocha": "10.2.0",
"jsdom": "29.0.1",
"lint-staged": "16.4.0",
"mocha": "11.7.5",
"mocha-lcov-reporter": "1.3.0",
"mockdate": "3.0.5",
"nyc": "15.1.0",
"smtp-server": "3.13.0"
"nyc": "18.0.0",
"smtp-server": "3.18.3"
},
"optionalDependencies": {
"sass-embedded": "1.69.5"
"sass-embedded": "1.98.0"
},
"resolutions": {
"*/jquery": "3.7.1"
@@ -181,7 +191,7 @@
"url": "https://github.com/NodeBB/NodeBB/issues"
},
"engines": {
"node": ">=16"
"node": ">=20"
},
"maintainers": [
{
@@ -195,4 +205,4 @@
"url": "https://github.com/barisusakli"
}
]
}
}

View File

@@ -2,6 +2,7 @@
const winston = require('winston');
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
@@ -13,7 +14,10 @@ const nconf = require('nconf');
const Benchpress = require('benchpressjs');
const { mkdirp } = require('mkdirp');
const { paths } = require('../src/constants');
const sass = require('../src/utils').getSass();
const utils = require('../src/utils');
const sass = utils.getSass();
const { generateToken, csrfSynchronisedProtection } = require('../src/middleware/csrf');
const app = express();
let server;
@@ -73,6 +77,13 @@ web.install = async function (port) {
app.use(bodyParser.urlencoded({
extended: true,
}));
app.use(session({
secret: utils.generateUUID(),
resave: false,
saveUninitialized: false,
}));
try {
await Promise.all([
compileTemplate(),
@@ -103,8 +114,8 @@ function launchExpress(port) {
}
function setupRoutes() {
app.get('/', welcome);
app.post('/', install);
app.get('/', csrfSynchronisedProtection, welcome);
app.post('/', csrfSynchronisedProtection, install);
app.get('/testdb', testDatabase);
app.get('/ping', ping);
app.get('/sping', ping);
@@ -160,6 +171,7 @@ function welcome(req, res) {
minimumPasswordStrength: defaults.minimumPasswordStrength,
installing: installing,
percentInstalled: installing ? ((Date.now() - timeStart) / totalTime * 100).toFixed(2) : 0,
csrf_token: generateToken(req),
});
}

View File

@@ -2,7 +2,6 @@
const nconf = require('nconf');
const fs = require('fs');
const url = require('url');
const path = require('path');
const { fork } = require('child_process');
const logrotate = require('logrotate-stream');
@@ -26,7 +25,7 @@ if (!fs.existsSync(logDir)) {
mkdirp.sync(path.dirname(outputLogFilePath));
}
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
const output = logrotate({ file: outputLogFilePath, size: '10m', keep: 3, compress: true });
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
let numProcs;
const workers = [];
@@ -99,6 +98,13 @@ Loader.start = function () {
function forkWorker(index, isPrimary) {
const ports = getPorts();
const args = [];
const execArgv = [];
if (nconf.get('max-memory')) {
execArgv.push(`--max-old-space-size=${nconf.get('max-memory')}`);
}
if (nconf.get('expose-gc')) {
execArgv.push('--expose-gc');
}
if (!ports[index]) {
return console.log(`[cluster] invalid port for worker : ${index} ports: ${ports.length}`);
@@ -107,10 +113,10 @@ function forkWorker(index, isPrimary) {
process.env.isPrimary = isPrimary;
process.env.isCluster = nconf.get('isCluster') || ports.length > 1;
process.env.port = ports[index];
const worker = fork(appPath, args, {
silent: silent,
env: process.env,
execArgv: execArgv,
});
worker.index = index;
@@ -133,7 +139,7 @@ function getPorts() {
console.log('[cluster] url is undefined, please check your config.json');
process.exit();
}
const urlObject = url.parse(_url);
const urlObject = new URL(_url);
let port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
if (!Array.isArray(port)) {
port = [port];

View File

@@ -1,3 +0,0 @@
{
"extends": "nodebb/public"
}

41
public/500.html Normal file
View File

@@ -0,0 +1,41 @@
<html>
<head>
<title>Internal Server Error</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/assets/5xx.css" />
<script type="text/javascript">
window.onload = function() {
let count = 0;
const bounce = document.getElementById('click-me');
bounce.onclick = function() {
count++;
bounce.className = '';
setTimeout(function() {
bounce.className = 'animated bounce';
}, 50);
if (count > 5) {
document.getElementById('hide').className = '';
}
};
}
</script>
</head>
<body>
<div class="wrapper">
<div class="center">
<h1 id="click-me" class="animated bounce">500</h1>
<p>
<strong>Internal server error. </strong>
</p>
<p>
{message}
</p>
<p>
&nbsp;<small id="hide" class="hide">Alright. You can stop clicking... it's not going to make the site come back sooner!</small>
</p>
</div>
</div>
</body>
</html>

View File

@@ -2,147 +2,12 @@
<head>
<title>Excessive Load Warning</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
body {
background: #00A9EA;
color: white;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
text-align: center;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
}
<link rel="stylesheet" type="text/css" href="/assets/5xx.css" />
h1 {
font-size: 250px;
color: #fff;
opacity: 0.5;
margin: 10px;
cursor: pointer;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
p {
font-size: 20px;
}
p strong {
font-size: 28px;
}
@media (max-width: 640px) {
h1 {
font-size: 125px;
}
p {
font-size: 16px;
}
p strong {
font-size: 20px;
}
}
.center {
position: relative;
top: 50%;
-webkit-transform: translateY(50%);
-ms-transform: translateY(50%);
transform: translateY(50%);
}
@-webkit-keyframes bounce {
0%, 20%, 53%, 80%, 100% {
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%, 43% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -30px, 0);
transform: translate3d(0, -30px, 0);
}
70% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -15px, 0);
transform: translate3d(0, -15px, 0);
}
90% {
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
@keyframes bounce {
0%, 20%, 53%, 80%, 100% {
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%, 43% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -30px, 0);
transform: translate3d(0, -30px, 0);
}
70% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -15px, 0);
transform: translate3d(0, -15px, 0);
}
90% {
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
.bounce {
-webkit-animation-name: bounce;
animation-name: bounce;
-webkit-transform-origin: center bottom;
-ms-transform-origin: center bottom;
transform-origin: center bottom;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.animated.hinge {
-webkit-animation-duration: 2s;
animation-duration: 2s;
}
.hide {
display: none;
}
</style>
<script type="text/javascript">
window.onload = function() {
var count = 0,
bounce = document.getElementById('click-me');
let count = 0;
const bounce = document.getElementById('click-me');
bounce.onclick = function() {
count++;
bounce.className = '';

135
public/5xx.css Normal file
View File

@@ -0,0 +1,135 @@
body {
background: #00A9EA;
color: white;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
text-align: center;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
}
h1 {
font-size: 250px;
color: #fff;
opacity: 0.5;
margin: 10px;
cursor: pointer;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
p {
font-size: 20px;
}
p strong {
font-size: 28px;
}
@media (max-width: 640px) {
h1 {
font-size: 125px;
}
p {
font-size: 16px;
}
p strong {
font-size: 20px;
}
}
.center {
position: relative;
top: 50%;
-webkit-transform: translateY(50%);
-ms-transform: translateY(50%);
transform: translateY(50%);
}
@-webkit-keyframes bounce {
0%, 20%, 53%, 80%, 100% {
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%, 43% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -30px, 0);
transform: translate3d(0, -30px, 0);
}
70% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -15px, 0);
transform: translate3d(0, -15px, 0);
}
90% {
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
@keyframes bounce {
0%, 20%, 53%, 80%, 100% {
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
40%, 43% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -30px, 0);
transform: translate3d(0, -30px, 0);
}
70% {
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
-webkit-transform: translate3d(0, -15px, 0);
transform: translate3d(0, -15px, 0);
}
90% {
-webkit-transform: translate3d(0,-4px,0);
transform: translate3d(0,-4px,0);
}
}
.bounce {
-webkit-animation-name: bounce;
animation-name: bounce;
-webkit-transform-origin: center bottom;
-ms-transform-origin: center bottom;
transform-origin: center bottom;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.animated.hinge {
-webkit-animation-duration: 2s;
animation-duration: 2s;
}
.hide {
display: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -1,9 +1,5 @@
{
"cache": "Cache",
"post-cache": "التخزين المؤقت للمشاركات",
"group-cache": "التخزين المؤقت للمجموعات",
"local-cache": "تخزين مؤقت محلي",
"object-cache": "تخزين مؤقت للأشياء",
"percent-full": "1% كاملة",
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
"items-in-cache": "العناصر في التخزين المؤقت"

View File

@@ -9,5 +9,9 @@
"filter-type": "نوع الحدث",
"filter-start": "تاريخ البدء",
"filter-end": "تاريخ الانتهاء",
"filter-user": "Filter by User",
"filter-user.placeholder": "قم بكتابة اسم مستخدم للتصفية...",
"filter-group": "Filter by Group",
"filter-group.placeholder": "قم بكتابة اسم مجموعة للتصفية...",
"filter-per-page": "لكل صفحة"
}

View File

@@ -0,0 +1,9 @@
{
"jobs": "Jobs",
"job-name": "Job Name",
"schedule": "Schedule",
"next-run": "Next Run",
"last-duration": "Last Duration",
"running": "Running",
"active": "Active"
}

View File

@@ -6,10 +6,13 @@
"new-users": "مستخدم جديد",
"posts": "مشاركات",
"topics": "مواضيع",
"remote-posts": "Remote Posts",
"remote-topics": "Remote Topics",
"messages": "Messages",
"page-views-seven": "آخر 7 ايام",
"page-views-thirty": "آخر 30 يوماً",
"page-views-last-day": "آخر 24 ساعة",
"page-views-custom": "مدة زمنية مخصصة",
"page-views-custom": "Custom Range",
"page-views-custom-start": "بداية المدة",
"page-views-custom-end": "نهاية المده",
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
@@ -75,6 +78,7 @@
"graphs.page-views-registered": "زيارات الصفحات المسجلة",
"graphs.page-views-guest": "زيارات الصفحات للزوار",
"graphs.page-views-bot": "زيارات الصفحات الآلية",
"graphs.page-views-ap": "ActivityPub Page Views",
"graphs.unique-visitors": "زوار فريدين",
"graphs.registered-users": "مستخدمين مسجلين",
"graphs.guest-users": "المستخدمين الزوار",
@@ -94,5 +98,8 @@
"view-as-json": "View as JSON",
"expand-analytics": "Expand analytics",
"clear-search-history": "Clear Search History",
"clear-search-history-confirm": "Are you sure you want to clear entire search history?"
"clear-search-history-confirm": "Are you sure you want to clear entire search history?",
"search-term": "Term",
"search-count": "Count",
"view-all": "View all"
}

View File

@@ -3,12 +3,12 @@
"ip": "رقم الآي بي <strong> %1 </strong>",
"nodes-responded": "عدد %1 نقطة/نقاط استجابوا خلال %2 جزء من الثانية.",
"host": "المضيف",
"primary": "primary / run jobs",
"primary": "primary / jobs",
"pid": "pid",
"nodejs": "nodejs",
"online": "online",
"git": "git",
"process-memory": "process memory",
"process-memory": "rss/heap used",
"system-memory": "system memory",
"used-memory-process": "Used memory by process",
"used-memory-os": "Used system memory",
@@ -19,6 +19,7 @@
"registered": "Registered",
"sockets": "Sockets",
"connection-count": "Connection Count",
"guests": "Guests",
"info": "Info"

View File

@@ -12,6 +12,8 @@
"container.card": "Card",
"container.card-header": "Card Header",
"container.card-body": "Card Body",
"container.title": "Title",
"container.body": "Body",
"container.alert": "Alert",
"alert.confirm-delete": "Are you sure you wish to delete this widget?",

View File

@@ -1,13 +1,22 @@
{
"manage-categories": "Manage Categories",
"add-category": "Add category",
"add-local-category": "Add Local category",
"add-remote-category": "Add Remote category",
"remove": "Remove",
"rename": "Rename",
"jump-to": "Jump to...",
"settings": "اعدادات القسم",
"edit-category": "Edit Category",
"privileges": "الصلاحيات",
"back-to-categories": "Back to categories",
"id": "Category ID",
"name": "Category Name",
"handle": "Category Handle",
"handle.help": "Your category handle is used as a representation of this category across other networks, similar to a username. A category handle must not match an existing username or user group.",
"description": "Category Description",
"topic-template": "Topic Template",
"topic-template.help": "Define a template for new topics created in this category.",
"bg-color": "Background Colour",
"text-color": "Text Colour",
"bg-image-size": "Background Image Size",
@@ -37,6 +46,7 @@
"disable": "Disable",
"edit": "Edit",
"analytics": "Analytics",
"federation": "Federation",
"view-category": "View category",
"set-order": "Set order",
@@ -45,7 +55,7 @@
"select-category": "Select Category",
"set-parent-category": "Set Parent Category",
"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.",
"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis.",
"privileges.category-selector": "Configuring privileges for ",
"privileges.warning": "<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.",
"privileges.section-viewing": "Viewing Privileges",
@@ -76,10 +86,32 @@
"analytics.topics-daily": "<strong>Figure 3</strong> &ndash; Daily topics created in this category</small>",
"analytics.posts-daily": "<strong>Figure 4</strong> &ndash; Daily posts made in this category</small>",
"federation.title": "Federation settings for \"%1\" category",
"federation.disabled": "Federation is disabled site-wide, so category federation settings are currently unavailable.",
"federation.disabled-cta": "Federation Settings &rarr;",
"federation.syncing-header": "Synchronization",
"federation.syncing-intro": "A category can follow a \"Group Actor\" via the ActivityPub protocol. If content is received from one of the actors listed below, it will be automatically added to this category.",
"federation.syncing-caveat": "N.B. Setting up syncing here establishes a one-way synchronization. NodeBB attempts to subscribe/follow the actor, but the reverse cannot be assumed.",
"federation.syncing-none": "This category is not currently following anybody.",
"federation.syncing-add": "Synchronize with...",
"federation.syncing-actorUri": "Actor",
"federation.syncing-follow": "Follow",
"federation.syncing-unfollow": "Unfollow",
"federation.followers": "Remote users following this category",
"federation.followers-handle": "Handle",
"federation.followers-id": "ID",
"federation.followers-none": "No followers.",
"federation.followers-autofill": "Autofill",
"alert.created": "Created",
"alert.create-success": "Category successfully created!",
"alert.none-active": "You have no active categories.",
"alert.create": "Create a Category",
"alert.add": "Add a Category",
"alert.add-help": "Remote categories can be added to the categories listing by specifying their handle.<br /><br /><strong>Note</strong> — The remote category may not reflect all topics published unless at least one local user tracks/watches it.",
"alert.rename": "Rename a Remote Category",
"alert.rename-help": "Please enter a new name for this category. Leave blank to restore original name.",
"alert.confirm-remove": "Do you really want to remove this category? You can add it back at any time.",
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
"alert.purge-success": "Category purged!",
"alert.copy-success": "Settings Copied!",

View File

@@ -0,0 +1,16 @@
{
"title": "Manage Custom Reasons",
"create-reason": "Create Reason",
"edit-reason": "Edit Reason",
"reasons-help": "Reasons are predefined explanations used when banning or muting users, or when rejecting posts in the post queue.",
"reason-title": "Title",
"reason-type": "Type",
"reason-body": "Body",
"reason-all": "All",
"reason-ban": "Ban",
"reason-mute": "Mute",
"reason-post-queue": "Post Queue",
"reason-type-help": "The type of action this reason applies to. If 'All' is selected, this reason will be available for all action types.",
"custom-reasons-saved": "Custom reasons saved successfully",
"delete-reason-confirm-x": "Are you sure you want to delete the custom reason with the title <strong>%1</strong>?"
}

View File

@@ -29,6 +29,7 @@
"access-topics": "Access Topics",
"create-topics": "Create Topics",
"reply-to-topics": "Reply to Topics",
"crosspost-topics": "Cross-post Topics",
"schedule-topics": "Schedule Topics",
"tag-topics": "Tag Topics",
"edit-posts": "Edit Posts",

View File

@@ -0,0 +1,28 @@
{
"title": "Manage Custom User Fields",
"create-field": "Create Field",
"edit-field": "Edit Field",
"manage-custom-fields": "Manage Custom Fields",
"type-of-input": "Type of input",
"key": "Key",
"name": "Name",
"icon": "Icon",
"type": "Type",
"min-rep": "Minimum Reputation",
"input-type-text": "Input (Text)",
"input-type-link": "Input (Link)",
"input-type-number": "Input (Number)",
"input-type-date": "Input (Date)",
"input-type-select": "Select",
"input-type-select-multi": "Select Multiple",
"select-options": "Options",
"select-options-help": "Add one option per line for the select element",
"minimum-reputation": "Minimum reputation",
"minimum-reputation-help": "If a user has less than this value they won't be able to use this field",
"delete-field-confirm-x": "Do you really want to delete custom field \"%1\"?",
"custom-fields-saved": "Custom fields saved",
"visibility": "Visibility",
"visibility-all": "Everyone can see the field",
"visibility-loggedin": "Only logged in users can see the field",
"visibility-privileged": "Only privileged users like admins & moderators can see the field"
}

View File

@@ -22,6 +22,8 @@
"delete-content": "Delete User(s) <strong>Content</strong>",
"purge": "Delete <strong>User(s)</strong> and <strong>Content</strong>",
"download-csv": "Download CSV",
"custom-user-fields": "Custom User Fields",
"custom-reasons": "Custom Reasons",
"manage-groups": "Manage Groups",
"set-reputation": "Set Reputation",
"add-group": "Add Group",
@@ -38,6 +40,7 @@
"250-per-page": "250 per page",
"500-per-page": "500 per page",
"search.help": "Use &quot;*&quot; to make partial searches, for example &quot;*query&quot;",
"search.uid": "By User ID",
"search.uid-placeholder": "Enter a user ID to search",
"search.username": "By User Name",
@@ -53,6 +56,7 @@
"inactive.12-months": "12 months",
"users.uid": "uid",
"users.user-id": "User ID",
"users.username": "username",
"users.email": "email",
"users.no-email": "(no email)",
@@ -61,6 +65,7 @@
"users.validation-pending": "Validation Pending",
"users.validation-expired": "Validation Expired",
"users.ip": "IP",
"users.recent-ips": "Recent IPs",
"users.postcount": "postcount",
"users.reputation": "reputation",
"users.flags": "flags",
@@ -76,9 +81,11 @@
"temp-ban.length": "Length",
"temp-ban.reason": "Reason <span class=\"text-muted\">(Optional)</span>",
"temp-ban.select-reason": "Select a reason",
"temp-ban.hours": "Hours",
"temp-ban.days": "Days",
"temp-ban.explanation": "Enter the length of time for the ban. Note that a time of 0 will be a considered a permanent ban.",
"temp-mute.explanation": "Enter the length of time for the mute. Note that a time of 0 will be a considered a permanent mute.",
"alerts.confirm-ban": "Do you really want to ban this user <strong>permanently</strong>?",
"alerts.confirm-ban-multi": "Do you really want to ban these users <strong>permanently</strong>?",
@@ -121,6 +128,28 @@
"alerts.email-sent-to": "An invitation email has been sent to %1",
"alerts.x-users-found": "%1 user(s) found, (%2 seconds)",
"alerts.select-a-single-user-to-change-email": "Select a single user to change email",
"export": "Export",
"export-users-fields-title": "Select CSV Fields",
"export-field-email": "Email",
"export-field-username": "Username",
"export-field-uid": "UID",
"export-field-ip": "IP",
"export-field-joindate": "Join date",
"export-field-lastonline": "Last Online",
"export-field-lastposttime": "Last Post Time",
"export-field-reputation": "Reputation",
"export-field-postcount": "Post Count",
"export-field-topiccount": "Topic Count",
"export-field-profileviews": "Profile Views",
"export-field-followercount": "Follower Count",
"export-field-followingcount": "Following Count",
"export-field-fullname": "Full Name",
"export-field-website": "Website",
"export-field-location": "Location",
"export-field-birthday": "Birthday",
"export-field-signature": "Signature",
"export-field-aboutme": "About Me",
"export-users-started": "Exporting users as csv, this might take a while. You will receive a notification when it is complete.",
"export-users-completed": "Users exported as csv, click here to download.",
"email": "Email",

View File

@@ -38,6 +38,7 @@
"settings/tags": "الكلمات المفتاحية",
"settings/notifications": "التنبيهات",
"settings/api": "API Access",
"settings/activitypub": "Federation (ActivityPub)",
"settings/sounds": "Sounds",
"settings/social": "Social",
"settings/cookies": "Cookies",
@@ -47,6 +48,14 @@
"settings.page-title": "%1 Settings",
"section-federation": "Federation",
"federation/general": "General",
"federation/content": "Content",
"federation/rules": "Categorization",
"federation/relays": "Relays",
"federation/pruning": "Storage",
"federation/safety": "Trust & Safety",
"section-appearance": "Appearance",
"appearance/themes": "Themes",
"appearance/skins": "Skins",
@@ -69,6 +78,7 @@
"advanced/logs": "Logs",
"advanced/errors": "Errors",
"advanced/cache": "Cache",
"advanced/jobs": "Jobs",
"development/logger": "Logger",
"development/info": "Info",

View File

@@ -0,0 +1,67 @@
{
"intro-lead": "What is Federation?",
"intro-body": "NodeBB is able to communicate with other NodeBB instances that support it. This is achieved through a protocol called <a href=\"https://activitypub.rocks/\">ActivityPub</a>. If enabled, NodeBB will also be able to communicate with other apps and websites that use ActivityPub (e.g. Mastodon, Peertube, etc.)",
"general": "General",
"pruning": "Content Pruning",
"content-pruning": "Days to keep remote content",
"content-pruning-help": "Note that remote content that has received engagement (a reply or a upvote/downvote) will be preserved. (0 for disabled)",
"user-pruning": "Days to cache remote user accounts",
"user-pruning-help": "Remote user accounts will only be pruned if they have no posts. Otherwise they will be re-retrieved. (0 for disabled)",
"enabled": "Enable Federation",
"enabled-help": "If enabled, will allow this NodeBB will be able to communicate with all Activitypub-enabled clients on the wider fediverse.",
"allowLoopback": "Allow loopback processing",
"allowLoopback-help": "Useful for debugging purposes only. You should probably leave this disabled.",
"probe": "Open in App",
"probe-enabled": "Try to open ActivityPub-enabled resources in NodeBB",
"probe-enabled-help": "If enabled, NodeBB will check every external link for an ActivityPub equivalent, and load it in NodeBB instead.",
"probe-timeout": "Lookup Timeout (milliseconds)",
"probe-timeout-help": "(Default: 2000) If the lookup query does not receive a response within the set timeframe, will send the user to the link directly instead. Adjust this number higher if sites are responding slowly and you wish to give extra time.",
"rules": "Categorization",
"rules-intro": "Content discovered via ActivityPub can be automatically categorized based on certain rules (e.g. hashtag)",
"rules.modal.title": "How it works",
"rules.modal.instructions": "Any incoming content is checked against these categorization rules, and matching content is automatically moved into the category of choice.<br /><br /><strong>N.B.</strong> Content that is already categorized (i.e. in a remote category) will not pass through these rules.",
"rules.add": "Add New Rule",
"rules.help-hashtag": "Topics containing this case-insensitive hashtag will match. Do not enter the <code>#</code> symbol",
"rules.help-user": "Topics created by the entered user will match. Enter a handle or full ID (e.g. <code>bob@example.org</code> or <code>https://example.org/users/bob</code>.",
"rules.type": "Type",
"rules.value": "Value",
"rules.cid": "Category",
"relays": "Relays",
"relays.intro": "A relay improves discovery of content to and from your NodeBB. Subscribing to a relay means content received by the relay is forwarded here, and content posted here is syndicated outward by the relay.",
"relays.warning": "Note: Relays can send larges amounts of traffic in, and may increase storage and processing costs.",
"relays.litepub": "NodeBB follows the LitePub-style relay standard. The URL you enter here should end with <code>/actor</code>.",
"relays.add": "Add New Relay",
"relays.relay": "Relay",
"relays.state": "State",
"relays.state-0": "Pending",
"relays.state-1": "Receiving only",
"relays.state-2": "Active",
"relays.errors.invalid-url": "Please enter a valid URL",
"blocklists": "Third-party Blocklists",
"blocklists-help": "For the safety of you and your users, maintaining a blocklist is an essential part of any trust &amp; safety effort when working with content from outside the local moderation scope. NodeBB ships with some recommended defaults, and these can be customized here.",
"blocklists-default": "NodeBB ships with two blocklists by default, the <a href=\"https://about.iftas.org/library/iftas-dni-list/\">IFTAS Do Not Interact Denylist</a> and the <a href=\"https://about.iftas.org/library/iftas-abandoned-and-unmanaged-domain-list/\">IFTAS Abandoned and Unmanaged Domain Denylist</a>.",
"blocklists.url": "URL",
"blocklists.count": "Domains",
"blocklists.add": "Add New Blocklist",
"blocklists.add-help": "Enter the URL of the blocklist you would like to add. NodeBB understands blocklists that match the format used by <a href=\"https://iftas.org\">IFTAS</a>.",
"blocklists.refreshed": "Blocklist refreshed — it now contains %1 entries",
"blocklists.view.title": "View Blocklist",
"blocklists.view.intro": "These are the %1 domain(s) blocked by this blocklist:",
"server-filtering": "Filtering",
"count": "This NodeBB is currently aware of <strong>%1</strong> server(s)",
"server.filter-help": "Specify servers you would like to bar from federating with your NodeBB. Alternatively, you may opt to selectively <em>allow</em> federation with specific servers, instead. Both options are supported, although they are mutually exclusive.",
"server.filter-help-hostname": "Enter just the instance hostname below (e.g. <code>example.org</code>), separated by line breaks.",
"server.filter-allow-list": "Use this as an Allow List instead",
"content.outgoing": "Outgoing",
"content.summary-limit": "Character count after which a summary is generated",
"content.summary-limit-help": "When content is federated out that exceeds this character count, a <code>summary</code> is generated, comprising of all complete sentences prior to this limit. (Default: 500)",
"content.break-string": "Note/Article Delimiter",
"content.break-string-help": "This delimiter can be manually inserted by power users when composing new topics. It instructs NodeBB to use content up until that point as part of the <code>summary</code>. If this string is not used, then the character count fallback applies. (Default: <code>[...]</code>)",
"content.world-default-cid": "Default category ID for &quot;World&quot; page composer"
}

View File

@@ -41,9 +41,6 @@
"sockets.default-placeholder": "Default: %1",
"sockets.delay": "Reconnection Delay",
"analytics.settings": "Analytics Settings",
"analytics.max-cache": "Analytics Cache Max Value",
"analytics.max-cache-help": "On high-traffic installs, the cache could be exhausted continuously if there are more concurrent active users than the Max Cache value. (Restart required)",
"compression.settings": "Compression Settings",
"compression.enable": "Enable Compression",
"compression.help": "This setting enables gzip compression. For a high-traffic website in production, the best way to put compression in place is to implement it at a reverse proxy level. You can enable it here for testing purposes."

View File

@@ -5,11 +5,11 @@
"disable-editing": "Disable chat message editing/deletion",
"disable-editing-help": "Administrators and global moderators are exempt from this restriction",
"max-length": "Maximum length of chat messages",
"max-length-remote": "Maximum length of remote chat messages",
"max-length-remote-help": "This value is usually set higher than the chat message maximum for local users as remote messages tend to be longer (with @ mentions, etc.)",
"max-chat-room-name-length": "Maximum length of chat room names",
"max-room-size": "Maximum number of users in chat rooms",
"delay": "Time between chat messages (ms)",
"notification-delay": "Notification delay for chat messages",
"notification-delay-help": "Additional messages sent between this time are collated, and the user is notified once per delay period. Set this to 0 to disable the delay.",
"restrictions.seconds-edit-after": "Number of seconds a chat message will remain editable.",
"restrictions.seconds-delete-after": "Number of seconds a chat message will remain deletable."
}

View File

@@ -28,14 +28,22 @@
"smtp-transport.password": "Password",
"smtp-transport.pool": "Enable pooled connections",
"smtp-transport.pool-help": "Pooling connections prevents NodeBB from creating a new connection for every email. This option only applies if SMTP Transport is enabled.",
"smtp-transport.allow-self-signed": "Allow self-signed certificates",
"smtp-transport.allow-self-signed-help": "Enabling this setting will allow you to use self-signed or invalid TLS certificates.",
"smtp-transport.test-success": "SMTP Test email sent successfully.",
"template": "Edit Email Template",
"template.select": "Select Email Template",
"template.revert": "Revert to Original",
"test-smtp-settings": "Test SMTP Settings",
"testing": "Email Testing",
"testing.success": "Test Email Sent.",
"testing.select": "Select Email Template",
"testing.send": "Send Test Email",
"testing.send-help": "The test email will be sent to the currently logged in user's email address.",
"testing.send-help-plugin": "<strong>\"%1\"</strong> will be used to send test emails.",
"testing.send-help-smtp": "SMTP transport is enabled and will be used to send emails.",
"testing.send-help-no-plugin": "No emailer plugin is installed to send emails, nodemailer will be used by default.",
"testing.send-help": "The test email will be sent to the currently logged in user's email address using the saved settings on this page. ",
"subscriptions": "Email Digests",
"subscriptions.disable": "Disable email digests",
"subscriptions.hour": "Digest Hour",

View File

@@ -15,10 +15,10 @@
"title-layout": "Title Layout",
"title-layout-help": "Define how the browser title will be structured ie. &#123;pageTitle&#125; | &#123;browserTitle&#125;",
"description.placeholder": "A short description about your community",
"description": "وصف الموقع",
"description": "Site Description",
"keywords": "الكلمات الدليله للموقع",
"keywords-placeholder": "Keywords describing your community, comma-separated",
"logo-and-icons": "Site Logo & Icons",
"logo-and-icons": "Media & Branding",
"logo.image": "صورة",
"logo.image-placeholder": "Path to a logo to display on forum header",
"logo.upload": "رفع",
@@ -35,6 +35,8 @@
"touch-icon.help": "Recommended size and format: 512x512, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
"maskable-icon": "Maskable (Homescreen) Icon",
"maskable-icon.help": "Recommended size and format: 512x512, PNG format only. If no maskable icon is specified, NodeBB will fall back to the Touch Icon.",
"screenshot": "Screenshot",
"screenshot.help": "Recommended size and format: between 320px and 3480px, JPG and PNG format only. If no screenshot is specified, NodeBB will fall back to a generic screenshot",
"outgoing-links": "Outgoing Links",
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
"search": "Search",
@@ -48,5 +50,16 @@
"background-color-help": "Color used for splash screen background when website is installed as a PWA",
"undo-timeout": "Undo Timeout",
"undo-timeout-help": "Some operations such as moving topics will allow for the moderator to undo their action within a certain timeframe. Set to 0 to disable undo completely.",
"topic-tools": "Topic Tools"
}
"topic-tools": "Topic Tools",
"home-page": "Home Page",
"home-page-route": "Home Page Route",
"home-page-description": "Choose what page is shown when users navigate to the root URL of your forum.",
"custom-route": "Custom Route",
"allow-user-home-pages": "Allow User Home Pages",
"home-page-title": "Title of the home page (default \"Home\")",
"default-language": "اللغة الافتراضية",
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي",
"default-language-help": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
"post-sharing": "Post Sharing",
"info-plugins-additional": "Plugins can add additional networks for sharing posts."
}

View File

@@ -1,8 +0,0 @@
{
"settings": "Settings",
"guest-settings": "Guest Settings",
"handles.enabled": "Allow guest handles",
"handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"",
"topic-views.enabled": "Allow guests to increase topic view counts",
"reply-notifications.enabled": "Allow guests to generate reply notifications"
}

View File

@@ -1,8 +0,0 @@
{
"home-page": "Home Page",
"description": "Choose what page is shown when users navigate to the root URL of your forum.",
"home-page-route": "Home Page Route",
"custom-route": "Custom Route",
"allow-user-home-pages": "Allow User Home Pages",
"home-page-title": "Title of the home page (default \"Home\")"
}

View File

@@ -1,6 +0,0 @@
{
"language-settings": "اعدادات اللغة",
"description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
"default-language": "اللغة الافتراضية",
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي"
}

View File

@@ -10,7 +10,7 @@
"id": "ID: <small>optional</small>",
"properties": "Properties:",
"groups": "Groups:",
"show-to-groups": "Show to Groups:",
"open-new-window": "Open in a new window",
"dropdown": "Dropdown",
"dropdown-placeholder": "Place your dropdown menu items below, ie: <br/>&lt;li&gt;&lt;a class=&quot;dropdown-item&quot; href&#x3D;&quot;https://myforum.com&quot;&gt;Link 1&lt;/a&gt;&lt;/li&gt;",

View File

@@ -3,5 +3,7 @@
"welcome-notification": "Welcome Notification",
"welcome-notification-link": "Welcome Notification Link",
"welcome-notification-uid": "Welcome Notification User (UID)",
"post-queue-notification-uid": "Post Queue User (UID)"
"post-queue-notification-uid": "Post Queue User (UID)",
"notification-delay": "Delay for sending notification emails (seconds)",
"notification-delay-help": "If the user has read the notification within this time, the email will not be sent.<br/>Default: 60 seconds."
}

View File

@@ -4,8 +4,11 @@
"sorting.post-default": "Default Post Sorting",
"sorting.oldest-to-newest": "Oldest to Newest",
"sorting.newest-to-oldest": "Newest to Oldest",
"sorting.recently-replied": "Recently Replied",
"sorting.recently-created": "Recently Created",
"sorting.most-votes": "Most Votes",
"sorting.most-posts": "Most Posts",
"sorting.most-views": "Most Views",
"sorting.topic-default": "Default Topic Sorting",
"length": "Post Length",
"post-queue": "Post Queue",

View File

@@ -2,7 +2,14 @@
"reputation": "Reputation Settings",
"disable": "Disable Reputation System",
"disable-down-voting": "Disable Down Voting",
"votes-are-public": "All Votes Are Public",
"upvote-visibility": "Up Vote visibility",
"upvote-visibility-all": "Everyone can see up votes",
"upvote-visibility-loggedin": "Only logged in users can see up votes",
"upvote-visibility-privileged": "Only privileged users like admins & moderators can see up votes",
"downvote-visibility": "Down Vote visibility",
"downvote-visibility-all": "Everyone can see down votes",
"downvote-visibility-loggedin": "Only logged in users can see down votes",
"downvote-visibility-privileged": "Only privileged users like admins & moderators can see down votes",
"thresholds": "Activity Thresholds",
"min-rep-upvote": "Minimum reputation to upvote posts",
"upvotes-per-day": "Upvotes per day (set to 0 for unlimited upvotes)",
@@ -13,7 +20,6 @@
"min-rep-chat": "Minimum reputation to send chat messages",
"min-rep-post-links": "Minimum reputation to post links",
"min-rep-flag": "Minimum reputation to flag posts",
"min-rep-website": "Minimum reputation to add \"Website\" to user profile",
"min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile",
"min-rep-signature": "Minimum reputation to add \"Signature\" to user profile",
"min-rep-profile-picture": "Minimum reputation to add \"Profile Picture\" to user profile",
@@ -23,7 +29,12 @@
"flags.limit-per-target": "Maximum number of times something can be flagged",
"flags.limit-per-target-placeholder": "Default: 0",
"flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a &quot;report&quot; and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.",
"flags.auto-flag-on-downvote-threshold": "Number of downvotes to auto flag posts (Set to 0 to disable, default: 0)",
"flags.limit-post-flags-per-day": "Maximum number of times a user can flag posts in a day",
"flags.limit-post-flags-per-day-help": "Set to 0 to disable, (default: 10)",
"flags.limit-user-flags-per-day": "Maximum number of times a user can flag users in a day",
"flags.limit-user-flags-per-day-help": "Set to 0 to disable, (default: 10)",
"flags.auto-flag-on-downvote-threshold": "Number of downvotes to auto flag posts",
"flags.auto-flag-on-downvote-threshold-help": "Set to 0 to disable, (default: 0)",
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned",
"flags.action-on-resolve": "Do the following when a flag is resolved",
"flags.action-on-reject": "Do the following when a flag is rejected",

View File

@@ -1,4 +0,0 @@
{
"post-sharing": "Post Sharing",
"info-plugins-additional": "Plugins can add additional networks for sharing posts."
}

View File

@@ -9,9 +9,10 @@
"private-extensions": "File extensions to make private",
"private-uploads-extensions-help": "Enter comma-separated list of file extensions to make private here (e.g. <code>pdf,xls,doc</code>). An empty list means all files are private.",
"resize-image-width-threshold": "Resize images if they are wider than specified width",
"resize-image-width-threshold-help": "(in pixels, default: 1520 pixels, set to 0 to disable)",
"resize-image-width-threshold-help": "(in pixels, default: 2000 pixels, set to 0 to disable)",
"resize-image-width": "Resize images down to specified width",
"resize-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
"resize-image-keep-original": "Keep original image after resize",
"resize-image-quality": "Quality to use when resizing images",
"resize-image-quality-help": "Use a lower quality setting to reduce the file size of resized images.",
"max-file-size": "الحد الأقصى لحجم الملف (بالكيبيبايت)",
@@ -20,7 +21,13 @@
"reject-image-width-help": "Images wider than this value will be rejected.",
"reject-image-height": "Maximum Image Height (in pixels)",
"reject-image-height-help": "Images taller than this value will be rejected.",
"convert-pasted-images-to": "Convert pasted images to:",
"convert-pasted-images-to-default": "No Conversion (Keep Original Format)",
"convert-pasted-images-to-png": "PNG",
"convert-pasted-images-to-jpeg": "JPEG",
"convert-pasted-images-to-webp": "WebP",
"allow-topic-thumbnails": "السماح للاعضاء برفع الصور المصغرة للموضوع",
"show-post-uploads-as-thumbnails": "Show post uploads as thumbnails",
"topic-thumb-size": "حجم الصورة المصغرة للموضوع",
"allowed-file-extensions": "إمتدادات الملفات المسموح بها",
"allowed-file-extensions-help": "أدخل قائمة بامتدادات الملفات مفصولة بفواصل (مثال: <code>pdf,xls,doc</code>). القائمة الفارغة تعني أن كل الامتدادات مسموح بها.",
@@ -33,7 +40,7 @@
"default-avatar": "الصورة الرمزية الافتراضية",
"upload": "رفع",
"profile-image-dimension": "أبعاد الصورة الرمزية",
"profile-image-dimension-help": "(بالبكسل، الافتراضي: 128 بكسل)",
"profile-image-dimension-help": "(in pixels, default: 200 pixels)",
"max-profile-image-size": "الحد الأقصى لحجم الصورة الرمزية",
"max-profile-image-size-help": "(بالكيبيبايت، الافتراضي: 256)",
"max-cover-image-size": "الحد الأقصى لحجم صورة الغلاف",

View File

@@ -64,6 +64,7 @@
"show-email": "عرض البريد الإلكتروني",
"show-fullname": "عرض الاسم الكامل",
"restrict-chat": "السماح فقط برسائل الدردشة من المستخدمين الذين أتبعهم",
"disable-incoming-chats": "Disable incoming chat messages",
"outgoing-new-tab": "Open outgoing links in new tab",
"topic-search": "Enable In-Topic Searching",
"update-url-with-post-index": "Update url with post index while browsing topics",
@@ -88,5 +89,10 @@
"restrictions.seconds-before-new": "Seconds before a new user can make their first post",
"restrictions.seconds-edit-after-new": "Number of seconds a post remains editable for new users (set to 0 to disable)",
"restrictions.milliseconds-between-messages": "Time between chat messages for new users (ms)",
"restrictions.groups-exempt-from-new-user-restrictions": "Select groups that should be exempt from the new user restrictions"
}
"restrictions.groups-exempt-from-new-user-restrictions": "Select groups that should be exempt from the new user restrictions",
"guest-settings": "Guest Settings",
"handles.enabled": "Allow guest handles",
"handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\"",
"topic-views.enabled": "Allow guests to increase topic view counts",
"reply-notifications.enabled": "Allow guests to generate reply notifications"
}

View File

@@ -5,6 +5,7 @@
"disable-rss-feeds": "Disable RSS Feeds",
"disable-sitemap-xml": "Disable Sitemap.xml",
"sitemap-topics": "Number of Topics to display in the Sitemap",
"sitemap-cache-duration-hours": "Sitemap Cache Duration (hours)",
"clear-sitemap-cache": "Clear Sitemap Cache",
"view-sitemap": "View Sitemap"
}

View File

@@ -0,0 +1,10 @@
{
"post-sort-option": "Post sort option, %1",
"topic-sort-option": "Topic sort option, %1",
"user-avatar-for": "User avatar for %1",
"profile-page-for": "Profile page for user %1",
"user-watched-tags": "User watched tags",
"delete-upload-button": "Delete upload button",
"group-page-link-for": "Group page link for %1",
"show-crossposts": "Show Cross-posts"
}

View File

@@ -1,9 +1,13 @@
{
"category": "قسم",
"subcategories": "قسم فرعي",
"uncategorized": "World",
"uncategorized.description": "Topics from outside of this forum. Views and opinions represented here may not reflect those of this forum and its members.",
"handle.description": "This category can be followed from the open social web via the handle %1",
"new-topic-button": "موضوع جديد",
"guest-login-post": "سجل الدخول للمشاركة",
"no-topics": "<strong>لا توجد مواضيع في هذه القسم</strong>لم لا تحاول إنشاء موضوع؟<br />",
"no-followers": "Nobody on this website is tracking or watching this category. Track or watch this category in order to begin receiving updates.",
"browsing": "تصفح",
"no-replies": "لم يرد أحد",
"no-new-posts": "لا توجد مشاركات جديدة.",

View File

@@ -1,8 +1,10 @@
{
"invalid-data": "بيانات غير صحيحة",
"invalid-config-field-value": "Invalid value for config field \"%1\": %2",
"invalid-json": "Invalid JSON",
"wrong-parameter-type": "A value of type %3 was expected for property `%1`, but %2 was received instead",
"required-parameters-missing": "Required parameters were missing from this API call: %1",
"reserved-ip-address": "Network requests to reserved IP ranges are not allowed.",
"not-logged-in": "لم تقم بتسجيل الدخول",
"account-locked": "تم حظر حسابك مؤقتًا.",
"search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل",
@@ -31,8 +33,9 @@
"invalid-path": "Invalid path",
"folder-exists": "Folder exists",
"invalid-pagination-value": "رقم الصفحة غير صحيح ، يجب أن يكون بين %1 و %2 .",
"invalid-unread-cutoff": "Invalid unread cutoff value, must be at least 1 and at most %1",
"username-taken": "اسم المستخدم مأخوذ",
"email-taken": "البريد الالكتروني مأخوذ",
"email-taken": "Email address is already taken.",
"email-nochange": "The email entered is the same as the email already on file.",
"email-invited": "Email was already invited",
"email-not-confirmed": "Posting in some categories or topics is enabled once your email is confirmed, please click here to send a confirmation email.",
@@ -67,7 +70,8 @@
"no-chat-room": "Chat room does not exist",
"no-privileges": "لاتملك الصلاحيات اللازمة للقيام بهذه العملية",
"category-disabled": "قائمة معطلة",
"topic-locked": "الموضوع مقفول",
"post-deleted": "Post deleted",
"topic-locked": "Topic locked",
"post-edit-duration-expired": "يسمح لك بتعديل مشاركتك حتى %1 ثانية من نشرها",
"post-edit-duration-expired-minutes": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة من نشرها",
"post-edit-duration-expired-minutes-seconds": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة و %2 ثوان من نشرها",
@@ -145,6 +149,7 @@
"post-already-restored": "سبق وتم إلغاء حذف هذا الرد",
"topic-already-deleted": "سبق وتم حذف هذا الموضوع",
"topic-already-restored": "سبق وتم إلغاء حذف هذا الرد",
"topic-already-crossposted": "This topic has already been cross-posted there.",
"cant-purge-main-post": "لا يمكنك محو المشاركة الأساسية، يرجى حذف الموضوع بدلاً عن ذلك",
"topic-thumbnails-are-disabled": "الصور المصغرة غير مفعلة.",
"invalid-file": "ملف غير مقبول",
@@ -153,6 +158,9 @@
"about-me-too-long": "نأسف، ( عني ) لا يمكن أن يكون أكثر من %1 حرف.",
"cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك",
"chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.",
"chat-allow-list-user-already-added": "This user is already in your allow list",
"chat-deny-list-user-already-added": "This user is already in your deny list",
"chat-user-blocked": "You have been blocked by this user.",
"chat-disabled": "نظام المحادثة معطل.",
"too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً",
"invalid-chat-message": "الرسالة غير صالحة.",
@@ -167,6 +175,7 @@
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long. Names can't be longer than %1 characters.",
"remote-chat-received-too-long": "You received a chat message from %1, but it was too long and was rejected.",
"already-voting-for-this-post": "لقد شاركت بالتصويت ، ألا تذكر؟",
"reputation-system-disabled": "نظام السمعة معطل",
"downvoting-disabled": "التصويتات السلبية معطلة",
@@ -180,10 +189,20 @@
"not-enough-reputation-min-rep-signature": "You need %1 reputation to add a signature",
"not-enough-reputation-min-rep-profile-picture": "You need %1 reputation to add a profile picture",
"not-enough-reputation-min-rep-cover-picture": "You need %1 reputation to add a cover picture",
"not-enough-reputation-custom-field": "You need %1 reputation for %2",
"custom-user-field-value-too-long": "Custom field value too long, %1",
"custom-user-field-select-value-invalid": "Custom field selected option is invalid, %1",
"custom-user-field-invalid-text": "Custom field text is invalid, %1",
"custom-user-field-invalid-link": "Custom field link is invalid, %1",
"custom-user-field-invalid-number": "Custom field number is invalid, %1",
"custom-user-field-invalid-date": "Custom field date is invalid, %1",
"invalid-custom-user-field": "Invalid custom user field, \"%1\" is already used by NodeBB",
"post-already-flagged": "You have already flagged this post",
"user-already-flagged": "You have already flagged this user",
"post-flagged-too-many-times": "This post has been flagged by others already",
"user-flagged-too-many-times": "This user has been flagged by others already",
"too-many-post-flags-per-day": "You can only flag %1 post(s) per day",
"too-many-user-flags-per-day": "You can only flag %1 user(s) per day",
"cant-flag-privileged": "You are not allowed to flag the profiles or content of privileged users (moderators/global moderators/admins)",
"cant-locate-flag-report": "Cannot locate flag report",
"self-vote": "You cannot vote on your own post",
@@ -212,6 +231,7 @@
"no-topics-selected": "No topics selected!",
"cant-move-to-same-topic": "Can't move post to same topic!",
"cant-move-topic-to-same-category": "Can't move topic to the same category!",
"cant-move-topic-to-from-remote-categories": "You cannot move topics in or out of remote categories; consider cross-posting instead.",
"cannot-block-self": "You cannot block yourself!",
"cannot-block-privileged": "You cannot block administrators or global moderators",
"cannot-block-guest": "Guest are not able to block other users",
@@ -219,10 +239,14 @@
"already-unblocked": "This user is already unblocked",
"no-connection": "There seems to be a problem with your internet connection",
"socket-reconnect-failed": "Unable to reach the server at this time. Click here to try again, or try again later",
"invalid-plugin-id": "Invalid plugin ID",
"plugin-not-whitelisted": "Unable to install plugin &ndash; only plugins whitelisted by the NodeBB Package Manager can be installed via the ACP",
"cannot-toggle-system-plugin": "You cannot toggle the state of a system plugin",
"plugin-installation-via-acp-disabled": "Plugin installation via ACP is disabled",
"plugins-set-in-configuration": "You are not allowed to change plugin state as they are defined at runtime (config.json, environmental variables or terminal arguments), please modify the configuration instead.",
"theme-not-set-in-configuration": "When defining active plugins in configuration, changing themes requires adding the new theme to the list of active plugins before updating it in the ACP",
"topic-event-unrecognized": "Topic event '%1' unrecognized",
"category.handle-taken": "Category handle is already taken, please choose another.",
"cant-set-child-as-parent": "Can't set child as parent category",
"cant-set-self-as-parent": "Can't set self as parent category",
"api.master-token-no-uid": "A master token was received without a corresponding `_uid` in the request body",
@@ -230,10 +254,17 @@
"api.401": "A valid login session was not found. Please log in and try again.",
"api.403": "You are not authorised to make this call",
"api.404": "Invalid API call",
"api.413": "The request payload is too large",
"api.426": "HTTPS is required for requests to the write api, please re-send your request via HTTPS",
"api.429": "You have made too many requests, please try again later",
"api.500": "An unexpected error was encountered while attempting to service your request.",
"api.501": "The route you are trying to call is not implemented yet, please try again tomorrow",
"api.503": "The route you are trying to call is not currently available due to a server configuration",
"api.reauth-required": "The resource you are trying to access requires (re-)authentication."
"api.reauth-required": "The resource you are trying to access requires (re-)authentication.",
"activitypub.not-enabled": "Federation is not enabled on this server",
"activitypub.invalid-id": "Unable to resolve the input id, likely as it is malformed.",
"activitypub.get-failed": "Unable to retrieve the specified resource.",
"activitypub.pubKey-not-found": "Unable to resolve public key, so payload verification cannot take place.",
"activitypub.origin-mismatch": "The received object's origin does not match the sender's origin",
"activitypub.not-implemented": "The request was denied because it or an aspect of it is not implemented by the recipient server"
}

View File

@@ -84,12 +84,18 @@
"modal-reason-offensive": "Offensive",
"modal-reason-other": "Other (specify below)",
"modal-reason-custom": "Reason for reporting this content...",
"modal-notify-remote": "Forward this report to %1",
"modal-submit": "Submit Report",
"modal-submit-success": "Content has been flagged for moderation.",
"modal-confirm-rescind": "Rescind Report?",
"bulk-actions": "Bulk Actions",
"bulk-resolve": "Resolve Flag(s)",
"confirm-purge": "Are you sure you want to permanently delete these flags?",
"purge-cancelled": "Flag Purge Cancelled",
"bulk-purge": "Purge Flag(s)",
"bulk-success": "%1 flags updated",
"flagged-timeago-readable": "Flagged <span class=\"timeago\" title=\"%1\"></span> (%2)",
"flagged-timeago": "Flagged <span class=\"timeago\" title=\"%1\"></span>",
"auto-flagged": "[Auto Flagged] Received %1 downvotes."
}

View File

@@ -24,8 +24,15 @@
"cancel": "Cancel",
"close": "أغلق",
"pagination": "الصفحات",
"pagination.previouspage": "الصفحة السابقة",
"pagination.nextpage": "الصفحة التالية",
"pagination.firstpage": "الصفحة الأولى",
"pagination.lastpage": "الصفحة الأخيرة",
"pagination.out-of": "%1 من %2",
"pagination.enter-index": "Go to post index",
"pagination.go-to-page": "Go to page",
"pagination.page-x": "Page %1",
"header.brand-logo": "Brand Logo",
"header.admin": "مدير النظام",
"header.categories": "الأقسام",
"header.recent": "حديث",
@@ -43,12 +50,11 @@
"header.navigation": "الاستكشاف",
"header.manage": "Manage",
"header.drafts": "Drafts",
"header.world": "World",
"notifications.loading": "تحميل التنبيهات",
"chats.loading": "تحميل الدردشات",
"drafts.loading": "Loading Drafts",
"motd.welcome": "مرحبا بكم في NodeBB، منصة المناقشة المستقبلية.",
"previouspage": "الصفحة السابقة",
"nextpage": "الصفحة التالية",
"alert.success": "نجاح",
"alert.error": "خطأ",
"alert.warning": "Warning",
@@ -62,6 +68,7 @@
"users": "الأعضاء",
"topics": "المواضيع",
"posts": "المشاركات",
"crossposts": "Cross-posts",
"x-posts": "<span class=\"formatted-number\">%1</span> posts",
"x-topics": "<span class=\"formatted-number\">%1</span> topics",
"x-reputation": "<span class=\"formatted-number\">%1</span> reputation",
@@ -76,6 +83,7 @@
"downvoted": "مصوت بالسالب",
"views": "المشاهدات",
"posters": "Posters",
"watching": "Watching",
"reputation": "السمعة",
"lastpost": "Last post",
"firstpost": "First post",
@@ -105,6 +113,7 @@
"dnd": "عدم الإزعاج",
"invisible": "مخفي",
"offline": "غير متصل",
"remote-user": "This user is from outside of this forum",
"email": "عنوان البريد الإلكتروني",
"language": "اللغة",
"guest": "زائر",
@@ -128,6 +137,7 @@
"allowed-file-types": "صيغ الملفات المدعومة هي 1%",
"unsaved-changes": "لديك تغييرات لم تحفظ. هل أنت متأكد من رغبتك بمغادرة الصفحة؟",
"reconnecting-message": "يبدو أن اتصالك لـ %1 قد فقد. رجاءًا أنتظر ثم حاول الإتصال مرة اخرى.",
"reconnected-message": "Reconnected to %1 successfully.",
"play": "تشغيل",
"cookies.message": "هذا الموقع يستخدم ملفات تعريف الارتباط لضمان حصولك على أفضل تجربة على موقعنا.",
"cookies.accept": "فهمت الأمر!",
@@ -135,9 +145,12 @@
"edited": "حُرِر",
"disabled": "معطل",
"select": "تحديد",
"selected": "Selected",
"copied": "Copied",
"user-search-prompt": "Type something here to find users...",
"hidden": "Hidden",
"sort": "Sort",
"actions": "Actions"
"actions": "Actions",
"rss-feed": "RSS Feed",
"skip-to-content": "Skip to content"
}

View File

@@ -1,7 +1,9 @@
{
"group": "Group",
"all-groups": "All groups",
"groups": "المجموعات",
"members": "Members",
"x-members": "%1 member(s)",
"view-group": "معاينة المجموعة",
"owner": "مالك المجموعة",
"new-group": "أنشئ مجموعة جديدة",

View File

@@ -1,8 +1,9 @@
{
"chat.room-id": "Room %1",
"chat.chatting-with": "Chat with",
"chat.placeholder": "Type chat message here, drag & drop images, press enter to send",
"chat.placeholder.mobile": "Type chat message here",
"chat.placeholder": "Type chat message here, drag & drop images",
"chat.placeholder.mobile": "Type chat message",
"chat.placeholder.message-room": "Message #%1",
"chat.scroll-up-alert": "Go to most recent message",
"chat.usernames-and-x-others": "%1 & %2 others",
"chat.chat-with-usernames": "Chat with %1",
@@ -47,6 +48,7 @@
"chat.add-user": "Add User",
"chat.notification-settings": "Notification Settings",
"chat.default-notification-setting": "Default Notification Setting",
"chat.join-leave-messages": "Join/Leave Messages",
"chat.notification-setting-room-default": "Room Default",
"chat.notification-setting-none": "No notifications",
"chat.notification-setting-at-mention-only": "@mention only",
@@ -80,7 +82,7 @@
"composer.hide-preview": "إخفاء المعاينة",
"composer.help": "Help",
"composer.user-said-in": "%1 كتب في %2",
"composer.user-said": "%1 كتب:",
"composer.user-said": "%1 [said](%2):",
"composer.discard": "هل أنت متأكد أنك تريد التخلي عن التغييرات؟",
"composer.submit-and-lock": "Submit and Lock",
"composer.toggle-dropdown": "Toggle Dropdown",
@@ -114,6 +116,8 @@
"composer.discard-all-drafts": "Discard all drafts",
"composer.no-drafts": "You have no drafts",
"composer.discard-draft-confirm": "Do you want to discard this draft?",
"composer.remote-pid-editing": "Editing a remote post",
"composer.remote-pid-content-immutable": "The content of remote posts cannot be edited. However, you are able change the topic title and tags.",
"bootbox.ok": "OK",
"bootbox.cancel": "إلغاء",
"bootbox.confirm": "تأكيد",

View File

@@ -22,23 +22,23 @@
"upvote": "الموافقين",
"awards": "Awards",
"new-flags": "New Flags",
"my-flags": "Flags assigned to me",
"my-flags": "My Flags",
"bans": "الحظر",
"new-message-from": "رسالة جديدة من <strong>%1</strong>",
"new-messages-from": "%1 new messages from <strong>%2</strong>",
"new-message-in": "New message in <strong>%1</strong>",
"new-messages-in": "%1 new messages in <strong>%2</strong>",
"user-posted-in-public-room": "<strong>%1</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %2\"></i>%3</strong>",
"user-posted-in-public-room-dual": "<strong>%1</strong> and <strong>%2</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %3\"></i>%4</strong>",
"user-posted-in-public-room-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %4\"></i>%5</strong>",
"user-posted-in-public-room-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others wrote in <strong class=\"text-nowrap\"><i class=\"fa %4\"></i>%5</strong>",
"upvoted-your-post-in": "<strong>%1</strong> أضاف صوتًا إيجابيا إلى مشاركتك في <strong>%2</strong>.",
"upvoted-your-post-in-dual": "<strong>%1</strong> and <strong>%2</strong> have upvoted your post in <strong>%3</strong>.",
"upvoted-your-post-in-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> have upvoted your post in <strong>%4</strong>.",
"upvoted-your-post-in-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others have upvoted your post in <strong>%4</strong>.",
"user-posted-in-public-room": "<strong>%1</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %2\"></i> %3</strong>",
"user-posted-in-public-room-dual": "<strong>%1</strong> and <strong>%2</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %3\"></i> %4</strong>",
"user-posted-in-public-room-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> wrote in <strong class=\"text-nowrap\"><i class=\"fa %4\"></i> %5</strong>",
"user-posted-in-public-room-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others wrote in <strong class=\"text-nowrap\"><i class=\"fa %4\"></i> %5</strong>",
"upvoted-your-post-in": "<strong>%1</strong> upvoted your post in <strong>%2</strong>",
"upvoted-your-post-in-dual": "<strong>%1</strong> and <strong>%2</strong> upvoted your post in <strong>%3</strong>",
"upvoted-your-post-in-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> upvoted your post in <strong>%4</strong>",
"upvoted-your-post-in-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others upvoted your post in <strong>%4</strong>.",
"moved-your-post": "<strong>%1</strong> has moved your post to <strong>%2</strong>",
"moved-your-topic": "<strong>%1</strong> has moved <strong>%2</strong>",
"user-flagged-post-in": "<strong>%1</strong> أشعَرَ بمشاركة مخلة في <strong>%2</strong>",
"user-flagged-post-in": "<strong>%1</strong> flagged a post in <strong>%2</strong>",
"user-flagged-post-in-dual": "<strong>%1</strong> and <strong>%2</strong> flagged a post in <strong>%3</strong>",
"user-flagged-post-in-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> flagged a post in <strong>%4</strong>",
"user-flagged-post-in-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others flagged a post in <strong>%4</strong>",
@@ -46,17 +46,17 @@
"user-flagged-user-dual": "<strong>%1</strong> and <strong>%2</strong> flagged a user profile (%3)",
"user-flagged-user-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> flagged a user profile (%4)",
"user-flagged-user-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others flagged a user profile (%4)",
"user-posted-to": "<strong>%1</strong> أضاف ردا إلى: <strong>%2</strong>",
"user-posted-to-dual": "<strong>%1</strong> and <strong>%2</strong> have posted replies to: <strong>%3</strong>",
"user-posted-to-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> have posted replies to: <strong>%4</strong>",
"user-posted-to-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others have posted replies to: <strong>%4</strong>",
"user-posted-topic": "<strong>%1</strong> أنشأ موضوعًا جديدًا: <strong>%2</strong>",
"user-edited-post": "<strong>%1</strong> has edited a post in <strong>%2</strong>",
"user-posted-topic-with-tag": "<strong>%1</strong> has posted a new topic with tag <strong>%2</strong>",
"user-posted-topic-with-tag-dual": "<strong>%1</strong> has posted a new topic with tags <strong>%2</strong> and <strong>%3</strong>",
"user-posted-topic-with-tag-triple": "<strong>%1</strong> has posted a new topic with tags <strong>%2</strong>, <strong>%3</strong> and <strong>%4</strong>",
"user-posted-topic-with-tag-multiple": "<strong>%1</strong> has posted a new topic with tags <strong>%2</strong>",
"user-posted-topic-in-category": "<strong>%1</strong> has posted a new topic in <strong>%2</strong>",
"user-posted-to": "<strong>%1</strong> posted a reply in <strong>%2</strong>",
"user-posted-to-dual": "<strong>%1</strong> and <strong>%2</strong> replied in <strong>%3</strong>",
"user-posted-to-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> replied in <strong>%4</strong>",
"user-posted-to-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others replied in <strong>%4</strong>",
"user-posted-topic": "<strong>%1</strong> posted <strong>%2</strong>",
"user-edited-post": "<strong>%1</strong> edited a post in <strong>%2</strong>",
"user-posted-topic-with-tag": "<strong>%1</strong> posted <strong>%2</strong> (tagged %3)",
"user-posted-topic-with-tag-dual": "<strong>%1</strong> posted <strong>%2</strong> (tagged %3 and %4)",
"user-posted-topic-with-tag-triple": "<strong>%1</strong> posted <strong>%2</strong> (tagged %3, %4, and %5)",
"user-posted-topic-with-tag-multiple": "<strong>%1</strong> posted <strong>%2</strong> (tagged %3)",
"user-posted-topic-in-category": "<strong>%1</strong> posted <strong>%2</strong> in <strong>%3</strong>",
"user-started-following-you": "<strong>%1</strong> صار يتابعك.",
"user-started-following-you-dual": "<strong>%1</strong> and <strong>%2</strong> started following you.",
"user-started-following-you-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> started following you.",
@@ -65,13 +65,15 @@
"new-register-multiple": "There are <strong>%1</strong> registration requests awaiting review.",
"flag-assigned-to-you": "تم تخصيص العلامة 1% لك",
"post-awaiting-review": "Post awaiting review",
"topic-awaiting-review": "Topic awaiting review",
"profile-exported": "<strong>%1</strong> profile exported, click to download",
"posts-exported": "<strong>%1</strong> posts exported, click to download",
"uploads-exported": "<strong>%1</strong> uploads exported, click to download",
"users-csv-exported": "Users csv exported, click to download",
"post-queue-accepted": "Your queued post has been accepted. Click here to see your post.",
"post-queue-rejected": "Your queued post has been rejected.",
"post-queue-notify": "Queued post received a notification:<br/>\"%1\"",
"post-queue-rejected-for-reason": "Your queued post has been rejected for the following reason: \"%1\"",
"post-queue-notify": "Queued post received a notification: \"%1\"",
"email-confirmed": "تم التحقق من عنوان البريد الإلكتروني",
"email-confirmed-message": "شكرًا على إثبات صحة عنوان بريدك الإلكتروني. صار حسابك مفعلًا بالكامل.",
"email-confirm-error-message": "حدث خطأ أثناء التحقق من عنوان بريدك الإلكتروني. ربما رمز التفعيل خاطئ أو انتهت صلاحيته.",
@@ -83,19 +85,23 @@
"notificationType-upvote": "عندما يوافقك احدهم على منشورك",
"notificationType-new-topic": "When someone you follow posts a topic",
"notificationType-new-topic-with-tag": "When a topic is posted with a tag you follow",
"notificationType-new-topic-in-category": "When a topic is posted in a category you are watching",
"notificationType-new-reply": "When a new reply is posted in a topic you are watching",
"notificationType-post-edit": "When a post is edited in a topic you are watching",
"notificationType-new-topic-in-category": "When a topic is posted in a watched category",
"notificationType-new-reply": "When a reply is posted in a watched topic",
"notificationType-post-edit": "When a post is edited in a watched topic",
"notificationType-follow": "When someone starts following you",
"notificationType-new-chat": "When you receive a chat message",
"notificationType-new-group-chat": "When you receive a group chat message",
"notificationType-new-public-chat": "When you receive a public group chat message",
"notificationType-group-invite": "When you receive a group invite",
"notificationType-group-leave": "When a user leaves your group",
"notificationType-group-request-membership": "When someone requests to join a group you own",
"notificationType-group-request-membership": "When a user requests to join a group you own",
"notificationType-new-register": "When someone gets added to registration queue",
"notificationType-post-queue": "When a new post is queued",
"notificationType-new-post-flag": "When a post is flagged",
"notificationType-new-user-flag": "When a user is flagged",
"notificationType-new-reward": "When you earn a new reward"
"notificationType-new-reward": "When you earn a new reward",
"activitypub.announce": "<strong>%1</strong> shared your post in <strong>%2</strong> to their followers.",
"activitypub.announce-dual": "<strong>%1</strong> and <strong>%2</strong> shared your post in <strong>%3</strong> to their followers.",
"activitypub.announce-triple": "<strong>%1</strong>, <strong>%2</strong> and <strong>%3</strong> shared your post in <strong>%4</strong> to their followers.",
"activitypub.announce-multiple": "<strong>%1</strong>, <strong>%2</strong> and %3 others shared your post in <strong>%4</strong> to their followers."
}

View File

@@ -36,6 +36,7 @@
"chat": "Chatting with %1",
"flags": "Flags",
"flag-details": "Flag %1 Details",
"world": "World",
"account/edit": "Editing \"%1\"",
"account/edit/password": "Editing password of \"%1\"",
"account/edit/username": "Editing username of \"%1\"",
@@ -54,6 +55,7 @@
"account/settings-of": "Changing settings of %1",
"account/watched": "Topics watched by %1",
"account/ignored": "Topics ignored by %1",
"account/read": "Topics read by %1",
"account/upvoted": "Posts upvoted by %1",
"account/downvoted": "Posts downvoted by %1",
"account/best": "Best posts made by %1",
@@ -61,6 +63,7 @@
"account/blocks": "Blocked users for %1",
"account/uploads": "Uploads by %1",
"account/sessions": "Login Sessions",
"account/shares": "Topics shared by %1",
"confirm": "تم التحقق من عنوان البريد الإلكتروني",
"maintenance.text": "%1 is currently undergoing maintenance.<br/>Please come back another time.",
"maintenance.messageIntro": "بالإضافة إلى ذلك، قام مدبر النظام بترك هذه الرسالة:",

View File

@@ -38,5 +38,6 @@
"remove-selected": "Remove Selected",
"remove-selected-confirm": "Do you want to remove %1 selected posts?",
"bulk-accept-success": "%1 posts accepted",
"bulk-reject-success": "%1 posts rejected"
"bulk-reject-success": "%1 posts rejected",
"links-in-this-post": "Links in this post"
}

View File

@@ -7,5 +7,7 @@
"alltime": "دائمًا",
"no-recent-topics": "لايوجد مواضيع جديدة",
"no-popular-topics": "لا يوجد مواضيع شائعة",
"load-new-posts": "Load new posts"
"load-new-posts": "Load new posts",
"uncategorized.title": "All known topics",
"uncategorized.intro": "This page shows a chronological listing of every topic that this forum has received.<br />The views and opinions expressed in the topics below are not moderated and may not represent the views and opinions of this website."
}

View File

@@ -7,6 +7,7 @@
"in-titles": "In titles",
"in-titles-posts": "In titles and posts",
"in-posts": "In posts",
"in-bookmarks": "In bookmarks",
"in-categories": "In categories",
"in-users": "In users",
"in-tags": "In tags",

View File

@@ -8,5 +8,7 @@
"log-in-with-facebook": "تسجيل الدخول باستخدام فيسبوك",
"continue-with-facebook": "التسجيل باستخدام فيسبوك",
"sign-in-with-linkedin": "Sign in with LinkedIn",
"sign-up-with-linkedin": "Sign up with LinkedIn"
"sign-up-with-linkedin": "Sign up with LinkedIn",
"sign-in-with-wordpress": "Sign in with WordPress",
"sign-up-with-wordpress": "Sign up with WordPress"
}

View File

@@ -3,7 +3,7 @@
"no-tag-topics": "لا يوجد مواضيع بهذه الكلمة الدلالية.",
"no-tags-found": "No tags found",
"tags": "الكلمات الدلالية",
"enter-tags-here": "Enter tags here, between %1 and %2 characters each.",
"enter-tags-here": "Enter tags, %1 - %2 characters.",
"enter-tags-here-short": "أدخل الكلمات الدلالية...",
"no-tags": "لا يوجد كلمات دلالية بعد.",
"select-tags": "Select Tags",

View File

@@ -1,17 +1,25 @@
{
"theme-name": "Harmony Theme",
"skins": "Skins",
"light": "Light",
"dark": "Dark",
"collapse": "Collapse",
"expand": "Expand",
"sidebar-toggle": "Sidebar Toggle",
"login-register-to-search": "Login or register to search.",
"settings.title": "Theme settings",
"settings.enableQuickReply": "Enable quick reply",
"settings.enableBreadcrumbs": "Show breadcrumbs in Category and Topic pages",
"settings.enableBreadcrumbs.why": "Breadcrumbs are visible in most pages for ease-of-navigation. The base design of the category and topic pages has alternative means to link back to parent pages, but the breadcrumb can be toggled off to reduce clutter.",
"settings.centerHeaderElements": "Center header elements",
"settings.mobileTopicTeasers": "Show topic teasers on mobile",
"settings.stickyToolbar": "Sticky toolbar",
"settings.stickyToolbar.help": "The toolbar on topic and category pages will stick to the top of the page",
"settings.autohideBottombar": "Auto hide bottom bar",
"settings.autohideBottombar.help": "The bottom bar on mobile view will be hidden when the page is scrolled down",
"settings.topicSidebarTools": "Topic sidebar tools",
"settings.topicSidebarTools.help": "This option will move the topic tools to the sidebar on desktop",
"settings.autohideBottombar": "Auto hide mobile navigation bar",
"settings.autohideBottombar.help": "The mobile bar will be hidden when the page is scrolled down",
"settings.topMobilebar": "Move the mobile navigation bar to the top",
"settings.openSidebars": "Open sidebars",
"settings.chatModals": "Enable chat modals"
}

View File

@@ -1,4 +1,4 @@
{
"title": "Top",
"no-top-topics": "No top topics"
"title": "أعلى الصفحة",
"no-top-topics": "لا توجد مواضيع أعلى الصفحة"
}

View File

@@ -15,6 +15,7 @@
"replies-to-this-post": "%1 الردود",
"one-reply-to-this-post": "1 رد",
"last-reply-time": "آخر رد",
"reply-options": "Reply options",
"reply-as-topic": "رد بموضوع",
"guest-login-reply": "يجب عليك تسجيل الدخول للرد",
"login-to-view": "سجل الدخول للمشاهدة",
@@ -26,6 +27,7 @@
"restore": "استعادة",
"move": "نقل",
"change-owner": "Change Owner",
"manage-editors": "Manage Editors",
"fork": "فرع",
"link": "رابط",
"share": "نشر",
@@ -34,6 +36,7 @@
"pinned": "مثبت",
"pinned-with-expiry": "مثبت حتى %1",
"scheduled": "Scheduled",
"deleted": "Deleted",
"moved": "منقول",
"moved-from": "Moved from %1",
"copy-code": "Copy Code",
@@ -58,12 +61,16 @@
"user-restored-topic-on": "%1 restored this topic on %2",
"user-moved-topic-from-ago": "%1 moved this topic from %2 %3",
"user-moved-topic-from-on": "%1 moved this topic from %2 on %3",
"user-shared-topic-ago": "%1 shared this topic %2",
"user-shared-topic-on": "%1 shared this topic on %2",
"user-queued-post-ago": "%1 <a href=\"%2\">queued</a> post for approval %3",
"user-queued-post-on": "%1 <a href=\"%2\">queued</a> post for approval on %3",
"user-referenced-topic-ago": "%1 <a href=\"%2\">referenced</a> this topic %3",
"user-referenced-topic-on": "%1 <a href=\"%2\">referenced</a> this topic on %3",
"user-forked-topic-ago": "%1 <a href=\"%2\">forked</a> this topic %3",
"user-forked-topic-on": "%1 <a href=\"%2\">forked</a> this topic on %3",
"user-crossposted-topic-ago": "%1 crossposted this topic to %2 %3",
"user-crossposted-topic-on": "%1 crossposted this topicto %2 on %3",
"bookmark-instructions": "اضغط هنا للعودة لأخر مشاركة مقروءة في الموضوع",
"flag-post": "Flag this post",
"flag-user": "Flag this user",
@@ -85,6 +92,8 @@
"watch.title": "استلم تنبيها بالردود الجديدة في هذا الموضوع",
"unwatch.title": "ألغ مراقبة هذا الموضوع",
"share-this-post": "انشر هذا الموضوع",
"share-mail-subject": "Check out this post on \"%1\"",
"share-mail-body": "I thought you might be interested in this post: %1",
"watching": "مراقبة",
"not-watching": "غير مراقب",
"ignoring": "تجاهل",
@@ -98,9 +107,11 @@
"thread-tools.lock": "أقفل الموضوع",
"thread-tools.unlock": "إلغاء إقفال الموضوع",
"thread-tools.move": "نقل الموضوع",
"thread-tools.crosspost": "Crosspost Topic",
"thread-tools.move-posts": "Move Posts",
"thread-tools.move-all": "نقل الكل",
"thread-tools.change-owner": "Change Owner",
"thread-tools.manage-editors": "Manage Editors",
"thread-tools.select-category": "Select Category",
"thread-tools.fork": "إنشاء فرع الموضوع",
"thread-tools.tag": "Tag Topic",
@@ -126,14 +137,17 @@
"pin-modal-help": "You can optionally set an expiration date for the pinned topic(s) here. Alternatively, you can leave this field blank to have the topic stay pinned until it is manually unpinned.",
"load-categories": "تحميل الفئات",
"confirm-move": "انقل",
"confirm-crosspost": "Cross-post",
"confirm-fork": "فرع",
"bookmark": "Bookmark",
"bookmarks": "Bookmarks",
"bookmarks.has-no-bookmarks": "You haven't bookmarked any posts yet.",
"copy-permalink": "Copy Permalink",
"go-to-original": "View Original Post",
"loading-more-posts": "تحميل المزيد من المشاركات",
"move-topic": "نقل الموضوع",
"move-topics": "نقل المواضيع",
"crosspost-topic": "Cross-post Topic",
"move-post": "نقل المشاركة",
"post-moved": "تم نقل المشاركة",
"fork-topic": "فرع الموضوع",
@@ -155,6 +169,10 @@
"move-posts-instruction": "Click the posts you want to move then enter a topic ID or go to the target topic",
"move-topic-instruction": "Select the target category and then click move",
"change-owner-instruction": "Click the posts you want to assign to another user",
"manage-editors-instruction": "Manage the users who can edit this post below.",
"crossposts.instructions": "Select one or more categories to cross-post to. Topic(s) will be accessible from the original category and all cross-posted categories.",
"crossposts.listing": "This topic has been cross-posted to the following local categories:",
"crossposts.none": "This topic has not been cross-posted to any additional categories",
"composer.title-placeholder": "أدخل عنوان موضوعك هنا...",
"composer.handle-placeholder": "Enter your name/handle here",
"composer.hide": "Hide",
@@ -166,6 +184,7 @@
"composer.replying-to": "الرد على %1",
"composer.new-topic": "موضوع جديد",
"composer.editing-in": "Editing post in %1",
"composer.untitled-topic": "Untitled Topic",
"composer.uploading": "جاري الرفع",
"composer.thumb-url-label": "ألصق رابط الصورة المصغرة للموضوع",
"composer.thumb-title": "إضافة صورة مصغرة للموضوع",
@@ -180,6 +199,8 @@
"sort-by": "ترتيب حسب",
"oldest-to-newest": "من الأقدم إلى الأحدث",
"newest-to-oldest": "من الأحدث إلى الأقدم",
"recently-replied": "Recently Replied",
"recently-created": "Recently Created",
"most-votes": "Most Votes",
"most-posts": "Most Posts",
"most-views": "Most Views",
@@ -204,7 +225,19 @@
"last-post": "Last post",
"go-to-my-next-post": "Go to my next post",
"no-more-next-post": "You don't have more posts in this topic",
"open-composer": "Open composer",
"post-quick-reply": "Quick reply",
"post-quick-create": "Quick post",
"navigator.index": "Post %1 of %2",
"navigator.unread": "%1 unread"
"navigator.unread": "%1 unread",
"upvote-post": "Upvote post",
"downvote-post": "Downvote post",
"post-tools": "Post tools",
"unread-posts-link": "Unread posts link",
"thumb-image": "Topic thumbnail image",
"announcers": "Shares",
"announcers-x": "Shares (%1)",
"guest-cta.title": "Hello! It looks like you're interested in this conversation, but you don't have an account yet.",
"guest-cta.message": "Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.",
"guest-cta.closing": "With your input, this post could be even better 💗"
}

View File

@@ -3,6 +3,7 @@
"no-unread-topics": "ليس هناك أي موضوع غير مقروء",
"load-more": "حمل المزيد",
"mark-as-read": "حدد غير مقروء",
"mark-as-unread": "Mark as Unread",
"selected": "المحددة",
"all": "الكل",
"all-categories": "كل الفئات",

View File

@@ -1,6 +1,9 @@
{
"user-menu": "قائمة المستخدم",
"banned": "محظور",
"unbanned": "Unbanned",
"muted": "كتم",
"unmuted": "Unmuted",
"offline": "غير متصل",
"deleted": "محذوف",
"username": "إسم المستخدم",
@@ -13,7 +16,7 @@
"ban-account": "حظر الحساب",
"ban-account-confirm": "هل تريد حقاً حظر هاذا العضو؟",
"unban-account": "إزالة حظر الحساب",
"mute-account": "Mute Account",
"mute-account": "كتم الحساب",
"unmute-account": "Unmute Account",
"delete-account": "حذف الحساب",
"delete-account-as-admin": "Delete <strong>Account</strong>",
@@ -36,13 +39,15 @@
"reputation": "السمعة",
"bookmarks": "المفضلات",
"watched-categories": "الأقسام المُتابعة",
"watched-tags": "Watched tags",
"watched-tags": "الوسوم المُتابعة",
"change-all": "غير الكل",
"watched": "متابع",
"ignored": "تم تجاهله",
"read": "Read",
"default-category-watch-state": "حالة مشاهدة اﻻقسام الافتراضية",
"followers": "المتابعون",
"following": "يتابع",
"shares": "Shares",
"blocks": "الكتل",
"blocked-users": "Blocked users",
"block-toggle": "تبديل الكتلة",
@@ -54,9 +59,12 @@
"chat": "محادثة",
"chat-with": "متابعة الدردشة مع %1",
"new-chat-with": "بدء دردشة جديدة مع %1",
"view-remote": "View Original",
"flag-profile": "ضع علامة على الملف الشخصي",
"profile-flagged": "Already flagged",
"follow": "تابع",
"unfollow": "إلغاء المتابعة",
"cancel-follow": "Cancel follow request",
"more": "المزيد",
"profile-update-success": "تم تحديث الملف الشخصي بنجاح",
"change-picture": "تغيير الصورة",
@@ -71,10 +79,11 @@
"upload-new-picture": "رفع صورة جديدة",
"upload-new-picture-from-url": "رفع صورة جديدة من رابط",
"current-password": "كلمة السر الحالية",
"new-password": "New Password",
"new-password": "كلمة السر الجديدة",
"change-password": "تغيير كلمة السر",
"change-password-error": "كلمة سر غير صحيحة",
"change-password-error-wrong-current": "كلمة السر الحالية ليست صحيحة",
"change-password-error-same-password": "كلمة السر الجديدة مطابقة لكلمة السر الحالية, فضلاً قم بإدخال كلمة سر جديدة.",
"change-password-error-match": "كلمة السر غير مطابقة لتأكيد كلمة السر",
"change-password-error-privileges": "ليس لديك الصلاحيات الكافية لتغيير كلمة السر هذه.",
"change-password-success": "تم تحديث كلمة السر خاصتك.",
@@ -96,6 +105,10 @@
"show-email": "أظهر بريدي الإلكتروني",
"show-fullname": "أظهر اسمي الكامل",
"restrict-chats": "لاتسمح بورود محادثات إلا من طرف المستخدمين الذين أتابعهم.",
"disable-incoming-chats": "Disable incoming chat messages <a class=\"text-reset\" data-bs-toggle=\"tooltip\" href=\"#\" title=\"Admins and moderators can still send you messages\"><i class=\"fa-solid fa-circle-info\"></i></a>",
"chat-allow-list": "Allow chat messages from the following users",
"chat-deny-list": "Deny chat messages from the following users",
"chat-list-add-user": "Add user",
"digest-label": "اشترك في النشرة الدورية",
"digest-description": "استلام اشعارات بآخر مستجدات هذا القسم (التنبيهات والمواضيع الجديدة) عبر البريد الإلكتروني وفقا لجدول زمني محدد.",
"digest-off": "غير مفعل",
@@ -110,15 +123,17 @@
"has-no-topics": "هذا المستخدم لم يكتب أي موضوع حتى الآن.",
"has-no-watched-topics": "هذا المستخدم لم يقم بمراقبة اية مواضيع حتى الآن.",
"has-no-ignored-topics": "هذا المستخدم لم يقم بتجاهل اية مواضيع حتى الآن.",
"has-no-read-topics": "هذا المستخدم لم يقم بقراءة أي مشاركة حتى الاّن.",
"has-no-upvoted-posts": "هذا المستخدم لم يقم بالتصويت للأعلى لأي مشاركة حتى الآن.",
"has-no-downvoted-posts": "هذا المستخدم لم يقم بالتصويت للأسفل لأي مشاركة حتى الآن.",
"has-no-controversial-posts": "هذا المستخدم ﻻيمتلك تصويت للأسفل لأي مشاركة حتى الآن.",
"has-no-blocks": "أنت لم تحظر اي مستخدم",
"has-no-shares": "This user has not shared any topics.",
"email-hidden": "البريد الإلكتروني مخفي",
"hidden": "مخفي",
"paginate-description": "عرض المواضيع والردود موزعة على صفحات عوضاً عن التمرير اللانهائي.",
"topics-per-page": "المواضيع في كل صفحة",
"posts-per-page": "الردود في كل صفحة",
"topics-per-page": "Topics per page",
"posts-per-page": "Posts per page",
"category-topic-sort": "Category topic sort",
"topic-post-sort": "Topic post sort",
"max-items-per-page": "أقصى %1",
@@ -131,7 +146,11 @@
"upvote-notif-freq.threshold": "On 1, 5, 10, 25, 50, 100, 150, 200...",
"upvote-notif-freq.logarithmic": "On 10, 100, 1000...",
"upvote-notif-freq.disabled": "معطل",
"notification-type-web": "Web",
"notification-type-email": "Email",
"browsing": "خيارات التصفح",
"unread.cutoff": "Unread cutoff (Maximum %1 days)",
"unread.cutoff-help": "Topics will be marked read if they have not been updated within this number of days.",
"open-links-in-new-tab": "فتح الروابط الخارجية في نافدة جديدة",
"enable-topic-searching": "تفعيل خاصية البحث داخل المواضيع",
"topic-search-help": "إذا قمت بتفعيل ميزة البحث في-الموضوع، سيتم تجاوز الخيار الافتراضي للمتصفح مما يؤدي للبحث بكامل الموضوع بدلا عن البحث في الجزء الظاهر في الشاشة.",
@@ -141,6 +160,10 @@
"follow-topics-you-create": "متابعة المواضيع التي تقوم بإنشائها",
"grouptitle": "عنوان المجموعة",
"group-order-help": "Select a group and use the arrows to order titles",
"show-group-title": "Show group title",
"hide-group-title": "Hide group title",
"order-group-up": "Order group up",
"order-group-down": "Order group down",
"no-group-title": "لا يوجد عنوان للمجموعة",
"select-skin": "إختر سمة",
"default": "Default (%1)",
@@ -156,12 +179,18 @@
"sso.dissociate": "فصل",
"sso.dissociate-confirm-title": "تأكيد الفصل",
"sso.dissociate-confirm": "هل تريد بالتأكيد فصل حسابك عن %1؟",
"info.invited-by": "Invited by",
"info.latest-flags": "أحدث العلامات",
"info.profile": "Profile",
"info.post": "Post",
"info.view-flag": "View flag",
"info.reported-by": "Reported by:",
"info.no-flags": "لم يتم العثور على مشاركات معلمة",
"info.ban-history": "سجل الحظر الأحدث",
"info.no-ban-history": "هذا المستخدم لم يتم حظره مطلقا",
"info.banned-until": "محظور حتى %1",
"info.banned-expiry": "Expiry",
"info.ban-expired": "Ban expired",
"info.banned-permanently": "محظور بشكل دائم",
"info.banned-reason-label": "سبب",
"info.banned-no-reason": "لم يتم إعطاء سبب.",

View File

@@ -1,5 +1,6 @@
{
"all-users": "All Users",
"all-users": "كل المستخدمين",
"followed-users": "Followed Users",
"latest-users": "أحدث الأعضاء",
"top-posters": "اﻷكثر مشاركة",
"most-reputation": "أعلى سمعة",

View File

@@ -0,0 +1,29 @@
{
"name": "World",
"latest": "Latest",
"latest-local": "Latest (Local)",
"latest-all": "Latest (All)",
"popular-day": "Popular (Day)",
"popular-week": "Popular (Week)",
"popular-month": "Popular (Month)",
"popular-year": "Popular (Year)",
"popular-alltime": "Popular (All Time)",
"recent": "All",
"help": "Help",
"help.title": "What is this page?",
"help.intro": "Welcome to your corner of the fediverse.",
"help.fediverse": "The \"fediverse\" is a network of interconnected applications and websites that all talk to one another and whose users can see each other. This forum is federated, and can interact with that social web (or \"fediverse\"). This page is your corner of the fediverse. It consists solely of topics created by — and shared from — users <strong>you</strong> follow.",
"help.build": "There might not be a lot of topics here to start; that's normal. You will start to see more content here over time when you start following other users.",
"help.federating": "Likewise, if users from outside of this forum start following <em>you</em>, then your posts will start appearing on those apps and websites as well.",
"help.next-generation": "This is the next generation of social media, start contributing today!",
"onboard.title": "A world of content at your fingertips&hellip;",
"onboard.what": "Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.",
"onboard.why": "While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.",
"onboard.how": "Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.",
"category-search": "Find a category...",
"see-more": "See more",
"see-less": "See less"
}

View File

@@ -0,0 +1,3 @@
# The files here are not meant to be edited directly
Please see the &rarr; [Internalization README](../README.md).

View File

@@ -0,0 +1,18 @@
{
"alert.confirm-rebuild-and-restart": "NodeBB-ni yenidən qurmaq və yenidən başlatmaq istədiyinizə əminsiniz?",
"alert.confirm-restart": "NodeBB-ni yenidən başlatmaq istədiyinizə əminsiniz?",
"acp-title": "%1 | NodeBB İdarəetmə Paneli",
"settings-header-contents": "Məzmun",
"changes-saved": "Dəyişikliklər yadda saxlandı",
"changes-saved-message": "NodeBB konfiqurasiyasına etdiyiniz dəyişikliklər yadda saxlanıldı.",
"changes-not-saved": "Dəyişikliklər saxlanmadı",
"changes-not-saved-message": "NodeBB dəyişikliklərinizi yadda saxlayarkən problemlə qarşılaşdı. (% 1)",
"save-changes": "Dəyişiklikləri yadda saxlayın",
"min": "Min:",
"max": "Maks:",
"view": "Bax",
"edit": "Redaktə et",
"add": "Əlavə et",
"select-icon": "İkonu seç"
}

View File

@@ -0,0 +1,6 @@
{
"cache": "Keş",
"percent-full": "%1% dolu",
"post-cache-size": "Yazıların keş ölçüsü",
"items-in-cache": "Keşdəki elementlər"
}

View File

@@ -0,0 +1,52 @@
{
"x-b": "%1 b",
"x-mb": "%1 mb",
"x-gb": "%1 gb",
"uptime-seconds": "Saniyələrlə işləmə müddəti",
"uptime-days": "Günlərlə işləmə müddəti",
"mongo": "MongoDB",
"mongo.version": "MongoDB versiyası",
"mongo.storage-engine": "Saxlama Motoru",
"mongo.collections": "Kolleksiyalar",
"mongo.objects": "Obyektlər",
"mongo.avg-object-size": "Orta. Obyekt Ölçüsü",
"mongo.data-size": "Data ölçüsü",
"mongo.storage-size": "Yaddaş ölçüsü",
"mongo.index-size": "İndeks ölçüsü",
"mongo.file-size": "Fayl ölçüsü",
"mongo.resident-memory": "Rezident yaddaş",
"mongo.virtual-memory": "Virtual yaddaş",
"mongo.mapped-memory": "Xəritəli yaddaş",
"mongo.bytes-in": "Daxil olan Baytlar",
"mongo.bytes-out": "Xaric olan Baytlar",
"mongo.num-requests": "Sorğuların sayı",
"mongo.raw-info": "MongoDB raw məlumatı",
"mongo.unauthorized": "NodeBB müvafiq statistika üçün MongoDB verilənlər bazasını sorğulaya bilmədi. NodeBB tərəfindən istifadə edilən istifadəçinin &quot;clusterMonitor&quot; &quot;admin&quot; üçün rol; verilənlər bazası.",
"redis": "Redis",
"redis.version": "Redis versiyası",
"redis.keys": "Açarlar",
"redis.expires": "Vaxtı bitir",
"redis.avg-ttl": "Ortalama TTL",
"redis.connected-clients": "Qoşulmuş müştərilər",
"redis.connected-slaves": "Əlaqədar qullar",
"redis.blocked-clients": "Blok edilmiş müştərilər",
"redis.used-memory": "İstifadə edilən yaddaş",
"redis.memory-frag-ratio": "Yaddaşın fraqmentasiya nisbəti",
"redis.total-connections-recieved": "Alınan ümumi əlaqələr",
"redis.total-commands-processed": "Cəmi əmrlər işlənib",
"redis.iops": "Dərhal əməliyyatlar. Saniyədə",
"redis.iinput": "Saniyədə ani daxiletmə",
"redis.ioutput": "Saniyədə ani çıxış",
"redis.total-input": "Ümumi giriş",
"redis.total-output": "Ümumi çıxış",
"redis.keyspace-hits": "Klaviatura xitləri",
"redis.keyspace-misses": "Klaviatura boşluqları boşdur",
"redis.raw-info": "Redis raw məlumatı",
"postgres": "Postgres",
"postgres.version": "PostgreSQL sersiyası",
"postgres.raw-info": "Postgres raw məlumatı"
}

View File

@@ -0,0 +1,15 @@
{
"errors": "Xətalar",
"figure-x": "Şəkil %1",
"error-events-per-day": "Gündə <code>%1</code> hadisə",
"error.404": "404 tapılmadı",
"error.503": "503 xidmət əlçatan deyil",
"manage-error-log": "Xəta qeydlərini idarə et",
"export-error-log": "İxrac xətası jurnalı (CSV)",
"clear-error-log": "Xəta loqlarını sil",
"route": "Marşrut",
"count": "Say",
"no-routes-not-found": "Yaşasın! 404 səhv yoxdur!",
"clear404-confirm": "404 xəta qeydlərini təmizləmək istədiyinizə əminsiniz?",
"clear404-success": "\"404 Tapılmadı\" xətaları silindi"
}

View File

@@ -0,0 +1,17 @@
{
"events": "Hadisələr",
"no-events": "Hadisə yoxdur",
"control-panel": "Hadisələrin İdarəetmə Paneli",
"delete-events": "Tədbirləri sil",
"confirm-delete-all-events": "Daxil edilmiş bütün hadisələri silmək istədiyinizə əminsiniz?",
"filters": "Filtrlər",
"filters-apply": "Filtrləri tətbiq et",
"filter-type": "Hadisə növü",
"filter-start": "Başlama tarixi",
"filter-end": "Bitmə tarixi",
"filter-user": "İstifadəçiyə görə filtrlə",
"filter-user.placeholder": "Filtr etmək üçün istifadəçi adını daxil edin...",
"filter-group": "Qrup üzrə filtrlə",
"filter-group.placeholder": "Filtr etmək üçün qrup adını yazın...",
"filter-per-page": "Hər səhifə üçün"
}

View File

@@ -0,0 +1,9 @@
{
"jobs": "Jobs",
"job-name": "Job Name",
"schedule": "Schedule",
"next-run": "Next Run",
"last-duration": "Last Duration",
"running": "Running",
"active": "Active"
}

View File

@@ -0,0 +1,7 @@
{
"logs": "Loqlar",
"control-panel": "Loqların idarəetmə paneli",
"reload": "Loqları yenidən yükləyin",
"clear": "Loqları təmizləyin",
"clear-success": "Loqlar təmizləndi!"
}

View File

@@ -0,0 +1,20 @@
{
"customise": "Fərdiləşdirmək",
"custom-css": "Fərdi CSS/SASS",
"custom-css.description": "Buraya bütün digər üslublardan sonra tətbiq olunacaq öz CSS/SASS bəyannamələrinizi daxil edin.",
"custom-css.enable": "Fərdi CSS/SASS-ı aktiv edin",
"custom-js": "Fərdi JavaScript",
"custom-js.description": "Burada öz javascriptinizi daxil edin. Səhifə tamamilə yükləndikdən sonra yerinə yetiriləcək.",
"custom-js.enable": "Fərdi Javascript-i aktiv edin",
"custom-header": "Fərdi başlıq",
"custom-header.description": "Bura xüsusi HTML daxil edin (məs. Meta Teqlər və s.), o <code>&lt;head&gt;</code> forumunuzun işarələmə bölməsi. <a href=\"#custom-js\" data-toggle=\"tab\">Xüsusi Javascript</a> nişanı mövcud olduğu üçün skript teqlərinə icazə verilir, lakin bunlar qadağandır.",
"custom-header.enable": "Fərdi Başlığı aktivləşdir",
"custom-css.livereload": "Canlı Yenidən Yükləməni aktiv et",
"custom-css.livereload.description": "Hesabınızın altındakı hər bir cihazdakı bütün sessiyaları yadda saxla düyməsini kliklədiyiniz zaman yeniləməyə məcbur etmək üçün bunu aktiv edin",
"bsvariables": "_variables.scss",
"bsvariables.description": "Bootstrap dəyişənlərini burada ləğv edin. Siz həmçinin <a href=\"https://bootstrap.build/app\">bootstrap.build</a> kimi alətdən istifadə edib nəticəni bura yapışdıra bilərsiniz. <br/>Dəyişikliklər yenidən qurmaq və yenidən işə salmaq tələb olunur.",
"bsvariables.enable": "_variables.scss-i aktivləşdirin"
}

View File

@@ -0,0 +1,18 @@
{
"skins": "Örtüklər",
"bootswatch-skins": "Bootswatch örtükləri",
"custom-skins": "Fərdi örtüklər",
"add-skin": "Örtük əlavə et",
"save-custom-skins": "Fərdi örtüyü saxla",
"save-custom-skins-success": "Fərdi örtüklər uğurla yadda saxlanıldı",
"custom-skin-name": "Fərdi örtük adı",
"custom-skin-variables": "Fərdi örtük dəyişənləri",
"loading": "Örtüklər yüklənir...",
"homepage": "Əsas səhifə",
"select-skin": "Örtüyü seçin",
"revert-skin": "Örtüyü bərpa edin",
"current-skin": "Cari örtük",
"skin-updated": "Örtük yeniləndi",
"applied-success": "%1 örtük uğurla tətbiq olundu",
"revert-success": "Örtük əsas rənglərə qayıtdı"
}

View File

@@ -0,0 +1,13 @@
{
"themes": "Mövzular",
"checking-for-installed": "Quraşdırılmış mövzular yoxlanılır...",
"homepage": "Əsas səhifə",
"select-theme": "Mövzu seç",
"revert-theme": "Mövzunu geri qaytar",
"current-theme": "Cari mövzu",
"no-themes": "Quraşdırılmış mövzu tapılmadı",
"revert-confirm": "Defolt NodeBB mövzusunu bərpa etmək istədiyinizə əminsiniz?",
"theme-changed": "Mövzu dəyişdirildi",
"revert-success": "Siz NodeBB-ni müvəffəqiyyətlə defolt mövzuya qaytardınız.",
"restart-to-activate": "Zəhmət olmasa, bu mövzunu tam aktivləşdirmək üçün NodeBB-ni yenidən qurun və başladın."
}

View File

@@ -0,0 +1,105 @@
{
"forum-traffic": "Forum trafiki",
"page-views": "Səhifə baxışları",
"unique-visitors": "Unikal ziyarətçilər",
"logins": "Daxil olmalar",
"new-users": "Yeni istifadəçilər",
"posts": "Yazılar",
"topics": "Mövzular",
"remote-posts": "Remote Posts",
"remote-topics": "Remote Topics",
"messages": "Messages",
"page-views-seven": "Son 7 Gün",
"page-views-thirty": "Son 30 Gün",
"page-views-last-day": "Son 24 saat",
"page-views-custom": "Custom Range",
"page-views-custom-start": "Aralığın başlanğıcı",
"page-views-custom-end": "Aralığın sonu",
"page-views-custom-help": "Baxmaq istədiyiniz səhifə baxışlarının tarix diapazonunu daxil edin. Əgər tarix seçici mövcud deyilsə, qəbul edilən format <code>YYYY-AA-GG</code>-dir",
"page-views-custom-error": "Lütfən, <code>YYYY-AA-GG</code> formatında etibarlı tarix diapazonu daxil edin",
"stats.yesterday": "Dünən",
"stats.today": "Bu gün",
"stats.last-week": "Keçən həftə",
"stats.this-week": "Bu həftə",
"stats.last-month": "Keçən ay",
"stats.this-month": "Bu ay",
"stats.all": "Bütün zamanlar",
"updates": "Yeniləmələr",
"running-version": "Siz NodeBB v%1 işlədirsiniz.",
"keep-updated": "Həmişə NodeBB-nin ən son təhlükəsizlik yamaları və xətaların düzəldilməsi üçün yeni olduğundan əmin olun.",
"up-to-date": "Siz son <strong>variantasınız</strong> <i class=\"fa fa-check\"></i>",
"upgrade-available": "Yeni versiya (v%1) buraxılmışdır. NodeBB-ni <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">təkmilləşdirməyi nəzərdən keçirin</a>.",
"prerelease-upgrade-available": "Bu, NodeBB proqramının köhnəlmiş buraxılışdan əvvəlki versiyasıdır. Yeni versiya (v%1) buraxılmışdır. NodeBB-ni <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">təkmilləşdirməyi nəzərdən keçirin</a>.",
"prerelease-warning": "Bu, NodeBB-nin <strong>buraxılışdan öncəki</strong> versiyasıdır. Gözlənilməz səhvlər baş verə bilər. <i class=\"fa fa-exclamation-triangle\"></i>",
"fallback-emailer-not-found": "Yekun e-poçt göndərişi tapılmadı!",
"running-in-development": "Forum inkişaf rejimində işləyir. Forum potensial boşluqlar üçün açıq ola bilər; lütfən sistem inzibatçısı ilə əlaqə saxlayın",
"latest-lookup-failed": "NodeBB-nin ən son mövcud versiyasını axtarmaq alınmadı",
"notices": "Bildirişlər",
"restart-not-required": "Yenidən başlama tələb olunmur",
"restart-required": "Yenidən başlatma tələb olunur",
"search-plugin-installed": "Axtarış plagini quraşdırılıb",
"search-plugin-not-installed": "Axtarış plagini quraşdırılmayıb",
"search-plugin-tooltip": "Axtarış funksiyasını aktivləşdirmək üçün plagin səhifəsindən axtarış plaqini quraşdır",
"control-panel": "Sistem nəzarəti",
"rebuild-and-restart": "Yenidən qur &amp; başlat",
"restart": "Yenidən başlat",
"restart-warning": "NodeBB-nin yenidən qurulması və ya yenidən işə salınması bütün mövcud əlaqələri bir neçə saniyəyə kəsəcək.",
"restart-disabled": "NodeBB-nin yenidən qurulması və yenidən işə salınması qeyri-aktiv edilib, çünki siz onu uyğun demon vasitəsilə idarə etmirsiniz.",
"maintenance-mode": "Texniki baxım rejimi",
"maintenance-mode-title": "NodeBB üçün texniki xidmət rejimini qurmaq üçün bura kliklə",
"dark-mode": "Qaranlıq rejimi",
"realtime-chart-updates": "Real vaxt qrafiki yeniləmələri",
"active-users": "Aktiv istifadəçilər",
"active-users.users": "İstifadəçilər",
"active-users.guests": "Qonaqlar",
"active-users.total": "Cəmi",
"active-users.connections": "Bağlantılar",
"guest-registered-users": "Qonaq və Qeydiyyatdan keçmiş istifadəçilər",
"guest": "Qonaq",
"registered": "Qeydiyyatdan keçib",
"user-presence": "İstifadəçi mövcudluğu",
"on-categories": "Kateqoriyalar siyahısında",
"reading-posts": "Yazıların oxunması",
"browsing-topics": "Mövzulara baxış",
"recent": "Ən yeni",
"unread": "Oxunmamış",
"high-presence-topics": "Yüksək mövzular",
"popular-searches": "Populyar axtarışlar",
"graphs.page-views": "Səhifə baxışları",
"graphs.page-views-registered": "Səhifə Baxışları qeydə alınıb",
"graphs.page-views-guest": "Səhifə baxışı qonaq",
"graphs.page-views-bot": "Səhifə baxış botu",
"graphs.page-views-ap": "ActivityPub Page Views",
"graphs.unique-visitors": "Unikal ziyarətçilər",
"graphs.registered-users": "Qeydiyyatdan keçmiş istifadəçilər",
"graphs.guest-users": "Qonaqlar",
"last-restarted-by": "Sonuncu dəfə yenidən başlat",
"no-users-browsing": "Axtarış edən heç bir istifadəçi yoxdur",
"back-to-dashboard": "İdarə Panelinə qayıt",
"details.no-users": "Seçilmiş müddət ərzində heç bir istifadəçi qoşulmayıb",
"details.no-topics": "Seçilmiş vaxt çərçivəsində heç bir mövzu yerləşdirilməyib",
"details.no-searches": "Seçilmiş müddət ərzində heç bir axtarış aparılmayıb",
"details.no-logins": "Seçilmiş müddət ərzində heç bir giriş qeydə alınmayıb",
"details.logins-static": "NodeBB yalnız %1 gün ərzində sessiya məlumatlarını saxlayır və buna görə də aşağıdakı cədvəldə yalnız ən son aktiv sessiyalar göstəriləcək",
"details.logins-login-time": "Daxil olma vaxtı",
"start": "Başlanğıc",
"end": "Son",
"filter": "Filtr",
"view-as-json": "JSON kimi bax",
"expand-analytics": "Analitikanı genişləndir",
"clear-search-history": "Axtarış tarixçəsini təmizlə",
"clear-search-history-confirm": "Bütün axtarış tarixçəsini təmizləmək istədiyinizə əminsiniz?",
"search-term": "Qayda",
"search-count": "Say",
"view-all": "Hamısına bax"
}

View File

@@ -0,0 +1,26 @@
{
"you-are-on": "Siz <strong>%1:%2</strong>-dəsiniz",
"ip": "IP <strong>%1</strong>",
"nodes-responded": "%1 nod %2ms ərzində cavab verdi!",
"host": "host",
"primary": "əsas / iş",
"pid": "pid",
"nodejs": "nodejs",
"online": "onlayn",
"git": "git",
"process-memory": "rss/heap used",
"system-memory": "sistem yaddaşı",
"used-memory-process": "Proseslər yaddaşdan istifadə edir",
"used-memory-os": "İstifadə olunmuş sistem yaddaşı",
"total-memory-os": "Ümumi sistem yaddaşı",
"load": "sistem yükləməsi",
"cpu-usage": "CPU istifadəsi",
"uptime": "işləmə müddəti",
"registered": "Qeydiyyatdan keçib",
"sockets": "Sockets",
"connection-count": "Bağlantı sayı",
"guests": "Qonaqlar",
"info": "Məlumat"
}

View File

@@ -0,0 +1,13 @@
{
"logger": "Logger",
"logger-settings": "Logger ayarları",
"description": "Yoxlama qutularını aktivləşdirməklə, siz terminalınıza qeydlər alacaqsınız. Bir yol göstərsəniz, qeydlər bunun əvəzinə faylda saxlanacaq. HTTP girişi, forumunuzda insanların kim, nə vaxt və nəyə daxil olması ilə bağlı statistika toplamaq üçün faydalıdır. HTTP sorğularını qeyd etməklə yanaşı, biz socket.io hadisələrini də qeyd edə bilərik. Socket.io girişi, redis-cli monitoru ilə birlikdə NodeBB-nin daxili hissələrini öyrənmək üçün çox faydalı ola bilər.",
"explanation": "Aktivləşdirmək və ya söndürmək üçün sadəcə giriş parametrlərini çek/ançek edin. Yenidən başlatmağa ehtiyac yoxdur.",
"enable-http": "HTTP loqunu aktiv edin",
"enable-socket": "Socket.io hadisə qeydini aktiv edin",
"file-path": "Loq faylına yol",
"file-path-placeholder": "/path/to/log/file.log ::: terminalınıza loqlamaq üçün boş saxlayın",
"control-panel": "Logger idarəetmə paneli",
"update-settings": "Logger parametrlərini yenilə"
}

View File

@@ -0,0 +1,58 @@
{
"plugins": "Plaginlər",
"trending": "Trend",
"installed": "Quraşdırılıb",
"active": "Aktiv",
"inactive": "Qeyri-aktiv",
"out-of-date": "Vaxtı keçmiş",
"none-found": "Heç bir plagin tapılmadı.",
"none-active": "Aktiv plaginlər yoxdur",
"find-plugins": "Pluginləri tapın",
"plugin-search": "Plugin axtarışı",
"plugin-search-placeholder": "Plugin axtarın...",
"submit-anonymous-usage": "Anonim plagin istifadə məlumatlarını təqdim edin.",
"reorder-plugins": "Pluginləri təkrar sifariş et",
"order-active": "Aktiv plaginlər sifariş et",
"dev-interested": "NodeBB üçün plaginlər yazmaqla maraqlanırsınız?",
"docs-info": "Plugin müəllifliyi ilə bağlı tam sənədləri <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\">NodeBB Sənədlər Portalında</a> tapa bilərsiniz.",
"order.description": "Bəzi plaginlər digər plaginlərdən əvvəl/sonra işə salındıqda ideal şəkildə işləyir.",
"order.explanation": "Pluginlər burada göstərilən ardıcıllıqla yuxarıdan aşağı yüklənir",
"plugin-item.themes": "Mövzular",
"plugin-item.deactivate": "Deaktiv et",
"plugin-item.activate": "Aktiv et",
"plugin-item.install": "Quraşdır",
"plugin-item.uninstall": "Sil",
"plugin-item.settings": "Parametrlər",
"plugin-item.installed": "Quraşdırılıb",
"plugin-item.latest": "Ən son",
"plugin-item.upgrade": "Təkmilləşdir",
"plugin-item.more-info": "Ətraflı məlumat üçün:",
"plugin-item.unknown": "Naməlum",
"plugin-item.unknown-explanation": "Bu plaqinin vəziyyəti yanlış konfiqurasiya xətası səbəbindən müəyyən edilə bilmədi.",
"plugin-item.compatible": "Bu plagin NodeBB %1-də işləyir",
"plugin-item.not-compatible": "Bu plaqinin uyğunluq məlumatı yoxdur, prod mühitinizdə quraşdırmadan əvvəl onun işlədiyinə əmin olun.",
"alert.enabled": "Plugin aktivdir",
"alert.disabled": "Plugin deaktivdir",
"alert.upgraded": "Plugin təkmilləşdirildi",
"alert.installed": "Plugin quraşdırılıb",
"alert.uninstalled": "Plugin silindi",
"alert.activate-success": "Bu plaqini tam aktivləşdirmək üçün NodeBB-ni yenidən qurun və yenidən başladın",
"alert.deactivate-success": "Plugin uğurla deaktiv edildi",
"alert.upgrade-success": "Bu plaqini tam təkmilləşdirmək üçün NodeBB-ni yenidən qurun və yenidən başladın.",
"alert.install-success": "Plugin uğurla quraşdırıldı, lütfən, plaqini aktivləşdirin.",
"alert.uninstall-success": "Plugin uğurla deaktiv edildi və silindi.",
"alert.suggest-error": "<p>NodeBB paket menecerinə çata bilmədi, ən son versiyanın quraşdırılmasına davam edin?</p><div class=\"alert alert-danger\"><strong> Server qaytarıldı (%1)</strong>: %2</div>",
"alert.package-manager-unreachable": "<p>NodeBB paket menecerinə müraciət edə bilmədi, hazırda təkmilləşdirmə təklif edilmir.</p>",
"alert.incompatible": "<p>NodeBB versiyanız (v%1) yalnız bu plaqinin v%2-yə yüksəldilməsi üçün silinib. Bu plaqinin daha yeni versiyasını quraşdırmaq istəyirsinizsə, lütfən, NodeBB-ni yeniləyin.</p>",
"alert.possibly-incompatible": "<div class=\"alert alert-warning\"><p><strong>Uyğunluq haqqında məlumat tapılmadı. </strong></p><p>Bu plagin sizin NodeBB versiyanızı nəzərə alaraq quraşdırma üçün xüsusi versiyanı təyin etməyib.</p></div><p> Tam uyğunluğa zəmanət verilə bilməz və NodeBB-nin artıq düzgün başlamamasına səbəb ola bilər. NodeBB düzgün yüklənə bilmədikdə:<pre><code>$ ./nodebb reset plugin=\"%1\"</code></pre><p>Bu plaqinin ən son versiyasının quraşdırılmasına davam edilsin?</p>",
"alert.reorder": "Pluginlər yenidən sifariş edildi",
"alert.reorder-success": "Prosesi tam başa çatdırmaq üçün NodeBB-ni yenidən qurun və yenidən başladın.",
"license.title": "Plugin lisenziya məlumatı",
"license.intro": "%1 plaqini %2 altında lisenziyalaşdırılıb. Bu plagini aktivləşdirməzdən əvvəl lisenziya şərtlərini oxuyun və anlayın.",
"license.cta": "Bu plaqini aktivləşdirməyə davam etmək istəyirsiniz?"
}

Some files were not shown because too many files have changed in this diff Show More