Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b088a5ef2a | ||
|
|
c502cd58db | ||
|
|
97e7d7190d | ||
|
|
38919003e9 | ||
|
|
f15a8dc277 | ||
|
|
92927b1373 | ||
|
|
6a4108ed61 | ||
|
|
c9f1be2faf | ||
|
|
57be6945f2 | ||
|
|
82abe52ea5 | ||
|
|
6fa7af7905 | ||
|
|
13e7a30bb9 | ||
|
|
955af04e32 | ||
|
|
035e67f759 | ||
|
|
167ec782ef | ||
|
|
743c33475f | ||
|
|
3f1d3b7833 | ||
|
|
3d76e41cd8 | ||
|
|
e9efbc17bc | ||
|
|
307d176430 | ||
|
|
7e24ba9721 | ||
|
|
f9774d82d8 | ||
|
|
7647a9f620 | ||
|
|
d4e8d4f58b | ||
|
|
4df8dea5cc | ||
|
|
84aa100f84 | ||
|
|
bddb87b9b3 | ||
|
|
18c10c0ac6 | ||
|
|
f02e2979c4 | ||
|
|
7b34cb14cb | ||
|
|
019ef090ac | ||
|
|
7304b54125 | ||
|
|
ea0d5216e8 | ||
|
|
62deb6c152 | ||
|
|
9ba2b4c82c | ||
|
|
a47d080657 |
28
.github/workflows/build-docker-image.yml
vendored
28
.github/workflows/build-docker-image.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Create Docker Image
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
release:
|
||||
@@ -10,15 +10,25 @@ jobs:
|
||||
steps:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: login to docker registry
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
- name: Build the image
|
||||
run: |
|
||||
docker buildx build --push \
|
||||
--tag stonith404/pingvin-share:latest \
|
||||
--tag stonith404/pingvin-share:${{ github.ref_name }} \
|
||||
--platform linux/amd64,linux/arm64 .
|
||||
|
||||
- name: Login to Docker registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: stonith404/pingvin-share:latest,stonith404/pingvin-share:${{ github.ref_name }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/backend/src/constants.ts
|
||||
56
CHANGELOG.md
56
CHANGELOG.md
@@ -1,3 +1,59 @@
|
||||
## [0.18.2](https://github.com/stonith404/pingvin-share/compare/v0.18.1...v0.18.2) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable image optimizations for logo to prevent caching issues with custom logos ([3891900](https://github.com/stonith404/pingvin-share/commit/38919003e9091203b507d0f0b061f4a1835ff4f4))
|
||||
* memory leak while downloading large files ([97e7d71](https://github.com/stonith404/pingvin-share/commit/97e7d7190dfe219caf441dffcd7830c304c3c939))
|
||||
|
||||
## [0.18.1](https://github.com/stonith404/pingvin-share/compare/v0.18.0...v0.18.1) (2023-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* permission changes of docker container brakes existing installations ([6a4108e](https://github.com/stonith404/pingvin-share/commit/6a4108ed6138e7297e66fd1e38450f23afe99aae))
|
||||
|
||||
## [0.18.0](https://github.com/stonith404/pingvin-share/compare/v0.17.5...v0.18.0) (2023-09-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* show upload modal on file drop ([13e7a30](https://github.com/stonith404/pingvin-share/commit/13e7a30bb96faeb25936ff08a107834fd7af5766))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docker:** Updated to newest version of alpine linux and fixed missing dependencies ([#255](https://github.com/stonith404/pingvin-share/issues/255)) ([6fa7af7](https://github.com/stonith404/pingvin-share/commit/6fa7af79051c964060bd291c9faad90fc01a1b72))
|
||||
* nextjs proxy warning ([e9efbc1](https://github.com/stonith404/pingvin-share/commit/e9efbc17bcf4827e935e2018dcdf3b70a9a49991))
|
||||
|
||||
## [0.17.5](https://github.com/stonith404/pingvin-share/compare/v0.17.4...v0.17.5) (2023-09-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **localization:** Added thai language ([#231](https://github.com/stonith404/pingvin-share/issues/231)) ([bddb87b](https://github.com/stonith404/pingvin-share/commit/bddb87b9b3ec5426a3c7a14a96caf2eb45b93ff7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* autocomplete on create share modal ([d4e8d4f](https://github.com/stonith404/pingvin-share/commit/d4e8d4f58b9b7d10b865eff49aa784547891c4e8))
|
||||
* missing translation ([7647a9f](https://github.com/stonith404/pingvin-share/commit/7647a9f620cbc5d38e019225a680a53bd3027698))
|
||||
|
||||
## [0.17.4](https://github.com/stonith404/pingvin-share/compare/v0.17.3...v0.17.4) (2023-08-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* redirection to `localhost:3000` ([ea0d521](https://github.com/stonith404/pingvin-share/commit/ea0d5216e89346b8d3ef0277b76fdc6302e9de15))
|
||||
|
||||
## [0.17.3](https://github.com/stonith404/pingvin-share/compare/v0.17.2...v0.17.3) (2023-07-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* logo doesn't get loaded correctly ([9ba2b4c](https://github.com/stonith404/pingvin-share/commit/9ba2b4c82cdad9097b33f0451771818c7b972a6b))
|
||||
* share expiration never doesn't work if using another language than English ([a47d080](https://github.com/stonith404/pingvin-share/commit/a47d080657e1d08ef06ec7425d8bdafd5a26c24a))
|
||||
|
||||
## [0.17.2](https://github.com/stonith404/pingvin-share/compare/v0.17.1...v0.17.2) (2023-07-31)
|
||||
|
||||
|
||||
|
||||
39
Dockerfile
39
Dockerfile
@@ -1,37 +1,40 @@
|
||||
# Using node slim because prisma ORM needs libc for ARM builds
|
||||
|
||||
# Stage 1: on frontend dependency change
|
||||
FROM node:19-slim AS frontend-dependencies
|
||||
# Stage 1: Frontend dependencies
|
||||
FROM node:20-alpine AS frontend-dependencies
|
||||
WORKDIR /opt/app
|
||||
COPY frontend/package.json frontend/package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Stage 2: on frontend change
|
||||
FROM node:19-slim AS frontend-builder
|
||||
# Stage 2: Build frontend
|
||||
FROM node:20-alpine AS frontend-builder
|
||||
WORKDIR /opt/app
|
||||
COPY ./frontend .
|
||||
COPY --from=frontend-dependencies /opt/app/node_modules ./node_modules
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: on backend dependency change
|
||||
FROM node:19-slim AS backend-dependencies
|
||||
# Stage 3: Backend dependencies
|
||||
FROM node:20-alpine AS backend-dependencies
|
||||
WORKDIR /opt/app
|
||||
COPY backend/package.json backend/package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Stage 4:on backend change
|
||||
FROM node:19-slim AS backend-builder
|
||||
RUN apt-get update && apt-get install -y openssl
|
||||
# Stage 4: Build backend
|
||||
FROM node:20-alpine AS backend-builder
|
||||
WORKDIR /opt/app
|
||||
COPY ./backend .
|
||||
COPY --from=backend-dependencies /opt/app/node_modules ./node_modules
|
||||
RUN npx prisma generate
|
||||
RUN npm run build && npm prune --production
|
||||
RUN npm run build && npm prune --production
|
||||
|
||||
# Stage 5: Final image
|
||||
FROM node:19-slim AS runner
|
||||
FROM node:20-alpine AS runner
|
||||
ENV NODE_ENV=docker
|
||||
RUN apt-get update && apt-get install -y curl openssl
|
||||
|
||||
# Alpine specific dependencies
|
||||
RUN apk update --no-cache
|
||||
RUN apk upgrade --no-cache
|
||||
RUN apk add --no-cache curl nginx
|
||||
|
||||
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
WORKDIR /opt/app/frontend
|
||||
COPY --from=frontend-builder /opt/app/public ./public
|
||||
@@ -46,8 +49,12 @@ COPY --from=backend-builder /opt/app/prisma ./prisma
|
||||
COPY --from=backend-builder /opt/app/package.json ./
|
||||
|
||||
WORKDIR /opt/app
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Add a health check to ensure the container is healthy
|
||||
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://localhost:3000/api/health || exit 1
|
||||
|
||||
# HOSTNAME=127.0.0.1 fixes https://github.com/vercel/next.js/issues/51684. It can be removed as soon as the issue is fixed
|
||||
CMD cp -rn /tmp/img /opt/app/frontend/public && HOSTNAME=127.0.0.1 node frontend/server.js & cd backend && npm run prod
|
||||
# Application startup
|
||||
# HOSTNAME=0.0.0.0 fixes https://github.com/vercel/next.js/issues/51684. It can be removed as soon as the issue is fixed
|
||||
CMD cp -rn /tmp/img /opt/app/frontend/public && nginx && PORT=3333 HOSTNAME=0.0.0.0 node frontend/server.js & cd backend && npm run prod
|
||||
@@ -63,6 +63,8 @@ npm run build
|
||||
pm2 start --name="pingvin-share-frontend" npm -- run start
|
||||
```
|
||||
|
||||
**Uploading Large Files**: By default, Pingvin Share uses a built-in reverse proxy to reduce the installation steps. However, this reverse proxy is not optimized for uploading large files. If you wish to upload larger files, you can either use the Docker installation or set up your own reverse proxy. An example configuration for Nginx can be found in `/nginx/nginx.conf`.
|
||||
|
||||
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
|
||||
|
||||
### Integrations
|
||||
|
||||
1
backend/.prettierignore
Normal file
1
backend/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
/src/constants.ts
|
||||
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.1.2",
|
||||
"@nestjs/config": "^3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"dev": "cross-env NODE_ENV=development nest start --watch",
|
||||
|
||||
19
backend/src/app.controller.ts
Normal file
19
backend/src/app.controller.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Controller, Get, Res } from "@nestjs/common";
|
||||
import { Response } from "express";
|
||||
import { PrismaService } from "./prisma/prisma.service";
|
||||
|
||||
@Controller("/")
|
||||
export class AppController {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
@Get("health")
|
||||
async health(@Res({ passthrough: true }) res: Response) {
|
||||
try {
|
||||
await this.prismaService.config.findMany();
|
||||
return "OK";
|
||||
} catch {
|
||||
res.statusCode = 500;
|
||||
return "ERROR";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { ShareModule } from "./share/share.module";
|
||||
import { UserModule } from "./user/user.module";
|
||||
import { ClamScanModule } from "./clamscan/clamscan.module";
|
||||
import { ReverseShareModule } from "./reverseShare/reverseShare.module";
|
||||
import { AppController } from "./app.controller";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -33,6 +34,9 @@ import { ReverseShareModule } from "./reverseShare/reverseShare.module";
|
||||
ClamScanModule,
|
||||
ReverseShareModule,
|
||||
],
|
||||
controllers:[
|
||||
AppController,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
|
||||
@@ -33,14 +33,14 @@ export class AuthController {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private authTotpService: AuthTotpService,
|
||||
private config: ConfigService
|
||||
private config: ConfigService,
|
||||
) {}
|
||||
|
||||
@Post("signUp")
|
||||
@Throttle(10, 5 * 60)
|
||||
async signUp(
|
||||
@Body() dto: AuthRegisterDTO,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
if (!this.config.get("share.allowRegistration"))
|
||||
throw new ForbiddenException("Registration is not allowed");
|
||||
@@ -50,7 +50,7 @@ export class AuthController {
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
result.accessToken,
|
||||
);
|
||||
|
||||
return result;
|
||||
@@ -61,7 +61,7 @@ export class AuthController {
|
||||
@HttpCode(200)
|
||||
async signIn(
|
||||
@Body() dto: AuthSignInDTO,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
const result = await this.authService.signIn(dto);
|
||||
|
||||
@@ -69,7 +69,7 @@ export class AuthController {
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
result.accessToken,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,14 +81,14 @@ export class AuthController {
|
||||
@HttpCode(200)
|
||||
async signInTotp(
|
||||
@Body() dto: AuthSignInTotpDTO,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
const result = await this.authTotpService.signInTotp(dto);
|
||||
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
result.accessToken,
|
||||
);
|
||||
|
||||
return new TokenDTO().from(result);
|
||||
@@ -113,12 +113,12 @@ export class AuthController {
|
||||
async updatePassword(
|
||||
@GetUser() user: User,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
@Body() dto: UpdatePasswordDTO
|
||||
@Body() dto: UpdatePasswordDTO,
|
||||
) {
|
||||
const result = await this.authService.updatePassword(
|
||||
user,
|
||||
dto.oldPassword,
|
||||
dto.password
|
||||
dto.password,
|
||||
);
|
||||
|
||||
response = this.addTokensToResponse(response, result.refreshToken);
|
||||
@@ -129,12 +129,12 @@ export class AuthController {
|
||||
@HttpCode(200)
|
||||
async refreshAccessToken(
|
||||
@Req() request: Request,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
if (!request.cookies.refresh_token) throw new UnauthorizedException();
|
||||
|
||||
const accessToken = await this.authService.refreshAccessToken(
|
||||
request.cookies.refresh_token
|
||||
request.cookies.refresh_token,
|
||||
);
|
||||
response = this.addTokensToResponse(response, undefined, accessToken);
|
||||
return new TokenDTO().from({ accessToken });
|
||||
@@ -143,7 +143,7 @@ export class AuthController {
|
||||
@Post("signOut")
|
||||
async signOut(
|
||||
@Req() request: Request,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
await this.authService.signOut(request.cookies.access_token);
|
||||
response.cookie("access_token", "accessToken", { maxAge: -1 });
|
||||
@@ -176,7 +176,7 @@ export class AuthController {
|
||||
private addTokensToResponse(
|
||||
response: Response,
|
||||
refreshToken?: string,
|
||||
accessToken?: string
|
||||
accessToken?: string,
|
||||
) {
|
||||
if (accessToken)
|
||||
response.cookie("access_token", accessToken, { sameSite: "lax" });
|
||||
|
||||
@@ -21,7 +21,7 @@ export class AuthService {
|
||||
private prisma: PrismaService,
|
||||
private jwtService: JwtService,
|
||||
private config: ConfigService,
|
||||
private emailService: EmailService
|
||||
private emailService: EmailService,
|
||||
) {}
|
||||
|
||||
async signUp(dto: AuthRegisterDTO) {
|
||||
@@ -39,7 +39,7 @@ export class AuthService {
|
||||
});
|
||||
|
||||
const { refreshToken, refreshTokenId } = await this.createRefreshToken(
|
||||
user.id
|
||||
user.id,
|
||||
);
|
||||
const accessToken = await this.createAccessToken(user, refreshTokenId);
|
||||
|
||||
@@ -49,7 +49,7 @@ export class AuthService {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const { refreshToken, refreshTokenId } = await this.createRefreshToken(
|
||||
user.id
|
||||
user.id,
|
||||
);
|
||||
const accessToken = await this.createAccessToken(user, refreshTokenId);
|
||||
|
||||
@@ -158,7 +158,7 @@ export class AuthService {
|
||||
{
|
||||
expiresIn: "15min",
|
||||
secret: this.config.get("internal.jwtSecret"),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ export class AuthService {
|
||||
|
||||
return this.createAccessToken(
|
||||
refreshTokenMetaData.user,
|
||||
refreshTokenMetaData.id
|
||||
refreshTokenMetaData.id,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class AuthTotpService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private authService: AuthService,
|
||||
private config: ConfigService
|
||||
private config: ConfigService,
|
||||
) {}
|
||||
|
||||
async signInTotp(dto: AuthSignInTotpDTO) {
|
||||
@@ -72,7 +72,7 @@ export class AuthTotpService {
|
||||
await this.authService.createRefreshToken(user.id);
|
||||
const accessToken = await this.authService.createAccessToken(
|
||||
user,
|
||||
refreshTokenId
|
||||
refreshTokenId,
|
||||
);
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
@@ -98,7 +98,7 @@ export class AuthTotpService {
|
||||
const otpURL = totp.keyuri(
|
||||
user.username || user.email,
|
||||
this.config.get("general.appName"),
|
||||
secret
|
||||
secret,
|
||||
);
|
||||
|
||||
await this.prisma.user.update({
|
||||
|
||||
@@ -5,5 +5,5 @@ export const GetUser = createParamDecorator(
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
return data ? user?.[data] : user;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -8,7 +8,10 @@ import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(config: ConfigService, private prisma: PrismaService) {
|
||||
constructor(
|
||||
config: ConfigService,
|
||||
private prisma: PrismaService,
|
||||
) {
|
||||
config.get("internal.jwtSecret");
|
||||
super({
|
||||
jwtFromRequest: JwtStrategy.extractJWT,
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ClamScanService {
|
||||
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private prisma: PrismaService
|
||||
private prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
private ClamScan: Promise<NodeClam | null> = new NodeClam()
|
||||
@@ -81,7 +81,7 @@ export class ClamScanService {
|
||||
});
|
||||
|
||||
this.logger.warn(
|
||||
`Share ${shareId} deleted because it contained ${infectedFiles.length} malicious file(s)`
|
||||
`Share ${shareId} deleted because it contained ${infectedFiles.length} malicious file(s)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ConfigController {
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private logoService: LogoService,
|
||||
private emailService: EmailService
|
||||
private emailService: EmailService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@@ -41,7 +41,7 @@ export class ConfigController {
|
||||
@UseGuards(JwtGuard, AdministratorGuard)
|
||||
async getByCategory(@Param("category") category: string) {
|
||||
return new AdminConfigDTO().fromList(
|
||||
await this.configService.getByCategory(category)
|
||||
await this.configService.getByCategory(category),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export class ConfigController {
|
||||
@UseGuards(JwtGuard, AdministratorGuard)
|
||||
async updateMany(@Body() data: UpdateConfigDTO[]) {
|
||||
return new AdminConfigDTO().fromList(
|
||||
await this.configService.updateMany(data)
|
||||
await this.configService.updateMany(data),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ export class ConfigController {
|
||||
@UploadedFile(
|
||||
new ParseFilePipe({
|
||||
validators: [new FileTypeValidator({ fileType: "image/png" })],
|
||||
})
|
||||
}),
|
||||
)
|
||||
file: Express.Multer.File
|
||||
file: Express.Multer.File,
|
||||
) {
|
||||
return await this.logoService.create(file.buffer);
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import { PrismaService } from "src/prisma/prisma.service";
|
||||
export class ConfigService {
|
||||
constructor(
|
||||
@Inject("CONFIG_VARIABLES") private configVariables: Config[],
|
||||
private prisma: PrismaService
|
||||
private prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
get(key: `${string}.${string}`): any {
|
||||
const configVariable = this.configVariables.filter(
|
||||
(variable) => `${variable.category}.${variable.name}` == key
|
||||
(variable) => `${variable.category}.${variable.name}` == key,
|
||||
)[0];
|
||||
|
||||
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
||||
@@ -89,7 +89,7 @@ export class ConfigService {
|
||||
configVariable.type != "text"
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
`Config variable must be of type ${configVariable.type}`
|
||||
`Config variable must be of type ${configVariable.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export class AdminConfigDTO extends ConfigDTO {
|
||||
|
||||
fromList(partial: Partial<AdminConfigDTO>[]) {
|
||||
return partial.map((part) =>
|
||||
plainToClass(AdminConfigDTO, part, { excludeExtraneousValues: true })
|
||||
plainToClass(AdminConfigDTO, part, { excludeExtraneousValues: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export class ConfigDTO {
|
||||
|
||||
fromList(partial: Partial<ConfigDTO>[]) {
|
||||
return partial.map((part) =>
|
||||
plainToClass(ConfigDTO, part, { excludeExtraneousValues: true })
|
||||
plainToClass(ConfigDTO, part, { excludeExtraneousValues: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ const IMAGES_PATH = "../frontend/public/img";
|
||||
@Injectable()
|
||||
export class LogoService {
|
||||
async create(file: Buffer) {
|
||||
fs.writeFileSync(`${IMAGES_PATH}/logo.png`, file, "binary");
|
||||
const resized = await sharp(file).resize(900).toBuffer();
|
||||
fs.writeFileSync(`${IMAGES_PATH}/logo.png`, resized, "binary");
|
||||
this.createFavicon(file);
|
||||
this.createPWAIcons(file);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class EmailService {
|
||||
await this.getTransporter()
|
||||
.sendMail({
|
||||
from: `"${this.config.get("general.appName")}" <${this.config.get(
|
||||
"smtp.email"
|
||||
"smtp.email",
|
||||
)}>`,
|
||||
to: email,
|
||||
subject,
|
||||
@@ -49,7 +49,7 @@ export class EmailService {
|
||||
shareId: string,
|
||||
creator?: User,
|
||||
description?: string,
|
||||
expiration?: Date
|
||||
expiration?: Date,
|
||||
) {
|
||||
if (!this.config.get("email.enableShareEmailRecipients"))
|
||||
throw new InternalServerErrorException("Email service disabled");
|
||||
@@ -69,8 +69,8 @@ export class EmailService {
|
||||
"{expires}",
|
||||
moment(expiration).unix() != 0
|
||||
? moment(expiration).fromNow()
|
||||
: "in: never"
|
||||
)
|
||||
: "in: never",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,13 +83,13 @@ export class EmailService {
|
||||
this.config
|
||||
.get("email.reverseShareMessage")
|
||||
.replaceAll("\\n", "\n")
|
||||
.replaceAll("{shareUrl}", shareUrl)
|
||||
.replaceAll("{shareUrl}", shareUrl),
|
||||
);
|
||||
}
|
||||
|
||||
async sendResetPasswordEmail(recipientEmail: string, token: string) {
|
||||
const resetPasswordUrl = `${this.config.get(
|
||||
"general.appUrl"
|
||||
"general.appUrl",
|
||||
)}/auth/resetPassword/${token}`;
|
||||
|
||||
await this.sendMail(
|
||||
@@ -98,7 +98,7 @@ export class EmailService {
|
||||
this.config
|
||||
.get("email.resetPasswordMessage")
|
||||
.replaceAll("\\n", "\n")
|
||||
.replaceAll("{url}", resetPasswordUrl)
|
||||
.replaceAll("{url}", resetPasswordUrl),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class EmailService {
|
||||
this.config
|
||||
.get("email.inviteMessage")
|
||||
.replaceAll("{url}", loginUrl)
|
||||
.replaceAll("{password}", password)
|
||||
.replaceAll("{password}", password),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ export class EmailService {
|
||||
await this.getTransporter()
|
||||
.sendMail({
|
||||
from: `"${this.config.get("general.appName")}" <${this.config.get(
|
||||
"smtp.email"
|
||||
"smtp.email",
|
||||
)}>`,
|
||||
to: recipientEmail,
|
||||
subject: "Test email",
|
||||
|
||||
@@ -28,7 +28,7 @@ export class FileController {
|
||||
@Query() query: any,
|
||||
|
||||
@Body() body: string,
|
||||
@Param("shareId") shareId: string
|
||||
@Param("shareId") shareId: string,
|
||||
) {
|
||||
const { id, name, chunkIndex, totalChunks } = query;
|
||||
|
||||
@@ -39,7 +39,7 @@ export class FileController {
|
||||
data,
|
||||
{ index: parseInt(chunkIndex), total: parseInt(totalChunks) },
|
||||
{ id, name },
|
||||
shareId
|
||||
shareId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class FileController {
|
||||
@UseGuards(FileSecurityGuard)
|
||||
async getZip(
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Param("shareId") shareId: string
|
||||
@Param("shareId") shareId: string,
|
||||
) {
|
||||
const zip = this.fileService.getZip(shareId);
|
||||
res.set({
|
||||
@@ -64,7 +64,7 @@ export class FileController {
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Param("shareId") shareId: string,
|
||||
@Param("fileId") fileId: string,
|
||||
@Query("download") download = "true"
|
||||
@Query("download") download = "true",
|
||||
) {
|
||||
const file = await this.fileService.get(shareId, fileId);
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ export class FileService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private jwtService: JwtService,
|
||||
private config: ConfigService
|
||||
private config: ConfigService,
|
||||
) {}
|
||||
|
||||
async create(
|
||||
data: string,
|
||||
chunk: { index: number; total: number },
|
||||
file: { id?: string; name: string },
|
||||
shareId: string
|
||||
shareId: string,
|
||||
) {
|
||||
if (!file.id) file.id = crypto.randomUUID();
|
||||
|
||||
@@ -40,7 +40,7 @@ export class FileService {
|
||||
let diskFileSize: number;
|
||||
try {
|
||||
diskFileSize = fs.statSync(
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
|
||||
).size;
|
||||
} catch {
|
||||
diskFileSize = 0;
|
||||
@@ -62,7 +62,7 @@ export class FileService {
|
||||
// Check if share size limit is exceeded
|
||||
const fileSizeSum = share.files.reduce(
|
||||
(n, { size }) => n + parseInt(size),
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
const shareSizeSum = fileSizeSum + diskFileSize + buffer.byteLength;
|
||||
@@ -74,23 +74,23 @@ export class FileService {
|
||||
) {
|
||||
throw new HttpException(
|
||||
"Max share size exceeded",
|
||||
HttpStatus.PAYLOAD_TOO_LARGE
|
||||
HttpStatus.PAYLOAD_TOO_LARGE,
|
||||
);
|
||||
}
|
||||
|
||||
fs.appendFileSync(
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
|
||||
buffer
|
||||
buffer,
|
||||
);
|
||||
|
||||
const isLastChunk = chunk.index == chunk.total - 1;
|
||||
if (isLastChunk) {
|
||||
fs.renameSync(
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}`
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}`,
|
||||
);
|
||||
const fileSize = fs.statSync(
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}`
|
||||
`${SHARE_DIRECTORY}/${shareId}/${file.id}`,
|
||||
).size;
|
||||
await this.prisma.file.create({
|
||||
data: {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ShareService } from "src/share/share.service";
|
||||
export class FileSecurityGuard extends ShareSecurityGuard {
|
||||
constructor(
|
||||
private _shareService: ShareService,
|
||||
private _prisma: PrismaService
|
||||
private _prisma: PrismaService,
|
||||
) {
|
||||
super(_shareService, _prisma);
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export class FileSecurityGuard extends ShareSecurityGuard {
|
||||
|
||||
const shareId = Object.prototype.hasOwnProperty.call(
|
||||
request.params,
|
||||
"shareId"
|
||||
"shareId",
|
||||
)
|
||||
? request.params.shareId
|
||||
: request.params.id;
|
||||
@@ -52,7 +52,7 @@ export class FileSecurityGuard extends ShareSecurityGuard {
|
||||
if (share.security?.maxViews && share.security.maxViews <= share.views) {
|
||||
throw new ForbiddenException(
|
||||
"Maximum views exceeded",
|
||||
"share_max_views_exceeded"
|
||||
"share_max_views_exceeded",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export class JobsService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private reverseShareService: ReverseShareService,
|
||||
private fileService: FileService
|
||||
private fileService: FileService,
|
||||
) {}
|
||||
|
||||
@Cron("0 * * * *")
|
||||
@@ -56,7 +56,7 @@ export class JobsService {
|
||||
|
||||
if (expiredReverseShares.length > 0) {
|
||||
this.logger.log(
|
||||
`Deleted ${expiredReverseShares.length} expired reverse shares`
|
||||
`Deleted ${expiredReverseShares.length} expired reverse shares`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export class JobsService {
|
||||
|
||||
for (const file of temporaryFiles) {
|
||||
const stats = fs.statSync(
|
||||
`${SHARE_DIRECTORY}/${shareDirectory}/${file}`
|
||||
`${SHARE_DIRECTORY}/${shareDirectory}/${file}`,
|
||||
);
|
||||
const isOlderThanOneDay = moment(stats.mtime)
|
||||
.add(1, "day")
|
||||
|
||||
@@ -23,7 +23,7 @@ export class ReverseShareTokenWithShares extends OmitType(ReverseShareDTO, [
|
||||
return partial.map((part) =>
|
||||
plainToClass(ReverseShareTokenWithShares, part, {
|
||||
excludeExtraneousValues: true,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ReverseShareService } from "./reverseShare.service";
|
||||
export class ReverseShareController {
|
||||
constructor(
|
||||
private reverseShareService: ReverseShareService,
|
||||
private config: ConfigService
|
||||
private config: ConfigService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
@@ -44,7 +44,7 @@ export class ReverseShareController {
|
||||
if (!isValid) throw new NotFoundException("Reverse share token not found");
|
||||
|
||||
return new ReverseShareDTO().from(
|
||||
await this.reverseShareService.getByToken(reverseShareToken)
|
||||
await this.reverseShareService.getByToken(reverseShareToken),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export class ReverseShareController {
|
||||
@UseGuards(JwtGuard)
|
||||
async getAllByUser(@GetUser() user: User) {
|
||||
return new ReverseShareTokenWithShares().fromList(
|
||||
await this.reverseShareService.getAllByUser(user.id)
|
||||
await this.reverseShareService.getAllByUser(user.id),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export class ReverseShareService {
|
||||
constructor(
|
||||
private config: ConfigService,
|
||||
private prisma: PrismaService,
|
||||
private fileService: FileService
|
||||
private fileService: FileService,
|
||||
) {}
|
||||
|
||||
async create(data: CreateReverseShareDTO, creatorId: string) {
|
||||
@@ -19,8 +19,8 @@ export class ReverseShareService {
|
||||
.add(
|
||||
data.shareExpiration.split("-")[0],
|
||||
data.shareExpiration.split(
|
||||
"-"
|
||||
)[1] as moment.unitOfTime.DurationConstructor
|
||||
"-",
|
||||
)[1] as moment.unitOfTime.DurationConstructor,
|
||||
)
|
||||
.toDate();
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ReverseShareService {
|
||||
|
||||
if (globalMaxShareSize < data.maxShareSize)
|
||||
throw new BadRequestException(
|
||||
`Max share size can't be greater than ${globalMaxShareSize} bytes.`
|
||||
`Max share size can't be greater than ${globalMaxShareSize} bytes.`,
|
||||
);
|
||||
|
||||
const reverseShare = await this.prisma.reverseShare.create({
|
||||
|
||||
@@ -27,7 +27,7 @@ export class MyShareDTO extends OmitType(ShareDTO, [
|
||||
|
||||
fromList(partial: Partial<MyShareDTO>[]) {
|
||||
return partial.map((part) =>
|
||||
plainToClass(MyShareDTO, part, { excludeExtraneousValues: true })
|
||||
plainToClass(MyShareDTO, part, { excludeExtraneousValues: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class ShareDTO {
|
||||
|
||||
fromList(partial: Partial<ShareDTO>[]) {
|
||||
return partial.map((part) =>
|
||||
plainToClass(ShareDTO, part, { excludeExtraneousValues: true })
|
||||
plainToClass(ShareDTO, part, { excludeExtraneousValues: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ReverseShareService } from "src/reverseShare/reverseShare.service";
|
||||
export class CreateShareGuard extends JwtGuard {
|
||||
constructor(
|
||||
configService: ConfigService,
|
||||
private reverseShareService: ReverseShareService
|
||||
private reverseShareService: ReverseShareService,
|
||||
) {
|
||||
super(configService);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export class CreateShareGuard extends JwtGuard {
|
||||
if (!reverseShareTokenId) return false;
|
||||
|
||||
const isReverseShareTokenValid = await this.reverseShareService.isValid(
|
||||
reverseShareTokenId
|
||||
reverseShareTokenId,
|
||||
);
|
||||
|
||||
return isReverseShareTokenValid;
|
||||
|
||||
@@ -16,7 +16,7 @@ export class ShareOwnerGuard implements CanActivate {
|
||||
const request: Request = context.switchToHttp().getRequest();
|
||||
const shareId = Object.prototype.hasOwnProperty.call(
|
||||
request.params,
|
||||
"shareId"
|
||||
"shareId",
|
||||
)
|
||||
? request.params.shareId
|
||||
: request.params.id;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ShareService } from "src/share/share.service";
|
||||
export class ShareSecurityGuard implements CanActivate {
|
||||
constructor(
|
||||
private shareService: ShareService,
|
||||
private prisma: PrismaService
|
||||
private prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext) {
|
||||
@@ -22,7 +22,7 @@ export class ShareSecurityGuard implements CanActivate {
|
||||
|
||||
const shareId = Object.prototype.hasOwnProperty.call(
|
||||
request.params,
|
||||
"shareId"
|
||||
"shareId",
|
||||
)
|
||||
? request.params.shareId
|
||||
: request.params.id;
|
||||
@@ -44,13 +44,13 @@ export class ShareSecurityGuard implements CanActivate {
|
||||
if (share.security?.password && !shareToken)
|
||||
throw new ForbiddenException(
|
||||
"This share is password protected",
|
||||
"share_password_required"
|
||||
"share_password_required",
|
||||
);
|
||||
|
||||
if (!(await this.shareService.verifyShareToken(shareId, shareToken)))
|
||||
throw new ForbiddenException(
|
||||
"Share token required",
|
||||
"share_token_required"
|
||||
"share_token_required",
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -16,7 +16,7 @@ export class ShareTokenSecurity implements CanActivate {
|
||||
const request: Request = context.switchToHttp().getRequest();
|
||||
const shareId = Object.prototype.hasOwnProperty.call(
|
||||
request.params,
|
||||
"shareId"
|
||||
"shareId",
|
||||
)
|
||||
? request.params.shareId
|
||||
: request.params.id;
|
||||
|
||||
@@ -33,7 +33,7 @@ export class ShareController {
|
||||
@UseGuards(JwtGuard)
|
||||
async getMyShares(@GetUser() user: User) {
|
||||
return new MyShareDTO().fromList(
|
||||
await this.shareService.getSharesByUser(user.id)
|
||||
await this.shareService.getSharesByUser(user.id),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ export class ShareController {
|
||||
async create(
|
||||
@Body() body: CreateShareDTO,
|
||||
@Req() request: Request,
|
||||
@GetUser() user: User
|
||||
@GetUser() user: User,
|
||||
) {
|
||||
const { reverse_share_token } = request.cookies;
|
||||
return new ShareDTO().from(
|
||||
await this.shareService.create(body, user, reverse_share_token)
|
||||
await this.shareService.create(body, user, reverse_share_token),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export class ShareController {
|
||||
async complete(@Param("id") id: string, @Req() request: Request) {
|
||||
const { reverse_share_token } = request.cookies;
|
||||
return new ShareDTO().from(
|
||||
await this.shareService.complete(id, reverse_share_token)
|
||||
await this.shareService.complete(id, reverse_share_token),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export class ShareController {
|
||||
async getShareToken(
|
||||
@Param("id") id: string,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
@Body() body: SharePasswordDto
|
||||
@Body() body: SharePasswordDto,
|
||||
) {
|
||||
const token = await this.shareService.getShareToken(id, body.password);
|
||||
response.cookie(`share_${id}_token`, token, {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ShareService {
|
||||
private config: ConfigService,
|
||||
private jwtService: JwtService,
|
||||
private reverseShareService: ReverseShareService,
|
||||
private clamScanService: ClamScanService
|
||||
private clamScanService: ClamScanService,
|
||||
) {}
|
||||
|
||||
async create(share: CreateShareDTO, user?: User, reverseShareToken?: string) {
|
||||
@@ -46,7 +46,7 @@ export class ShareService {
|
||||
|
||||
// If share is created by a reverse share token override the expiration date
|
||||
const reverseShare = await this.reverseShareService.getByToken(
|
||||
reverseShareToken
|
||||
reverseShareToken,
|
||||
);
|
||||
if (reverseShare) {
|
||||
expirationDate = reverseShare.shareExpiration;
|
||||
@@ -57,8 +57,8 @@ export class ShareService {
|
||||
.add(
|
||||
share.expiration.split("-")[0],
|
||||
share.expiration.split(
|
||||
"-"
|
||||
)[1] as moment.unitOfTime.DurationConstructor
|
||||
"-",
|
||||
)[1] as moment.unitOfTime.DurationConstructor,
|
||||
)
|
||||
.toDate();
|
||||
} else {
|
||||
@@ -134,13 +134,13 @@ export class ShareService {
|
||||
|
||||
if (share.files.length == 0)
|
||||
throw new BadRequestException(
|
||||
"You need at least on file in your share to complete it."
|
||||
"You need at least on file in your share to complete it.",
|
||||
);
|
||||
|
||||
// Asynchronously create a zip of all files
|
||||
if (share.files.length > 1)
|
||||
this.createZip(id).then(() =>
|
||||
this.prisma.share.update({ where: { id }, data: { isZipReady: true } })
|
||||
this.prisma.share.update({ where: { id }, data: { isZipReady: true } }),
|
||||
);
|
||||
|
||||
// Send email for each recipient
|
||||
@@ -150,7 +150,7 @@ export class ShareService {
|
||||
share.id,
|
||||
share.creator,
|
||||
share.description,
|
||||
share.expiration
|
||||
share.expiration,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ export class ShareService {
|
||||
) {
|
||||
await this.emailService.sendMailToReverseShareCreator(
|
||||
share.reverseShare.creator.email,
|
||||
share.id
|
||||
share.id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ export class ShareService {
|
||||
if (share.security?.maxViews && share.security.maxViews <= share.views) {
|
||||
throw new ForbiddenException(
|
||||
"Maximum views exceeded",
|
||||
"share_max_views_exceeded"
|
||||
"share_max_views_exceeded",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ export class ShareService {
|
||||
{
|
||||
expiresIn: moment(expiration).diff(new Date(), "seconds") + "s",
|
||||
secret: this.config.get("internal.jwtSecret"),
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ import { OmitType, PartialType } from "@nestjs/swagger";
|
||||
import { UserDTO } from "./user.dto";
|
||||
|
||||
export class UpdateOwnUserDTO extends PartialType(
|
||||
OmitType(UserDTO, ["isAdmin", "password"] as const)
|
||||
OmitType(UserDTO, ["isAdmin", "password"] as const),
|
||||
) {}
|
||||
|
||||
@@ -31,7 +31,7 @@ export class UserDTO {
|
||||
|
||||
fromList(partial: Partial<UserDTO>[]) {
|
||||
return partial.map((part) =>
|
||||
plainToClass(UserDTO, part, { excludeExtraneousValues: true })
|
||||
plainToClass(UserDTO, part, { excludeExtraneousValues: true }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export class UserController {
|
||||
@UseGuards(JwtGuard)
|
||||
async updateCurrentUser(
|
||||
@GetUser() user: User,
|
||||
@Body() data: UpdateOwnUserDTO
|
||||
@Body() data: UpdateOwnUserDTO,
|
||||
) {
|
||||
return new UserDTO().from(await this.userService.update(user.id, data));
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export class UserController {
|
||||
@UseGuards(JwtGuard)
|
||||
async deleteCurrentUser(
|
||||
@GetUser() user: User,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
) {
|
||||
response.cookie("access_token", "accessToken", { maxAge: -1 });
|
||||
response.cookie("refresh_token", "", {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { UpdateUserDto } from "./dto/updateUser.dto";
|
||||
export class UserSevice {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private emailService: EmailService
|
||||
private emailService: EmailService,
|
||||
) {}
|
||||
|
||||
async list() {
|
||||
@@ -46,7 +46,7 @@ export class UserSevice {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ export class UserSevice {
|
||||
if (e.code == "P2002") {
|
||||
const duplicatedField: string = e.meta.target[0];
|
||||
throw new BadRequestException(
|
||||
`A user with this ${duplicatedField} already exists`
|
||||
`A user with this ${duplicatedField} already exists`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"react-hooks/exhaustive-deps": ["off"],
|
||||
"import/no-anonymous-default-export": ["off"],
|
||||
"no-unused-vars": ["warn"],
|
||||
"react/no-unescaped-entities": ["off"]
|
||||
"react/no-unescaped-entities": ["off"],
|
||||
"@next/next/no-img-element": ["off"]
|
||||
}
|
||||
}
|
||||
|
||||
1
frontend/.prettierignore
Normal file
1
frontend/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
/src/i18n/translations/*
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "0.17.2",
|
||||
"version": "0.18.2",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "node .next/standalone/server.js",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"format": "prettier --end-of-line=auto --write \"src/**/*.ts*\""
|
||||
},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 86 KiB |
@@ -1,6 +1,4 @@
|
||||
import Image from "next/image";
|
||||
|
||||
const Logo = ({ height, width }: { height: number; width: number }) => {
|
||||
return <Image src="/img/logo.png" alt="logo" height={height} width={width} />;
|
||||
return <img src="/img/logo.png" alt="logo" height={height} width={width} />;
|
||||
};
|
||||
export default Logo;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LOCALES } from "../../i18n/locales";
|
||||
const LanguagePicker = () => {
|
||||
const t = useTranslate();
|
||||
const [selectedLanguage, setSelectedLanguage] = useState(
|
||||
getCookie("language")?.toString()
|
||||
getCookie("language")?.toString(),
|
||||
);
|
||||
|
||||
const languages = Object.values(LOCALES).map((locale) => ({
|
||||
@@ -23,7 +23,7 @@ const LanguagePicker = () => {
|
||||
setCookie("language", value, {
|
||||
sameSite: "lax",
|
||||
expires: new Date(
|
||||
new Date().setFullYear(new Date().getFullYear() + 1)
|
||||
new Date().setFullYear(new Date().getFullYear() + 1),
|
||||
),
|
||||
});
|
||||
location.reload();
|
||||
|
||||
@@ -14,7 +14,7 @@ import userPreferences from "../../utils/userPreferences.util";
|
||||
|
||||
const ThemeSwitcher = () => {
|
||||
const [colorScheme, setColorScheme] = useState(
|
||||
userPreferences.get("colorScheme")
|
||||
userPreferences.get("colorScheme"),
|
||||
);
|
||||
const { toggleColorScheme } = useMantineColorScheme();
|
||||
const systemColorScheme = useColorScheme();
|
||||
@@ -26,7 +26,7 @@ const ThemeSwitcher = () => {
|
||||
userPreferences.set("colorScheme", value);
|
||||
setColorScheme(value);
|
||||
toggleColorScheme(
|
||||
value == "system" ? systemColorScheme : (value as ColorScheme)
|
||||
value == "system" ? systemColorScheme : (value as ColorScheme),
|
||||
);
|
||||
}}
|
||||
data={[
|
||||
|
||||
@@ -26,7 +26,7 @@ const showEnableTotpModal = (
|
||||
qrCode: string;
|
||||
secret: string;
|
||||
password: string;
|
||||
}
|
||||
},
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { translateOutsideContext } from "../../hooks/useTranslate.hook";
|
||||
const showReverseShareLinkModal = (
|
||||
modals: ModalsContextProps,
|
||||
reverseShareToken: string,
|
||||
appUrl: string
|
||||
appUrl: string,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
const link = `${appUrl}/upload/${reverseShareToken}`;
|
||||
|
||||
@@ -12,7 +12,7 @@ const showShareInformationsModal = (
|
||||
modals: ModalsContextProps,
|
||||
share: MyShare,
|
||||
appUrl: string,
|
||||
maxShareSize: number
|
||||
maxShareSize: number,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
const link = `${appUrl}/s/${share.id}`;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { translateOutsideContext } from "../../hooks/useTranslate.hook";
|
||||
const showShareLinkModal = (
|
||||
modals: ModalsContextProps,
|
||||
shareId: string,
|
||||
appUrl: string
|
||||
appUrl: string,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
const link = `${appUrl}/s/${shareId}`;
|
||||
|
||||
@@ -21,7 +21,7 @@ const AdminConfigInput = ({
|
||||
stringValue: configVariable.value ?? configVariable.defaultValue,
|
||||
textValue: configVariable.value ?? configVariable.defaultValue,
|
||||
numberValue: parseInt(
|
||||
configVariable.value ?? configVariable.defaultValue
|
||||
configVariable.value ?? configVariable.defaultValue,
|
||||
),
|
||||
booleanValue:
|
||||
(configVariable.value ?? configVariable.defaultValue) == "true",
|
||||
|
||||
@@ -33,7 +33,7 @@ const TestEmailButton = ({
|
||||
<Textarea minRows={4} readOnly value={e.response.data.message} />
|
||||
</Stack>
|
||||
),
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import toast from "../../../utils/toast.util";
|
||||
const showCreateUserModal = (
|
||||
modals: ModalsContextProps,
|
||||
smtpEnabled: boolean,
|
||||
getUsers: () => void
|
||||
getUsers: () => void,
|
||||
) => {
|
||||
return modals.openModal({
|
||||
title: "Create user",
|
||||
@@ -55,7 +55,7 @@ const Body = ({
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.optional(),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ const Body = ({
|
||||
labelPosition="left"
|
||||
label={t("admin.users.modal.create.manual-password")}
|
||||
description={t(
|
||||
"admin.users.modal.create.manual-password.description"
|
||||
"admin.users.modal.create.manual-password.description",
|
||||
)}
|
||||
{...form.getInputProps("setPasswordManually", {
|
||||
type: "checkbox",
|
||||
|
||||
@@ -21,7 +21,7 @@ import toast from "../../../utils/toast.util";
|
||||
const showUpdateUserModal = (
|
||||
modals: ModalsContextProps,
|
||||
user: User,
|
||||
getUsers: () => void
|
||||
getUsers: () => void,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
@@ -53,7 +53,7 @@ const Body = ({
|
||||
username: yup
|
||||
.string()
|
||||
.min(3, t("common.error.too-short", { length: 3 })),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ const Body = ({
|
||||
password: yup
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 })),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -115,8 +115,8 @@ const Body = ({
|
||||
})
|
||||
.then(() =>
|
||||
toast.success(
|
||||
t("admin.users.edit.update.notify.password.success")
|
||||
)
|
||||
t("admin.users.edit.update.notify.password.success"),
|
||||
),
|
||||
)
|
||||
.catch(toast.axiosError);
|
||||
})}
|
||||
|
||||
@@ -76,7 +76,7 @@ const SignUpForm = () => {
|
||||
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) =>
|
||||
signUp(values.email, values.username, values.password)
|
||||
signUp(values.email, values.username, values.password),
|
||||
)}
|
||||
>
|
||||
<TextInput
|
||||
|
||||
@@ -44,6 +44,7 @@ const FilePreview = ({
|
||||
href={`/api/shares/${shareId}/files/${fileId}?download=false`}
|
||||
>
|
||||
View original file
|
||||
{/* Add translation? */}
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import CopyTextField from "../../upload/CopyTextField";
|
||||
const showCompletedReverseShareModal = (
|
||||
modals: ModalsContextProps,
|
||||
link: string,
|
||||
getReverseShares: () => void
|
||||
getReverseShares: () => void,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
|
||||
@@ -13,7 +13,9 @@ import { useForm } from "@mantine/form";
|
||||
import { useModals } from "@mantine/modals";
|
||||
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import useTranslate from "../../../hooks/useTranslate.hook";
|
||||
import useTranslate, {
|
||||
translateOutsideContext,
|
||||
} from "../../../hooks/useTranslate.hook";
|
||||
import shareService from "../../../services/share.service";
|
||||
import { getExpirationPreview } from "../../../utils/date.util";
|
||||
import toast from "../../../utils/toast.util";
|
||||
@@ -23,10 +25,11 @@ import showCompletedReverseShareModal from "./showCompletedReverseShareModal";
|
||||
const showCreateReverseShareModal = (
|
||||
modals: ModalsContextProps,
|
||||
showSendEmailNotificationOption: boolean,
|
||||
getReverseShares: () => void
|
||||
getReverseShares: () => void,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
title: "Create reverse share",
|
||||
title: t("account.reverseShares.modal.title"),
|
||||
children: (
|
||||
<Body
|
||||
showSendEmailNotificationOption={showSendEmailNotificationOption}
|
||||
@@ -64,7 +67,7 @@ const Body = ({
|
||||
values.expiration_num + values.expiration_unit,
|
||||
values.maxShareSize,
|
||||
values.maxUseCount,
|
||||
values.sendEmailNotification
|
||||
values.sendEmailNotification,
|
||||
)
|
||||
.then(({ link }) => {
|
||||
modals.closeAll();
|
||||
@@ -150,7 +153,7 @@ const Body = ({
|
||||
expiresOn: t("account.reverseShare.expires-on"),
|
||||
neverExpires: t("account.reverseShare.never-expires"),
|
||||
},
|
||||
form
|
||||
form,
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
@@ -174,7 +177,7 @@ const Body = ({
|
||||
labelPosition="left"
|
||||
label={t("account.reverseShares.modal.send-email")}
|
||||
description={t(
|
||||
"account.reverseShares.modal.send-email.description"
|
||||
"account.reverseShares.modal.send-email.description",
|
||||
)}
|
||||
{...form.getInputProps("sendEmailNotification", {
|
||||
type: "checkbox",
|
||||
|
||||
@@ -6,7 +6,7 @@ import FilePreview from "../FilePreview";
|
||||
const showFilePreviewModal = (
|
||||
shareId: string,
|
||||
file: FileMetaData,
|
||||
modals: ModalsContextProps
|
||||
modals: ModalsContextProps,
|
||||
) => {
|
||||
const mimeType = (mime.contentType(file.name) || "").split(";")[0];
|
||||
return modals.openModal({
|
||||
|
||||
@@ -8,7 +8,7 @@ import useTranslate, {
|
||||
|
||||
const showEnterPasswordModal = (
|
||||
modals: ModalsContextProps,
|
||||
submitCallback: (password: string) => Promise<void>
|
||||
submitCallback: (password: string) => Promise<void>,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
|
||||
@@ -7,7 +7,7 @@ import { FormattedMessage } from "react-intl";
|
||||
const showErrorModal = (
|
||||
modals: ModalsContextProps,
|
||||
title: string,
|
||||
text: string
|
||||
text: string,
|
||||
) => {
|
||||
return modals.openModal({
|
||||
closeOnClickOutside: false,
|
||||
|
||||
@@ -12,7 +12,7 @@ function CopyTextField(props: { link: string }) {
|
||||
const [checkState, setCheckState] = useState(false);
|
||||
const [textClicked, setTextClicked] = useState(false);
|
||||
const timerRef = useRef<number | ReturnType<typeof setTimeout> | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
|
||||
const copyLink = () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, Center, createStyles, Group, Text } from "@mantine/core";
|
||||
import { Dropzone as MantineDropzone } from "@mantine/dropzone";
|
||||
import { Dispatch, ForwardedRef, SetStateAction, useRef } from "react";
|
||||
import { ForwardedRef, useRef } from "react";
|
||||
import { TbCloudUpload, TbUpload } from "react-icons/tb";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import useTranslate from "../../hooks/useTranslate.hook";
|
||||
@@ -35,13 +35,11 @@ const useStyles = createStyles((theme) => ({
|
||||
const Dropzone = ({
|
||||
isUploading,
|
||||
maxShareSize,
|
||||
files,
|
||||
setFiles,
|
||||
showCreateUploadModalCallback,
|
||||
}: {
|
||||
isUploading: boolean;
|
||||
maxShareSize: number;
|
||||
files: FileUpload[];
|
||||
setFiles: Dispatch<SetStateAction<FileUpload[]>>;
|
||||
showCreateUploadModalCallback: (files: FileUpload[]) => void;
|
||||
}) => {
|
||||
const t = useTranslate();
|
||||
|
||||
@@ -55,11 +53,8 @@ const Dropzone = ({
|
||||
}}
|
||||
disabled={isUploading}
|
||||
openRef={openRef as ForwardedRef<() => void>}
|
||||
onDrop={(newFiles: FileUpload[]) => {
|
||||
const fileSizeSum = [...newFiles, ...files].reduce(
|
||||
(n, { size }) => n + size,
|
||||
0
|
||||
);
|
||||
onDrop={(files: FileUpload[]) => {
|
||||
const fileSizeSum = files.reduce((n, { size }) => n + size, 0);
|
||||
|
||||
if (fileSizeSum > maxShareSize) {
|
||||
toast.error(
|
||||
@@ -68,11 +63,11 @@ const Dropzone = ({
|
||||
})
|
||||
);
|
||||
} else {
|
||||
newFiles = newFiles.map((newFile) => {
|
||||
files = files.map((newFile) => {
|
||||
newFile.uploadingProgress = 0;
|
||||
return newFile;
|
||||
});
|
||||
setFiles([...newFiles, ...files]);
|
||||
showCreateUploadModalCallback(files);
|
||||
}
|
||||
}}
|
||||
className={classes.dropzone}
|
||||
|
||||
@@ -13,7 +13,7 @@ import CopyTextField from "../CopyTextField";
|
||||
const showCompletedUploadModal = (
|
||||
modals: ModalsContextProps,
|
||||
share: Share,
|
||||
appUrl: string
|
||||
appUrl: string,
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
return modals.openModal({
|
||||
|
||||
@@ -26,6 +26,7 @@ import useTranslate, {
|
||||
translateOutsideContext,
|
||||
} from "../../../hooks/useTranslate.hook";
|
||||
import shareService from "../../../services/share.service";
|
||||
import { FileUpload } from "../../../types/File.type";
|
||||
import { CreateShare } from "../../../types/share.type";
|
||||
import { getExpirationPreview } from "../../../utils/date.util";
|
||||
|
||||
@@ -38,7 +39,8 @@ const showCreateUploadModal = (
|
||||
allowUnauthenticatedShares: boolean;
|
||||
enableEmailRecepients: boolean;
|
||||
},
|
||||
uploadCallback: (createShare: CreateShare) => void
|
||||
files: FileUpload[],
|
||||
uploadCallback: (createShare: CreateShare, files: FileUpload[]) => void
|
||||
) => {
|
||||
const t = translateOutsideContext();
|
||||
|
||||
@@ -47,6 +49,7 @@ const showCreateUploadModal = (
|
||||
children: (
|
||||
<CreateUploadModalBody
|
||||
options={options}
|
||||
files={files}
|
||||
uploadCallback={uploadCallback}
|
||||
/>
|
||||
),
|
||||
@@ -55,9 +58,11 @@ const showCreateUploadModal = (
|
||||
|
||||
const CreateUploadModalBody = ({
|
||||
uploadCallback,
|
||||
files,
|
||||
options,
|
||||
}: {
|
||||
uploadCallback: (createShare: CreateShare) => void;
|
||||
files: FileUpload[];
|
||||
uploadCallback: (createShare: CreateShare, files: FileUpload[]) => void;
|
||||
options: {
|
||||
isUserSignedIn: boolean;
|
||||
isReverseShare: boolean;
|
||||
@@ -119,18 +124,21 @@ const CreateUploadModalBody = ({
|
||||
form.setFieldError("link", t("upload.modal.link.error.taken"));
|
||||
} else {
|
||||
const expiration = form.values.never_expires
|
||||
? t("upload.modal.expires.never")
|
||||
? "never"
|
||||
: form.values.expiration_num + form.values.expiration_unit;
|
||||
uploadCallback({
|
||||
id: values.link,
|
||||
expiration: expiration,
|
||||
recipients: values.recipients,
|
||||
description: values.description,
|
||||
security: {
|
||||
password: values.password,
|
||||
maxViews: values.maxViews,
|
||||
uploadCallback(
|
||||
{
|
||||
id: values.link,
|
||||
expiration: expiration,
|
||||
recipients: values.recipients,
|
||||
description: values.description,
|
||||
security: {
|
||||
password: values.password,
|
||||
maxViews: values.maxViews,
|
||||
},
|
||||
},
|
||||
});
|
||||
files
|
||||
);
|
||||
modals.closeAll();
|
||||
}
|
||||
})}
|
||||
@@ -140,7 +148,7 @@ const CreateUploadModalBody = ({
|
||||
<TextInput
|
||||
style={{ flex: "1" }}
|
||||
variant="filled"
|
||||
label="Link"
|
||||
label={t("upload.modal.link.label")}
|
||||
placeholder="myAwesomeShare"
|
||||
{...form.getInputProps("link")}
|
||||
/>
|
||||
@@ -283,8 +291,8 @@ const CreateUploadModalBody = ({
|
||||
data={form.values.recipients}
|
||||
placeholder={t("upload.modal.accordion.email.placeholder")}
|
||||
searchable
|
||||
{...form.getInputProps("recipients")}
|
||||
creatable
|
||||
autoComplete="email-recipients"
|
||||
getCreateLabel={(query) => `+ ${query}`}
|
||||
onCreate={(query) => {
|
||||
if (!query.match(/^\S+@\S+\.\S+$/)) {
|
||||
@@ -301,6 +309,7 @@ const CreateUploadModalBody = ({
|
||||
return query;
|
||||
}
|
||||
}}
|
||||
{...form.getInputProps("recipients")}
|
||||
/>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
@@ -318,6 +327,7 @@ const CreateUploadModalBody = ({
|
||||
"upload.modal.accordion.security.password.placeholder"
|
||||
)}
|
||||
label={t("upload.modal.accordion.security.password.label")}
|
||||
autoComplete="off"
|
||||
{...form.getInputProps("password")}
|
||||
/>
|
||||
<NumberInput
|
||||
@@ -334,7 +344,7 @@ const CreateUploadModalBody = ({
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
<Button type="submit">
|
||||
<Button type="submit" data-autofocus>
|
||||
<FormattedMessage id="common.button.share" />
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
@@ -7,7 +7,7 @@ const useTranslate = () => {
|
||||
return (
|
||||
id: string,
|
||||
values?: Parameters<typeof intl.formatMessage>[1],
|
||||
opts?: Parameters<typeof intl.formatMessage>[2]
|
||||
opts?: Parameters<typeof intl.formatMessage>[2],
|
||||
) => {
|
||||
return intl.formatMessage({ id }, values, opts) as string;
|
||||
};
|
||||
@@ -25,12 +25,12 @@ export const translateOutsideContext = () => {
|
||||
messages: i18nUtil.getLocaleByCode(locale)?.messages,
|
||||
defaultLocale: "en",
|
||||
},
|
||||
cache
|
||||
cache,
|
||||
);
|
||||
return (
|
||||
id: string,
|
||||
values?: Parameters<typeof intl.formatMessage>[1],
|
||||
opts?: Parameters<typeof intl.formatMessage>[2]
|
||||
opts?: Parameters<typeof intl.formatMessage>[2],
|
||||
) => {
|
||||
return intl.formatMessage({ id }, values, opts) as string;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,12 @@ import english from "./translations/en-US";
|
||||
import spanish from "./translations/es-ES";
|
||||
import finnish from "./translations/fi-FI";
|
||||
import french from "./translations/fr-FR";
|
||||
import japanese from "./translations/ja-JP";
|
||||
import dutch from "./translations/nl-BE";
|
||||
import portuguese from "./translations/pt-BR";
|
||||
import russian from "./translations/ru-RU";
|
||||
import serbian from "./translations/sr-SP";
|
||||
import thai from "./translations/th-TH";
|
||||
import chineseSimplified from "./translations/zh-CN";
|
||||
|
||||
export const LOCALES = {
|
||||
@@ -54,4 +58,24 @@ export const LOCALES = {
|
||||
code: "ru-RU",
|
||||
messages: russian,
|
||||
},
|
||||
THAI: {
|
||||
name: "ไทย",
|
||||
code: "th-TH",
|
||||
messages: thai,
|
||||
},
|
||||
SERBIAN: {
|
||||
name: "Srpski",
|
||||
code: "sr-SP",
|
||||
messages: serbian,
|
||||
},
|
||||
DUTCH: {
|
||||
name: "Nederlands",
|
||||
code: "nl-BE",
|
||||
messages: dutch,
|
||||
},
|
||||
JAPANESE: {
|
||||
name: "日本語",
|
||||
code: "ja-JP",
|
||||
messages: japanese,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -65,7 +65,7 @@ export default {
|
||||
"account.notify.password.success": "Adgangskoden er ændret",
|
||||
"account.card.security.title": "Sikkerhed",
|
||||
"account.card.security.totp.enable.description": "Indtast din nuværende adgangskode for at begynde opsætningen af 2-faktor login",
|
||||
"account.card.security.totp.disable.description": "Enter your current password to disable TOTP",
|
||||
"account.card.security.totp.disable.description": "Indtast din nuværende adgangskode for at begynde opsætningen af 2-faktor login",
|
||||
"account.card.security.totp.button.start": "Start",
|
||||
"account.modal.totp.title": "Aktiver 2-faktor login",
|
||||
"account.modal.totp.step1": "Trin 1: Tilføj din 2-faktor Authenticator",
|
||||
@@ -102,7 +102,7 @@ export default {
|
||||
"account.shares.table.size": "Størrelse",
|
||||
"account.shares.modal.share-informations": "Share informations",
|
||||
"account.shares.modal.share-link": "Del link",
|
||||
"account.shares.modal.delete.title": "Delete share {share}",
|
||||
"account.shares.modal.delete.title": "Slet share {share}",
|
||||
"account.shares.modal.delete.description": "Ønsker du virkelig at slette denne deling?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "Der er tomt her 👀",
|
||||
"account.reverseShares.description.empty": "You don't have any reverse shares.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Udløb",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minut",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutter",
|
||||
@@ -133,12 +134,12 @@ export default {
|
||||
"account.reverseShare.expires-on": "Denne omvendte deling udløber den {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "Der er ikke oprettet nogle delinger endnu",
|
||||
"account.reverseShares.table.count.singular": "del",
|
||||
"account.reverseShares.table.count.plural": "delinger",
|
||||
"account.reverseShares.table.count.plural": "shares",
|
||||
"account.reverseShares.table.shares": "Delinger",
|
||||
"account.reverseShares.table.remaining": "Resterende anvendelser",
|
||||
"account.reverseShares.table.max-size": "Maksimal størrelse for deling",
|
||||
"account.reverseShares.table.expires": "Udløber d",
|
||||
"account.reverseShares.modal.reverse-share-link": "Omvendt deling",
|
||||
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
|
||||
"account.reverseShares.modal.delete.title": "Delete reverse share",
|
||||
"account.reverseShares.modal.delete.description": "Ønsker du virkelig at slette denne omvendte deling? Hvis du gør det, vil de tilknyttede delinger også blive slettet.",
|
||||
// END /account/reverseShares
|
||||
@@ -168,7 +169,7 @@ export default {
|
||||
"admin.users.modal.create.password": "Adgangskode",
|
||||
"admin.users.modal.create.manual-password": "Angiv adgangskode manuelt",
|
||||
"admin.users.modal.create.manual-password.description": "If not checked, the user will receive an email with a link to set their password.",
|
||||
"admin.users.modal.create.admin": "Admin privileges",
|
||||
"admin.users.modal.create.admin": "Admin rettigheder",
|
||||
"admin.users.modal.create.admin.description": "If checked, the user will be able to access the admin panel.",
|
||||
// END /admin/users
|
||||
// /upload
|
||||
@@ -205,7 +206,7 @@ export default {
|
||||
"upload.modal.expires.year-singular": "År",
|
||||
"upload.modal.expires.year-plural": "År",
|
||||
"upload.modal.accordion.description.title": "Beskrivelse",
|
||||
"upload.modal.accordion.description.placeholder": "Note for the recipients of this share",
|
||||
"upload.modal.accordion.description.placeholder": "Bemærkning til modtagerne af dette share",
|
||||
"upload.modal.accordion.email.title": "E-mail modtagere",
|
||||
"upload.modal.accordion.email.placeholder": "Indtast e-mail modtagere",
|
||||
"upload.modal.accordion.email.invalid-email": "Ugyldig e-mailadresse",
|
||||
@@ -295,7 +296,7 @@ export default {
|
||||
"admin.config.smtp.button.test": "Send test e-mail",
|
||||
// 404
|
||||
"404.description": "Ups! Denne side findes ikke.",
|
||||
"404.button.home": "Bring me back home",
|
||||
"404.button.home": "Gå tilbage",
|
||||
// Common translations
|
||||
"common.button.save": "Gem",
|
||||
"common.button.create": "Opret",
|
||||
|
||||
@@ -64,8 +64,8 @@ export default {
|
||||
"account.card.password.new": "Neues Passwort",
|
||||
"account.notify.password.success": "Passwort erfolgreich geändert",
|
||||
"account.card.security.title": "Sicherheit",
|
||||
"account.card.security.totp.enable.description": "Geben dein aktuelles Passwort ein, um TOTP zu aktivieren",
|
||||
"account.card.security.totp.disable.description": "Geben dein aktuelles Passwort ein, um TOTP zu deaktivieren",
|
||||
"account.card.security.totp.enable.description": "Gib dein aktuelles Passwort ein, um TOTP zu aktivieren",
|
||||
"account.card.security.totp.disable.description": "Gib dein aktuelles Passwort ein, um TOTP zu deaktivieren",
|
||||
"account.card.security.totp.button.start": "Starten",
|
||||
"account.modal.totp.title": "TOTP aktivieren",
|
||||
"account.modal.totp.step1": "Schritt 1: Füge deinen Authenticator hinzu",
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "Es ist leer hier 👀",
|
||||
"account.reverseShares.description.empty": "Du hast keine externen Freigaben erstellt.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Externe Freigabe erstellen",
|
||||
"account.reverseShares.modal.expiration.label": "Gültig bis",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minute",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minuten",
|
||||
@@ -177,7 +178,7 @@ export default {
|
||||
"upload.notify.count-failed": "{count} Dateien konnten nicht hochgeladen werden. Wird erneut versucht.",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "Dateien hochladen",
|
||||
"upload.dropzone.description": "Ziehen Sie Dateien hierher, um Ihre Freigabe zu starten. Wir können nur Dateien akzeptieren, die insgesamt weniger als {maxSize} groß sind.",
|
||||
"upload.dropzone.description": "Ziehe Dateien hierher, um deine Freigabe zu starten. Wir können nur Dateien akzeptieren, die insgesamt weniger als {maxSize} groß sind.",
|
||||
"upload.dropzone.notify.file-too-big": "Ihre Dateien überschreiten die maximale Freigabegröße von {maxSize}.",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "Name",
|
||||
@@ -228,7 +229,7 @@ export default {
|
||||
"share.error.not-found.title": "Freigabe nicht gefunden",
|
||||
"share.error.not-found.description": "Die gesuchte Freigabe existiert nicht.",
|
||||
"share.modal.password.title": "Passwort erforderlich",
|
||||
"share.modal.password.description": "Um auf diese Freigabe zuzugreifen, geben Sie bitte das Passwort für die Freigabe ein.",
|
||||
"share.modal.password.description": "Um auf diese Freigabe zuzugreifen, gib bitte das Passwort für die Freigabe ein.",
|
||||
"share.modal.password": "Passwort",
|
||||
"share.modal.error.invalid-password": "Ungültiges Passwort",
|
||||
"share.button.download-all": "Alles herunterladen",
|
||||
@@ -237,7 +238,7 @@ export default {
|
||||
"share.table.name": "Name",
|
||||
"share.table.size": "Größe",
|
||||
"share.modal.file-preview.error.not-supported.title": "Vorschau wird nicht unterstützt",
|
||||
"share.modal.file-preview.error.not-supported.description": "Eine Vorschau für diesen Dateityp wird nicht unterstützt. Bitte laden Sie die Datei herunter, um sie anzuzeigen.",
|
||||
"share.modal.file-preview.error.not-supported.description": "Eine Vorschau für diesen Dateityp wird nicht unterstützt. Bitte lade die Datei herunter, um sie anzuzeigen.",
|
||||
// END /share/[id]
|
||||
// /admin/config
|
||||
"admin.config.title": "Einstellungen",
|
||||
@@ -279,7 +280,7 @@ export default {
|
||||
"admin.config.share.max-size": "Maximale Größe",
|
||||
"admin.config.share.max-size.description": "Maximale Größe einer Freigabe in Bytes",
|
||||
"admin.config.share.zip-compression-level": "Zip Komprimierungsstufe",
|
||||
"admin.config.share.zip-compression-level.description": "Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
|
||||
"admin.config.share.zip-compression-level.description": "Passe den Wert an, um ein Gleichgewicht zwischen Dateigröße und Komprimierungsgeschwindigkeit herzustellen. Gültige Werte liegen zwischen 0 und 9, wobei 0 für keine Komprimierung und 9 für maximale Komprimierung steht.",
|
||||
"admin.config.smtp.enabled": "Aktiviert",
|
||||
"admin.config.smtp.enabled.description": "Gibt an, ob SMTP aktiviert ist. Aktiviere dies nur, wenn Du den Host, den Port, die Email, den Benutzernamen und das Passwort deines SMTP-Servers eingegeben hast.",
|
||||
"admin.config.smtp.host": "Host",
|
||||
|
||||
@@ -149,6 +149,7 @@ export default {
|
||||
"You don't have any reverse shares.",
|
||||
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Expiration",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minute",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutes",
|
||||
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
"navbar.home": "Inicio",
|
||||
"navbar.signup": "Registrarse",
|
||||
"navbar.links.shares": "Mis compartidos",
|
||||
"navbar.links.reverse": "Reverse Shares",
|
||||
"navbar.links.reverse": "Comparticiones inversas",
|
||||
"navbar.avatar.account": "Mi cuenta",
|
||||
"navbar.avatar.admin": "Administración",
|
||||
"navbar.avatar.signout": "Cerrar sesión",
|
||||
@@ -106,11 +106,12 @@ export default {
|
||||
"account.shares.modal.delete.description": "¿Seguro que quieres eliminar este compartido?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Reverse Shares",
|
||||
"account.reverseShares.description": "Un Reverse Share te permite generar una URL única con la que usuarios externos pueden compartir archivos.",
|
||||
"account.reverseShares.title": "Comparticiones inversas",
|
||||
"account.reverseShares.description": "Una compartición inversa te permite generar una URL única con la que usuarios externos pueden compartir archivos.",
|
||||
"account.reverseShares.title.empty": "Aquí está vacío 👀",
|
||||
"account.reverseShares.description.empty": "No tienes ningún Reverse Share.",
|
||||
"account.reverseShares.description.empty": "No tienes ninguna compartición inversa.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Crear compartición inversa",
|
||||
"account.reverseShares.modal.expiration.label": "Expiración",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minuto",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutos",
|
||||
@@ -126,11 +127,11 @@ export default {
|
||||
"account.reverseShares.modal.expiration.year-plural": "Años",
|
||||
"account.reverseShares.modal.max-size.label": "Tamaño máximo del compartido",
|
||||
"account.reverseShares.modal.send-email": "Enviar notificación por correo",
|
||||
"account.reverseShares.modal.send-email.description": "Enviar una notificación por correo cuando se comparta algo con este enlace de Reverse Share.",
|
||||
"account.reverseShares.modal.send-email.description": "Enviar una notificación por correo cuando se comparta algo con este enlace de compartición inversa.",
|
||||
"account.reverseShares.modal.max-use.label": "Máximo de usos",
|
||||
"account.reverseShares.modal.max-use.description": "Cantidad máxima de veces que esta URL se puede usar para crear un compartido.",
|
||||
"account.reverseShare.never-expires": "Esta Reverse Share nunca expirará.",
|
||||
"account.reverseShare.expires-on": "Esta Reverse Share expirará en {expiration}.",
|
||||
"account.reverseShare.never-expires": "Esta compartición inversa nunca expirará.",
|
||||
"account.reverseShare.expires-on": "Esta compartición inversa expirará en {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "Todavía no se han creado compartidos",
|
||||
"account.reverseShares.table.count.singular": "compartido",
|
||||
"account.reverseShares.table.count.plural": "compartidos",
|
||||
@@ -138,9 +139,9 @@ export default {
|
||||
"account.reverseShares.table.remaining": "Usos restantes",
|
||||
"account.reverseShares.table.max-size": "Tamaño máximo del compartido",
|
||||
"account.reverseShares.table.expires": "Expira en",
|
||||
"account.reverseShares.modal.reverse-share-link": "Enlace de Reverse Share",
|
||||
"account.reverseShares.modal.delete.title": "Eliminar Reverse Share",
|
||||
"account.reverseShares.modal.delete.description": "¿Seguro que quieres eliminar esta Reverse Share? Si lo haces, todos los archivos asociados también serán eliminados.",
|
||||
"account.reverseShares.modal.reverse-share-link": "Enlace de compartición inversa",
|
||||
"account.reverseShares.modal.delete.title": "Eliminar compartición inversa",
|
||||
"account.reverseShares.modal.delete.description": "¿Seguro que quieres eliminar esta compartición inversa? Si lo haces, todos los archivos asociados también serán eliminados.",
|
||||
// END /account/reverseShares
|
||||
// /admin
|
||||
"admin.title": "Administración",
|
||||
@@ -260,10 +261,10 @@ export default {
|
||||
"admin.config.email.share-recipients-subject.description": "Asunto del correo el cual es enviado al destinatario del compartido.",
|
||||
"admin.config.email.share-recipients-message": "Mensaje destinatario",
|
||||
"admin.config.email.share-recipients-message.description": "Mensaje el cual es enviado al destinatario del compartido. Variables disponibles:\n{creator} - Nombre del creador del compartido\n {shareUrl} - URL del compartido\n {desc} - Descripción del compartido\n {expires} - Fecha de expiración del compartido\nLas variables serán remplazadas con los valores reales.",
|
||||
"admin.config.email.reverse-share-subject": "Asunto del Reverse Share",
|
||||
"admin.config.email.reverse-share-subject.description": "Asunto del correo el cual se envía cuando alguien comparte algo con tu enlace de Reverse Share.",
|
||||
"admin.config.email.reverse-share-message": "Mensaje del Reverse Share",
|
||||
"admin.config.email.reverse-share-message.description": "Mensaje que se envía cuando alguien comparte algo con tu enlace de Reverse Share. {shareUrl} Se remplazará con el nombre del creador y la URL del compartido.",
|
||||
"admin.config.email.reverse-share-subject": "Asunto de la compartición inversa",
|
||||
"admin.config.email.reverse-share-subject.description": "Asunto del correo el cual se envía cuando alguien comparte algo con tu enlace de compartición inversa.",
|
||||
"admin.config.email.reverse-share-message": "Mensaje de la compartición inversa",
|
||||
"admin.config.email.reverse-share-message.description": "Mensaje que se envía cuando alguien comparte algo con tu enlace de compartición inversa. {shareUrl} Se remplazará con el nombre del creador y la URL del compartido.",
|
||||
"admin.config.email.reset-password-subject": "Asunto restablecer contraseña",
|
||||
"admin.config.email.reset-password-subject.description": "Asunto del correo que se envía cuando un usuario solicita restablecer la contraseña.",
|
||||
"admin.config.email.reset-password-message": "Mensaje restablecer contraseña",
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "Täällä on tyhjää 👀",
|
||||
"account.reverseShares.description.empty": "Sinulla ei ole käänteisiä jakoja.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Vanhentuminen",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minuutti",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minuuttia",
|
||||
@@ -258,7 +259,7 @@ export default {
|
||||
"admin.config.email.enable-share-email-recipients.description": "Salli sähköpostien jakaminen vastaanottajille. Ota tämä käyttöön vain, jos olet ottanut SMTP:n käyttöön.",
|
||||
"admin.config.email.share-recipients-subject": "Sähköpostijaon otsikko",
|
||||
"admin.config.email.share-recipients-subject.description": "Sähköpostin aihe, joka saa lähetetään jaon vastaanottajille.",
|
||||
"admin.config.email.share-recipients-message": "Sähköpostijaon otsikko",
|
||||
"admin.config.email.share-recipients-message": "Sähköpostijaon viesti",
|
||||
"admin.config.email.share-recipients-message.description": "Viesti joka lähetetään jaon vastaanottajille. Saatavilla olevat muuttujat:\n {creator} - Jaon luojan käyttäjänimi\n {shareUrl} - Jaon URL\n {desc} - Jaon kuvaus\n {expires} - Jaon päättymispäivä\n Muuttujat korvataan todellisella arvolla.",
|
||||
"admin.config.email.reverse-share-subject": "Käänteisen jaon aihe",
|
||||
"admin.config.email.reverse-share-subject.description": "Aihe sähköpostin joka lähetetään kun joku loi jaon käänteisen jakolinkin kanssa.",
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "Il n'y a rien ici 👀",
|
||||
"account.reverseShares.description.empty": "Vous n'avez aucun partage inversé.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Créer un partage inversé",
|
||||
"account.reverseShares.modal.expiration.label": "Expiration",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minute",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutes",
|
||||
|
||||
324
frontend/src/i18n/translations/ja-JP.ts
Normal file
324
frontend/src/i18n/translations/ja-JP.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
export default {
|
||||
// Navbar
|
||||
"navbar.upload": "アップロード",
|
||||
"navbar.signin": "サインイン",
|
||||
"navbar.home": "ホーム",
|
||||
"navbar.signup": "会員登録",
|
||||
"navbar.links.shares": "自分の共有",
|
||||
"navbar.links.reverse": "自分と共有",
|
||||
"navbar.avatar.account": "マイアカウント",
|
||||
"navbar.avatar.admin": "管理画面",
|
||||
"navbar.avatar.signout": "サインアウト",
|
||||
// END navbar
|
||||
// /
|
||||
"home.title": "<h>セルフホスト</h>のファイル共有プラットフォーム。",
|
||||
"home.description": "WeTransferのようなサードパーティーサービスに自分のファイルを渡したいですか?",
|
||||
"home.bullet.a.name": "セルフホスト",
|
||||
"home.bullet.a.description": "Pingvin Shareをあなたのマシンでホストしましょう。",
|
||||
"home.bullet.b.name": "プライバシー",
|
||||
"home.bullet.b.description": "あなたのファイルはあなたのものであり、決して第三者の手に入るべきではありません。",
|
||||
"home.bullet.c.name": "ファイルサイズ制限に悩まされることはありません",
|
||||
"home.bullet.c.description": "大きなファイルもアップロード。ストレージのサイズだけが唯一の制限です。",
|
||||
"home.button.start": "始めましょう",
|
||||
"home.button.source": "ソースコード",
|
||||
// END /
|
||||
// /auth/signin
|
||||
"signin.title": "おかえりなさい",
|
||||
"signin.description": "アカウントをお持ちではありませんか?",
|
||||
"signin.button.signup": "会員登録",
|
||||
"signin.input.email-or-username": "メールアドレスまたはユーザー名",
|
||||
"signin.input.email-or-username.placeholder": "メールアドレスまたはユーザー名",
|
||||
"signin.input.password": "パスワード",
|
||||
"signin.input.password.placeholder": "あなたのパスワード",
|
||||
"signin.button.submit": "サインイン",
|
||||
"signIn.notify.totp-required.title": "二段階認証が必要です",
|
||||
"signIn.notify.totp-required.description": "二段階認証コードを入力してください",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "アカウントを作成",
|
||||
"signup.description": "既にアカウントをお持ちですか?",
|
||||
"signup.button.signin": "サインイン",
|
||||
"signup.input.username": "ユーザー名",
|
||||
"signup.input.username.placeholder": "あなたのユーザー名",
|
||||
"signup.input.email": "メールアドレス",
|
||||
"signup.input.email.placeholder": "あなたのメールアドレス",
|
||||
"signup.button.submit": "さあ始めましょう",
|
||||
// END /auth/signup
|
||||
// /auth/reset-password
|
||||
"resetPassword.title": "パスワードを忘れてしまいましたか?",
|
||||
"resetPassword.description": "登録しているメールアドレスを入力してください。",
|
||||
"resetPassword.notify.success": "パスワードをリセットするためのリンクをメールで送信しました。",
|
||||
"resetPassword.button.back": "サインインページに戻る",
|
||||
"resetPassword.text.resetPassword": "パスワードをリセット",
|
||||
"resetPassword.text.enterNewPassword": "新規パスワードを入力",
|
||||
"resetPassword.input.password": "新規パスワード",
|
||||
"resetPassword.notify.passwordReset": "Your password has been reset successfully.",
|
||||
// /account
|
||||
"account.title": "My account",
|
||||
"account.card.info.title": "Account info",
|
||||
"account.card.info.username": "Username",
|
||||
"account.card.info.email": "Email",
|
||||
"account.notify.info.success": "Account updated successfully",
|
||||
"account.card.password.title": "Password",
|
||||
"account.card.password.old": "Old password",
|
||||
"account.card.password.new": "New password",
|
||||
"account.notify.password.success": "Password changed successfully",
|
||||
"account.card.security.title": "Security",
|
||||
"account.card.security.totp.enable.description": "Enter your current password to start enabling TOTP",
|
||||
"account.card.security.totp.disable.description": "Enter your current password to disable TOTP",
|
||||
"account.card.security.totp.button.start": "Start",
|
||||
"account.modal.totp.title": "Enable TOTP",
|
||||
"account.modal.totp.step1": "Step 1: Add your authenticator",
|
||||
"account.modal.totp.step2": "Step 2: Validate your code",
|
||||
"account.modal.totp.enterManually": "Enter manually",
|
||||
"account.modal.totp.code": "Code",
|
||||
"account.modal.totp.clickToCopy": "Click to copy",
|
||||
"account.modal.totp.verify": "Verify",
|
||||
"account.notify.totp.disable": "TOTP disabled successfully",
|
||||
"account.notify.totp.enable": "TOTP enabled successfully",
|
||||
"account.card.language.title": "Language",
|
||||
"account.card.language.description": "The project is translated by the community. Some languages might be incomplete.",
|
||||
"account.card.color.title": "Color scheme",
|
||||
// ThemeSwitcher.tsx
|
||||
"account.theme.dark": "Dark",
|
||||
"account.theme.light": "Light",
|
||||
"account.theme.system": "System",
|
||||
"account.button.delete": "Delete Account",
|
||||
"account.modal.delete.title": "Delete Account",
|
||||
"account.modal.delete.description": "Do you really want to delete your account including all your active shares?",
|
||||
// END /account
|
||||
// /account/shares
|
||||
"account.shares.title": "My shares",
|
||||
"account.shares.title.empty": "It's empty here 👀",
|
||||
"account.shares.description.empty": "You don't have any shares.",
|
||||
"account.shares.button.create": "Create one",
|
||||
"account.shares.info.title": "Share informations",
|
||||
"account.shares.table.id": "ID",
|
||||
"account.shares.table.name": "Name",
|
||||
"account.shares.table.description": "Description",
|
||||
"account.shares.table.visitors": "Visitors",
|
||||
"account.shares.table.expiresAt": "Expires at",
|
||||
"account.shares.table.createdAt": "Created at",
|
||||
"account.shares.table.size": "Size",
|
||||
"account.shares.modal.share-informations": "Share informations",
|
||||
"account.shares.modal.share-link": "Share link",
|
||||
"account.shares.modal.delete.title": "Delete share {share}",
|
||||
"account.shares.modal.delete.description": "Do you really want to delete this share?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Reverse shares",
|
||||
"account.reverseShares.description": "A reverse share allows you to generate a unique URL that allows external users to create a share.",
|
||||
"account.reverseShares.title.empty": "It's empty here 👀",
|
||||
"account.reverseShares.description.empty": "You don't have any reverse shares.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Expiration",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minute",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutes",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "Hour",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "Hours",
|
||||
"account.reverseShares.modal.expiration.day-singular": "Day",
|
||||
"account.reverseShares.modal.expiration.day-plural": "Days",
|
||||
"account.reverseShares.modal.expiration.week-singular": "Week",
|
||||
"account.reverseShares.modal.expiration.week-plural": "Weeks",
|
||||
"account.reverseShares.modal.expiration.month-singular": "Month",
|
||||
"account.reverseShares.modal.expiration.month-plural": "Months",
|
||||
"account.reverseShares.modal.expiration.year-singular": "Year",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Years",
|
||||
"account.reverseShares.modal.max-size.label": "Max share size",
|
||||
"account.reverseShares.modal.send-email": "Send email notification",
|
||||
"account.reverseShares.modal.send-email.description": "Send an email notification when a share is created with this reverse share link.",
|
||||
"account.reverseShares.modal.max-use.label": "Max uses",
|
||||
"account.reverseShares.modal.max-use.description": "The maximum amount of times this URL can be used to create a share.",
|
||||
"account.reverseShare.never-expires": "This reverse share will never expire.",
|
||||
"account.reverseShare.expires-on": "This reverse share will expire on {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "No shares created yet",
|
||||
"account.reverseShares.table.count.singular": "share",
|
||||
"account.reverseShares.table.count.plural": "shares",
|
||||
"account.reverseShares.table.shares": "Shares",
|
||||
"account.reverseShares.table.remaining": "Remaining uses",
|
||||
"account.reverseShares.table.max-size": "Max share size",
|
||||
"account.reverseShares.table.expires": "Expires at",
|
||||
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
|
||||
"account.reverseShares.modal.delete.title": "Delete reverse share",
|
||||
"account.reverseShares.modal.delete.description": "Do you really want to delete this reverse share? If you do, the associated shares will be deleted as well.",
|
||||
// END /account/reverseShares
|
||||
// /admin
|
||||
"admin.title": "Administration",
|
||||
"admin.button.users": "User management",
|
||||
"admin.button.config": "Configuration",
|
||||
"admin.version": "Version",
|
||||
// END /admin
|
||||
// /admin/users
|
||||
"admin.users.title": "User management",
|
||||
"admin.users.table.username": "Username",
|
||||
"admin.users.table.email": "Email",
|
||||
"admin.users.table.admin": "Admin",
|
||||
"admin.users.edit.update.title": "Update user {username}",
|
||||
"admin.users.edit.update.admin-privileges": "Admin privileges",
|
||||
"admin.users.edit.update.change-password.title": "Change password",
|
||||
"admin.users.edit.update.change-password.field": "New password",
|
||||
"admin.users.edit.update.change-password.button": "Save new password",
|
||||
"admin.users.edit.update.notify.password.success": "Password changed successfully",
|
||||
"admin.users.edit.delete.title": "Delete user {username}",
|
||||
"admin.users.edit.delete.description": "Do you really want to delete this user and all his shares?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "Create user",
|
||||
"admin.users.modal.create.username": "Username",
|
||||
"admin.users.modal.create.email": "Email",
|
||||
"admin.users.modal.create.password": "Password",
|
||||
"admin.users.modal.create.manual-password": "Set password manually",
|
||||
"admin.users.modal.create.manual-password.description": "If not checked, the user will receive an email with a link to set their password.",
|
||||
"admin.users.modal.create.admin": "Admin privileges",
|
||||
"admin.users.modal.create.admin.description": "If checked, the user will be able to access the admin panel.",
|
||||
// END /admin/users
|
||||
// /upload
|
||||
"upload.title": "Upload",
|
||||
"upload.notify.generic-error": "An error occurred while finishing your share.",
|
||||
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "Upload files",
|
||||
"upload.dropzone.description": "Drag'n'drop files here to start your share. We can accept only files that are less than {maxSize} in total.",
|
||||
"upload.dropzone.notify.file-too-big": "Your files exceed the maximum share size of {maxSize}.",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "Name",
|
||||
"upload.filelist.size": "Size",
|
||||
// showCreateUploadModal.tsx
|
||||
"upload.modal.title": "Create Share",
|
||||
"upload.modal.link.error.invalid": "Can only contain letters, numbers, underscores, and hyphens",
|
||||
"upload.modal.link.error.taken": "This link is already in use",
|
||||
"upload.modal.not-signed-in": "You're not signed in",
|
||||
"upload.modal.not-signed-in-description": "You will be unable to delete your share manually and view the visitor count.",
|
||||
"upload.modal.expires.never": "never",
|
||||
"upload.modal.expires.never-long": "Never Expires",
|
||||
"upload.modal.link.label": "Link",
|
||||
"upload.modal.expires.label": "Expiration",
|
||||
"upload.modal.expires.minute-singular": "Minute",
|
||||
"upload.modal.expires.minute-plural": "Minutes",
|
||||
"upload.modal.expires.hour-singular": "Hour",
|
||||
"upload.modal.expires.hour-plural": "Hours",
|
||||
"upload.modal.expires.day-singular": "Day",
|
||||
"upload.modal.expires.day-plural": "Days",
|
||||
"upload.modal.expires.week-singular": "Week",
|
||||
"upload.modal.expires.week-plural": "Weeks",
|
||||
"upload.modal.expires.month-singular": "Month",
|
||||
"upload.modal.expires.month-plural": "Months",
|
||||
"upload.modal.expires.year-singular": "Year",
|
||||
"upload.modal.expires.year-plural": "Years",
|
||||
"upload.modal.accordion.description.title": "Description",
|
||||
"upload.modal.accordion.description.placeholder": "Note for the recipients of this share",
|
||||
"upload.modal.accordion.email.title": "Email recipients",
|
||||
"upload.modal.accordion.email.placeholder": "Enter email recipients",
|
||||
"upload.modal.accordion.email.invalid-email": "Invalid email address",
|
||||
"upload.modal.accordion.security.title": "Security options",
|
||||
"upload.modal.accordion.security.password.label": "Password protection",
|
||||
"upload.modal.accordion.security.password.placeholder": "No password",
|
||||
"upload.modal.accordion.security.max-views.label": "Maximum views",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "No limit",
|
||||
// showCompletedUploadModal.tsx
|
||||
"upload.modal.completed.never-expires": "This share will never expire.",
|
||||
"upload.modal.completed.expires-on": "This share will expire on {expiration}.",
|
||||
"upload.modal.completed.share-ready": "Share ready",
|
||||
// END /upload
|
||||
// /share/[id]
|
||||
"share.title": "Share {shareId}",
|
||||
"share.description": "Look what I've shared with you!",
|
||||
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
|
||||
"share.error.visitor-limit-exceeded.description": "The visitor limit from this share has been exceeded.",
|
||||
"share.error.removed.title": "Share removed",
|
||||
"share.error.not-found.title": "Share not found",
|
||||
"share.error.not-found.description": "The share you're looking for doesn't exist.",
|
||||
"share.modal.password.title": "Password required",
|
||||
"share.modal.password.description": "To access this share please enter the password for the share.",
|
||||
"share.modal.password": "Password",
|
||||
"share.modal.error.invalid-password": "Invalid password",
|
||||
"share.button.download-all": "Download all",
|
||||
"share.notify.download-all-preparing": "The share is preparing. Try again in a few minutes.",
|
||||
"share.modal.file-link": "File link",
|
||||
"share.table.name": "Name",
|
||||
"share.table.size": "Size",
|
||||
"share.modal.file-preview.error.not-supported.title": "Preview not supported",
|
||||
"share.modal.file-preview.error.not-supported.description": "A preview for thise file type is unsupported. Please download the file to view it.",
|
||||
// END /share/[id]
|
||||
// /admin/config
|
||||
"admin.config.title": "Configuration",
|
||||
"admin.config.category.general": "General",
|
||||
"admin.config.category.share": "Share",
|
||||
"admin.config.category.email": "Email",
|
||||
"admin.config.category.smtp": "SMTP",
|
||||
"admin.config.general.app-name": "App name",
|
||||
"admin.config.general.app-name.description": "Name of the application",
|
||||
"admin.config.general.app-url": "App URL",
|
||||
"admin.config.general.app-url.description": "On which URL Pingvin Share is available",
|
||||
"admin.config.general.show-home-page": "Show home page",
|
||||
"admin.config.general.show-home-page.description": "Whether to show the home page",
|
||||
"admin.config.general.logo": "Logo",
|
||||
"admin.config.general.logo.description": "Change your logo by uploading a new image. The image must be a PNG and should have the format 1:1.",
|
||||
"admin.config.general.logo.placeholder": "Pick image",
|
||||
"admin.config.email.enable-share-email-recipients": "Enable share email recipients",
|
||||
"admin.config.email.enable-share-email-recipients.description": "Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
|
||||
"admin.config.email.share-recipients-subject": "Share recipients subject",
|
||||
"admin.config.email.share-recipients-subject.description": "Subject of the email which gets sent to the share recipients.",
|
||||
"admin.config.email.share-recipients-message": "Share recipients message",
|
||||
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n The variables will be replaced with the actual value.",
|
||||
"admin.config.email.reverse-share-subject": "Reverse share subject",
|
||||
"admin.config.email.reverse-share-subject.description": "Subject of the email which gets sent when someone created a share with your reverse share link.",
|
||||
"admin.config.email.reverse-share-message": "Reverse share message",
|
||||
"admin.config.email.reverse-share-message.description": "Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
|
||||
"admin.config.email.reset-password-subject": "Reset password subject",
|
||||
"admin.config.email.reset-password-subject.description": "Subject of the email which gets sent when a user requests a password reset.",
|
||||
"admin.config.email.reset-password-message": "Reset password message",
|
||||
"admin.config.email.reset-password-message.description": "Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
|
||||
"admin.config.email.invite-subject": "Invite subject",
|
||||
"admin.config.email.invite-subject.description": "Subject of the email which gets sent when an admin invites a user.",
|
||||
"admin.config.email.invite-message": "Invite message",
|
||||
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL and {password} with the password.",
|
||||
"admin.config.share.allow-registration": "Allow registration",
|
||||
"admin.config.share.allow-registration.description": "Whether registration is allowed",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Allow unauthenticated shares",
|
||||
"admin.config.share.allow-unauthenticated-shares.description": "Whether unauthenticated users can create shares",
|
||||
"admin.config.share.max-size": "Max size",
|
||||
"admin.config.share.max-size.description": "Maximum share size in bytes",
|
||||
"admin.config.share.zip-compression-level": "Zip compression level",
|
||||
"admin.config.share.zip-compression-level.description": "Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
|
||||
"admin.config.smtp.enabled": "Enabled",
|
||||
"admin.config.smtp.enabled.description": "Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
|
||||
"admin.config.smtp.host": "Host",
|
||||
"admin.config.smtp.host.description": "Host of the SMTP server",
|
||||
"admin.config.smtp.port": "Port",
|
||||
"admin.config.smtp.port.description": "Port of the SMTP server",
|
||||
"admin.config.smtp.email": "Email",
|
||||
"admin.config.smtp.email.description": "Email address which the emails get sent from",
|
||||
"admin.config.smtp.username": "Username",
|
||||
"admin.config.smtp.username.description": "Username of the SMTP server",
|
||||
"admin.config.smtp.password": "Password",
|
||||
"admin.config.smtp.password.description": "Password of the SMTP server",
|
||||
"admin.config.smtp.button.test": "Send test email",
|
||||
// 404
|
||||
"404.description": "Oops this page doesn't exist.",
|
||||
"404.button.home": "Bring me back home",
|
||||
// Common translations
|
||||
"common.button.save": "Save",
|
||||
"common.button.create": "Create",
|
||||
"common.button.submit": "Submit",
|
||||
"common.button.delete": "Delete",
|
||||
"common.button.cancel": "Cancel",
|
||||
"common.button.confirm": "Confirm",
|
||||
"common.button.disable": "Disable",
|
||||
"common.button.share": "Share",
|
||||
"common.button.generate": "Generate",
|
||||
"common.button.done": "Done",
|
||||
"common.text.link": "Link",
|
||||
"common.text.or": "or",
|
||||
"common.button.go-back": "Go back",
|
||||
"common.notify.copied": "Your link was copied to the clipboard",
|
||||
"common.success": "Success",
|
||||
"common.error": "Error",
|
||||
"common.error.unknown": "An unknown error occurred",
|
||||
"common.error.invalid-email": "Invalid email address",
|
||||
"common.error.too-short": "Must be at least {length} characters",
|
||||
"common.error.too-long": "Must be at most {length} characters",
|
||||
"common.error.exact-length": "Must be exactly {length} characters",
|
||||
"common.error.invalid-number": "Must be a number",
|
||||
"common.error.field-required": "This field is required"
|
||||
};
|
||||
324
frontend/src/i18n/translations/nl-BE.ts
Normal file
324
frontend/src/i18n/translations/nl-BE.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
export default {
|
||||
// Navbar
|
||||
"navbar.upload": "Verzenden",
|
||||
"navbar.signin": "Aanmelden",
|
||||
"navbar.home": "Startpagina",
|
||||
"navbar.signup": "Registreren",
|
||||
"navbar.links.shares": "Mijn gedeelde bestanden",
|
||||
"navbar.links.reverse": "Reverse shares",
|
||||
"navbar.avatar.account": "Mijn account",
|
||||
"navbar.avatar.admin": "Beheer",
|
||||
"navbar.avatar.signout": "Afmelden",
|
||||
// END navbar
|
||||
// /
|
||||
"home.title": "Een <h>zelfgehost</h> platform voor het delen van bestanden.",
|
||||
"home.description": "Wil je echt je persoonlijke bestanden geven aan derden zoals WeTransfer?",
|
||||
"home.bullet.a.name": "Zelf-gehost",
|
||||
"home.bullet.a.description": "Host Pingvin Share op uw eigen machine.",
|
||||
"home.bullet.b.name": "Privacy",
|
||||
"home.bullet.b.description": "Uw bestanden zijn van u en mogen nooit in handen komen van derden.",
|
||||
"home.bullet.c.name": "Geen vervelende limiet voor bestandsgrootte",
|
||||
"home.bullet.c.description": "Upload zo grote bestanden als je wilt. Alleen je harde schijf is je limiet.",
|
||||
"home.button.start": "Aan de slag",
|
||||
"home.button.source": "Bron code",
|
||||
// END /
|
||||
// /auth/signin
|
||||
"signin.title": "Welkom terug",
|
||||
"signin.description": "Heeft u nog geen account?",
|
||||
"signin.button.signup": "Registreren",
|
||||
"signin.input.email-or-username": "E-mailadres of gebruikersnaam",
|
||||
"signin.input.email-or-username.placeholder": "Uw e-mailadres of gebruikersnaam",
|
||||
"signin.input.password": "Wachtwoord",
|
||||
"signin.input.password.placeholder": "Uw wachtwoord",
|
||||
"signin.button.submit": "Aanmelden",
|
||||
"signIn.notify.totp-required.title": "Tweestapsverificatie vereist",
|
||||
"signIn.notify.totp-required.description": "Voer uw tweestapsverificatiecode in",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Account aanmaken",
|
||||
"signup.description": "Heeft u al een account?",
|
||||
"signup.button.signin": "Aanmelden",
|
||||
"signup.input.username": "Gebruikersnaam",
|
||||
"signup.input.username.placeholder": "Uw gebruikersnaam",
|
||||
"signup.input.email": "E-mailadres",
|
||||
"signup.input.email.placeholder": "Uw e-mailadres",
|
||||
"signup.button.submit": "Laten we beginnen",
|
||||
// END /auth/signup
|
||||
// /auth/reset-password
|
||||
"resetPassword.title": "Wachtwoord vergeten?",
|
||||
"resetPassword.description": "Voer uw e-mailadres in om uw wachtwoord opnieuw in te stellen.",
|
||||
"resetPassword.notify.success": "Een e-mail is verzonden met een link om uw wachtwoord te resetten.",
|
||||
"resetPassword.button.back": "Terug naar login pagina",
|
||||
"resetPassword.text.resetPassword": "Wachtwoord opnieuw instellen",
|
||||
"resetPassword.text.enterNewPassword": "Voer uw nieuwe wachtwoord in",
|
||||
"resetPassword.input.password": "Nieuw wachtwoord",
|
||||
"resetPassword.notify.passwordReset": "Uw wachtwoord is met succes opnieuw ingesteld.",
|
||||
// /account
|
||||
"account.title": "Mijn account",
|
||||
"account.card.info.title": "Account informatie",
|
||||
"account.card.info.username": "Gebruikersnaam",
|
||||
"account.card.info.email": "E-mailadres",
|
||||
"account.notify.info.success": "Account succesvol bijgewerkt",
|
||||
"account.card.password.title": "Wachtwoord",
|
||||
"account.card.password.old": "Oud wachtwoord",
|
||||
"account.card.password.new": "Nieuw wachtwoord",
|
||||
"account.notify.password.success": "Wachtwoord succesvol gewijzigd",
|
||||
"account.card.security.title": "Beveiliging",
|
||||
"account.card.security.totp.enable.description": "Voer uw huidige wachtwoord in om TOTP in te schakelen",
|
||||
"account.card.security.totp.disable.description": "Voer uw huidige wachtwoord in om TOTP uit te schakelen",
|
||||
"account.card.security.totp.button.start": "Start",
|
||||
"account.modal.totp.title": "TOTP inschakelen",
|
||||
"account.modal.totp.step1": "Stap 1: Voeg uw authenticator toe",
|
||||
"account.modal.totp.step2": "Stap 2: Valideer uw code",
|
||||
"account.modal.totp.enterManually": "Handmatig invoeren",
|
||||
"account.modal.totp.code": "Code",
|
||||
"account.modal.totp.clickToCopy": "Klik om te kopiëren",
|
||||
"account.modal.totp.verify": "Verifiëren",
|
||||
"account.notify.totp.disable": "TOTP succesvol uitgeschakeld",
|
||||
"account.notify.totp.enable": "TOTP succesvol ingeschakeld",
|
||||
"account.card.language.title": "Taal",
|
||||
"account.card.language.description": "Het project is vertaald door de community. Sommige talen zijn mogelijk onvolledig.",
|
||||
"account.card.color.title": "Kleuren schema",
|
||||
// ThemeSwitcher.tsx
|
||||
"account.theme.dark": "Donker",
|
||||
"account.theme.light": "Licht",
|
||||
"account.theme.system": "Systeem",
|
||||
"account.button.delete": "Account verwijderen",
|
||||
"account.modal.delete.title": "Account verwijderen",
|
||||
"account.modal.delete.description": "Weet u zeker dat u uw account met al uw gedeelde bestanden wilt verwijderen?",
|
||||
// END /account
|
||||
// /account/shares
|
||||
"account.shares.title": "Mijn gedeelde bestanden",
|
||||
"account.shares.title.empty": "Het is hier leeg 👀",
|
||||
"account.shares.description.empty": "U heeft geen gedeelde bestanden.",
|
||||
"account.shares.button.create": "Maak er één",
|
||||
"account.shares.info.title": "Gegevens delen",
|
||||
"account.shares.table.id": "ID",
|
||||
"account.shares.table.name": "Naam",
|
||||
"account.shares.table.description": "Beschrijving",
|
||||
"account.shares.table.visitors": "Bezoekers",
|
||||
"account.shares.table.expiresAt": "Verloopt op",
|
||||
"account.shares.table.createdAt": "Aangemaakt op",
|
||||
"account.shares.table.size": "Grootte",
|
||||
"account.shares.modal.share-informations": "Gegevens delen",
|
||||
"account.shares.modal.share-link": "Deel link",
|
||||
"account.shares.modal.delete.title": "Delete share {share}",
|
||||
"account.shares.modal.delete.description": "Do you really want to delete this share?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Reverse shares",
|
||||
"account.reverseShares.description": "A reverse share allows you to generate a unique URL that allows external users to create a share.",
|
||||
"account.reverseShares.title.empty": "Het is hier leeg 👀",
|
||||
"account.reverseShares.description.empty": "You don't have any reverse shares.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Vervaldatum",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minuut",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minuten",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "Uur",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "Uren",
|
||||
"account.reverseShares.modal.expiration.day-singular": "Dag",
|
||||
"account.reverseShares.modal.expiration.day-plural": "Dagen",
|
||||
"account.reverseShares.modal.expiration.week-singular": "Week",
|
||||
"account.reverseShares.modal.expiration.week-plural": "Weken",
|
||||
"account.reverseShares.modal.expiration.month-singular": "Maand",
|
||||
"account.reverseShares.modal.expiration.month-plural": "Maanden",
|
||||
"account.reverseShares.modal.expiration.year-singular": "Jaar",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Jaren",
|
||||
"account.reverseShares.modal.max-size.label": "Max share size",
|
||||
"account.reverseShares.modal.send-email": "Stuur e-mail notificatie",
|
||||
"account.reverseShares.modal.send-email.description": "Send an email notification when a share is created with this reverse share link.",
|
||||
"account.reverseShares.modal.max-use.label": "Max uses",
|
||||
"account.reverseShares.modal.max-use.description": "The maximum amount of times this URL can be used to create a share.",
|
||||
"account.reverseShare.never-expires": "This reverse share will never expire.",
|
||||
"account.reverseShare.expires-on": "This reverse share will expire on {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "No shares created yet",
|
||||
"account.reverseShares.table.count.singular": "share",
|
||||
"account.reverseShares.table.count.plural": "shares",
|
||||
"account.reverseShares.table.shares": "Shares",
|
||||
"account.reverseShares.table.remaining": "Remaining uses",
|
||||
"account.reverseShares.table.max-size": "Max share size",
|
||||
"account.reverseShares.table.expires": "Verloopt op",
|
||||
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
|
||||
"account.reverseShares.modal.delete.title": "Delete reverse share",
|
||||
"account.reverseShares.modal.delete.description": "Do you really want to delete this reverse share? If you do, the associated shares will be deleted as well.",
|
||||
// END /account/reverseShares
|
||||
// /admin
|
||||
"admin.title": "Beheer",
|
||||
"admin.button.users": "Gebruikers beheer",
|
||||
"admin.button.config": "Configuratie",
|
||||
"admin.version": "Versie",
|
||||
// END /admin
|
||||
// /admin/users
|
||||
"admin.users.title": "Gebruikers beheren",
|
||||
"admin.users.table.username": "Gebruikersnaam",
|
||||
"admin.users.table.email": "E-mailadres",
|
||||
"admin.users.table.admin": "Beheerder",
|
||||
"admin.users.edit.update.title": "Gebruiker {username} bijwerken",
|
||||
"admin.users.edit.update.admin-privileges": "Beheerder privileges",
|
||||
"admin.users.edit.update.change-password.title": "Wachtwoord wijzigen",
|
||||
"admin.users.edit.update.change-password.field": "Nieuw wachtwoord",
|
||||
"admin.users.edit.update.change-password.button": "Nieuw wachtwoord opslaan",
|
||||
"admin.users.edit.update.notify.password.success": "Wachtwoord succesvol gewijzigd",
|
||||
"admin.users.edit.delete.title": "Gebruiker {username} verwijderen",
|
||||
"admin.users.edit.delete.description": "Do you really want to delete this user and all his shares?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "Gebruiker aanmaken",
|
||||
"admin.users.modal.create.username": "Gebruikersnaam",
|
||||
"admin.users.modal.create.email": "E-mailadres",
|
||||
"admin.users.modal.create.password": "Wachtwoord",
|
||||
"admin.users.modal.create.manual-password": "Wachtwoord handmatig instellen",
|
||||
"admin.users.modal.create.manual-password.description": "Indien niet aangevinkt, ontvangt de gebruiker een e-mail met een link om zijn wachtwoord in te stellen.",
|
||||
"admin.users.modal.create.admin": "Beheerder privileges",
|
||||
"admin.users.modal.create.admin.description": "Indien aangevinkt, heeft de gebruiker toegang tot de beheeromgeving.",
|
||||
// END /admin/users
|
||||
// /upload
|
||||
"upload.title": "Uploaden",
|
||||
"upload.notify.generic-error": "An error occurred while finishing your share.",
|
||||
"upload.notify.count-failed": "{count} bestanden konden niet worden geüpload. Opnieuw proberen.",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "Bestanden uploaden",
|
||||
"upload.dropzone.description": "Drag'n'drop files here to start your share. We can accept only files that are less than {maxSize} in total.",
|
||||
"upload.dropzone.notify.file-too-big": "Your files exceed the maximum share size of {maxSize}.",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "Naam",
|
||||
"upload.filelist.size": "Grootte",
|
||||
// showCreateUploadModal.tsx
|
||||
"upload.modal.title": "Create Share",
|
||||
"upload.modal.link.error.invalid": "Can only contain letters, numbers, underscores, and hyphens",
|
||||
"upload.modal.link.error.taken": "Deze link is al in gebruik",
|
||||
"upload.modal.not-signed-in": "U bent niet aangemeld",
|
||||
"upload.modal.not-signed-in-description": "You will be unable to delete your share manually and view the visitor count.",
|
||||
"upload.modal.expires.never": "nooit",
|
||||
"upload.modal.expires.never-long": "Verloopt nooit",
|
||||
"upload.modal.link.label": "Koppeling",
|
||||
"upload.modal.expires.label": "Vervaldatum",
|
||||
"upload.modal.expires.minute-singular": "Minuut",
|
||||
"upload.modal.expires.minute-plural": "Minuten",
|
||||
"upload.modal.expires.hour-singular": "Uur",
|
||||
"upload.modal.expires.hour-plural": "Uren",
|
||||
"upload.modal.expires.day-singular": "Dag",
|
||||
"upload.modal.expires.day-plural": "Dagen",
|
||||
"upload.modal.expires.week-singular": "Week",
|
||||
"upload.modal.expires.week-plural": "Weken",
|
||||
"upload.modal.expires.month-singular": "Maand",
|
||||
"upload.modal.expires.month-plural": "Maanden",
|
||||
"upload.modal.expires.year-singular": "Jaar",
|
||||
"upload.modal.expires.year-plural": "Jaren",
|
||||
"upload.modal.accordion.description.title": "Beschrijving",
|
||||
"upload.modal.accordion.description.placeholder": "Note for the recipients of this share",
|
||||
"upload.modal.accordion.email.title": "E-mail ontvangers",
|
||||
"upload.modal.accordion.email.placeholder": "Voer e-mail ontvangers in",
|
||||
"upload.modal.accordion.email.invalid-email": "Ongeldig e-mailadres",
|
||||
"upload.modal.accordion.security.title": "Beveiliging opties",
|
||||
"upload.modal.accordion.security.password.label": "Paswoord beveiling",
|
||||
"upload.modal.accordion.security.password.placeholder": "Geen wachtwoord",
|
||||
"upload.modal.accordion.security.max-views.label": "Maximum aantal weergaven",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "Onbeperkt",
|
||||
// showCompletedUploadModal.tsx
|
||||
"upload.modal.completed.never-expires": "This share will never expire.",
|
||||
"upload.modal.completed.expires-on": "This share will expire on {expiration}.",
|
||||
"upload.modal.completed.share-ready": "Share ready",
|
||||
// END /upload
|
||||
// /share/[id]
|
||||
"share.title": "Share {shareId}",
|
||||
"share.description": "Kijk eens wat ik met je heb gedeeld!",
|
||||
"share.error.visitor-limit-exceeded.title": "Bezoekerslimiet overschreden",
|
||||
"share.error.visitor-limit-exceeded.description": "The visitor limit from this share has been exceeded.",
|
||||
"share.error.removed.title": "Share removed",
|
||||
"share.error.not-found.title": "Share not found",
|
||||
"share.error.not-found.description": "The share you're looking for doesn't exist.",
|
||||
"share.modal.password.title": "Wachtwoord vereist",
|
||||
"share.modal.password.description": "To access this share please enter the password for the share.",
|
||||
"share.modal.password": "Wachtwoord",
|
||||
"share.modal.error.invalid-password": "Ongeldig wachtwoord",
|
||||
"share.button.download-all": "Alles downloaden",
|
||||
"share.notify.download-all-preparing": "The share is preparing. Try again in a few minutes.",
|
||||
"share.modal.file-link": "Bestand koppeling",
|
||||
"share.table.name": "Naam",
|
||||
"share.table.size": "Grootte",
|
||||
"share.modal.file-preview.error.not-supported.title": "Voorbeeld niet ondersteund",
|
||||
"share.modal.file-preview.error.not-supported.description": "Een voorbeeld voor dit bestandstype wordt niet ondersteund. Download het bestand om het te bekijken.",
|
||||
// END /share/[id]
|
||||
// /admin/config
|
||||
"admin.config.title": "Configuratie",
|
||||
"admin.config.category.general": "Algemeen",
|
||||
"admin.config.category.share": "Delen",
|
||||
"admin.config.category.email": "E-mail",
|
||||
"admin.config.category.smtp": "SMTP",
|
||||
"admin.config.general.app-name": "App naam",
|
||||
"admin.config.general.app-name.description": "Naam van de applicatie",
|
||||
"admin.config.general.app-url": "App URL",
|
||||
"admin.config.general.app-url.description": "On which URL Pingvin Share is available",
|
||||
"admin.config.general.show-home-page": "Toon startpagina",
|
||||
"admin.config.general.show-home-page.description": "Toon of verberg de home pagina",
|
||||
"admin.config.general.logo": "Logo",
|
||||
"admin.config.general.logo.description": "Verander uw logo door een nieuwe afbeelding te uploaden. De afbeelding moet PNG zijn en het formaat moet 1:1 hebben.",
|
||||
"admin.config.general.logo.placeholder": "Afbeelding kiezen",
|
||||
"admin.config.email.enable-share-email-recipients": "Enable share email recipients",
|
||||
"admin.config.email.enable-share-email-recipients.description": "Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
|
||||
"admin.config.email.share-recipients-subject": "Share recipients subject",
|
||||
"admin.config.email.share-recipients-subject.description": "Subject of the email which gets sent to the share recipients.",
|
||||
"admin.config.email.share-recipients-message": "Share recipients message",
|
||||
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n The variables will be replaced with the actual value.",
|
||||
"admin.config.email.reverse-share-subject": "Reverse share subject",
|
||||
"admin.config.email.reverse-share-subject.description": "Subject of the email which gets sent when someone created a share with your reverse share link.",
|
||||
"admin.config.email.reverse-share-message": "Reverse share message",
|
||||
"admin.config.email.reverse-share-message.description": "Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
|
||||
"admin.config.email.reset-password-subject": "Reset wachtwoord onderwerp",
|
||||
"admin.config.email.reset-password-subject.description": "Onderwerp van de e-mail die wordt verzonden wanneer een gebruiker een wachtwoordreset aanvraagt.",
|
||||
"admin.config.email.reset-password-message": "Wachtwoord opnieuw instellen bericht",
|
||||
"admin.config.email.reset-password-message.description": "Bericht dat wordt verzonden wanneer een gebruiker een wachtwoord reset aanvraagt. {url} zal worden vervangen door de wachtwoord reset URL.",
|
||||
"admin.config.email.invite-subject": "Onderwerp uitnodiging",
|
||||
"admin.config.email.invite-subject.description": "Onderwerp van de e-mail die wordt verzonden wanneer een beheerder een gebruiker uitnodigt.",
|
||||
"admin.config.email.invite-message": "Bericht uitnodiging",
|
||||
"admin.config.email.invite-message.description": "Bericht dat wordt verzonden wanneer een beheerder een gebruiker uitnodigt. {url} zal worden vervangen door de uitnodigings-URL en {password} met het wachtwoord.",
|
||||
"admin.config.share.allow-registration": "Sta registratie toe",
|
||||
"admin.config.share.allow-registration.description": "Of registratie is toegestaan",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Allow unauthenticated shares",
|
||||
"admin.config.share.allow-unauthenticated-shares.description": "Whether unauthenticated users can create shares",
|
||||
"admin.config.share.max-size": "Max grootte",
|
||||
"admin.config.share.max-size.description": "Maximum share size in bytes",
|
||||
"admin.config.share.zip-compression-level": "Zip compressie niveau",
|
||||
"admin.config.share.zip-compression-level.description": "Pas het niveau aan voor evenwicht tussen bestandsgrootte en compressie snelheid. Geldige waarden variëren van 0 tot 9, waarbij 0 geen compressie is en 9 de maximale compressie is. ",
|
||||
"admin.config.smtp.enabled": "Inschakelen",
|
||||
"admin.config.smtp.enabled.description": "Of SMTP is ingeschakeld. Stel dit alleen in op true als u de host hebt ingevoerd, poort, e-mail, gebruiker en wachtwoord van uw SMTP-server.",
|
||||
"admin.config.smtp.host": "Host",
|
||||
"admin.config.smtp.host.description": "Host van de SMTP-server",
|
||||
"admin.config.smtp.port": "Poort",
|
||||
"admin.config.smtp.port.description": "Poort van de SMTP-server",
|
||||
"admin.config.smtp.email": "E-mail",
|
||||
"admin.config.smtp.email.description": "E-mailadres waar de e-mails van worden verzonden",
|
||||
"admin.config.smtp.username": "Gebruikersnaam",
|
||||
"admin.config.smtp.username.description": "Gebruikersnaam van de SMTP-server",
|
||||
"admin.config.smtp.password": "Wachtwoord",
|
||||
"admin.config.smtp.password.description": "Wachtwoord van de SMTP-server",
|
||||
"admin.config.smtp.button.test": "Test e-mail verzenden",
|
||||
// 404
|
||||
"404.description": "Oeps, deze pagina bestaat niet.",
|
||||
"404.button.home": "Breng me terug naar huis",
|
||||
// Common translations
|
||||
"common.button.save": "Opslaan",
|
||||
"common.button.create": "Aanmaken",
|
||||
"common.button.submit": "Verzenden",
|
||||
"common.button.delete": "Verwijderen",
|
||||
"common.button.cancel": "Annuleer",
|
||||
"common.button.confirm": "Bevestigen",
|
||||
"common.button.disable": "Uitschakelen",
|
||||
"common.button.share": "Delen",
|
||||
"common.button.generate": "Genereren",
|
||||
"common.button.done": "Voltooid",
|
||||
"common.text.link": "Koppeling",
|
||||
"common.text.or": "of",
|
||||
"common.button.go-back": "Ga terug",
|
||||
"common.notify.copied": "Uw link is gekopieerd naar het klembord",
|
||||
"common.success": "Succes",
|
||||
"common.error": "Fout",
|
||||
"common.error.unknown": "Er is een onbekende fout opgetreden",
|
||||
"common.error.invalid-email": "Ongeldig e-mailadres",
|
||||
"common.error.too-short": "Moet ten minste {length} tekens bevatten",
|
||||
"common.error.too-long": "Moet maximaal {length} tekens bevatten",
|
||||
"common.error.exact-length": "Moet precies {length} tekens bevatten",
|
||||
"common.error.invalid-number": "Moet een getal zijn",
|
||||
"common.error.field-required": "Dit veld is verplicht"
|
||||
};
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
"signin.title": "Bem-vindo de volta",
|
||||
"signin.description": "Ainda não tem uma conta?",
|
||||
"signin.button.signup": "Registo",
|
||||
"signin.input.email-or-username": "Correio ou Nome de utilizador",
|
||||
"signin.input.email-or-username": "E-mail ou nome de usuário",
|
||||
"signin.input.email-or-username.placeholder": "Seu e-mail ou nome de usuário",
|
||||
"signin.input.password": "Senha",
|
||||
"signin.input.password.placeholder": "A sua senha",
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
"signup.title": "Criar uma conta",
|
||||
"signup.description": "Já tem uma conta?",
|
||||
"signup.button.signin": "Iniciar sessão",
|
||||
"signup.input.username": "Utilizador",
|
||||
"signup.input.username": "Nome de usuário",
|
||||
"signup.input.username.placeholder": "Seu nome de usuário",
|
||||
"signup.input.email": "E-mail",
|
||||
"signup.input.email.placeholder": "Seu e-mail",
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
// /account
|
||||
"account.title": "A minha conta",
|
||||
"account.card.info.title": "Informação sobre a conta",
|
||||
"account.card.info.username": "Nome do usuário",
|
||||
"account.card.info.username": "Nome de usuário",
|
||||
"account.card.info.email": "E-mail",
|
||||
"account.notify.info.success": "A conta foi atualizada com sucesso",
|
||||
"account.card.password.title": "Senha",
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "Está vazio aqui 👀",
|
||||
"account.reverseShares.description.empty": "Você não tem nenhum compartilhamento reverso.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Criar compartilhamento reverso",
|
||||
"account.reverseShares.modal.expiration.label": "Expiração",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minuto",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutos",
|
||||
@@ -150,7 +151,7 @@ export default {
|
||||
// END /admin
|
||||
// /admin/users
|
||||
"admin.users.title": "Gerenciamento de usuários",
|
||||
"admin.users.table.username": "Nome do usuário",
|
||||
"admin.users.table.username": "Nome de usuário",
|
||||
"admin.users.table.email": "E-mail",
|
||||
"admin.users.table.admin": "Admin",
|
||||
"admin.users.edit.update.title": "Atualizar usuário {username}",
|
||||
@@ -163,7 +164,7 @@ export default {
|
||||
"admin.users.edit.delete.description": "Você realmente quer excluir este usuário e todos os seus compartilhamentos?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "Criar usuário",
|
||||
"admin.users.modal.create.username": "Nome do usuário",
|
||||
"admin.users.modal.create.username": "Nome de usuário",
|
||||
"admin.users.modal.create.email": "E-mail",
|
||||
"admin.users.modal.create.password": "Senha",
|
||||
"admin.users.modal.create.manual-password": "Definir senha manualmente",
|
||||
@@ -281,14 +282,14 @@ export default {
|
||||
"admin.config.share.zip-compression-level": "Nível de compressão",
|
||||
"admin.config.share.zip-compression-level.description": "Ajuste o nível para equilibrar entre o tamanho do arquivo e a velocidade de compressão. Valores válidos vão de 0 a 9, com 0 sendo sem compressão e 9 sendo compressão máxima. ",
|
||||
"admin.config.smtp.enabled": "Ativado",
|
||||
"admin.config.smtp.enabled.description": "Se o SMTP está habilitado. Apenas defina como verdadeiro se você digitou o host, porta, e-mail, usuário e senha do seu servidor SMTP.",
|
||||
"admin.config.smtp.enabled.description": "Se o SMTP está habilitado. Apenas defina como verdadeiro se você digitou o servidor, porta, e-mail, usuário e senha do seu servidor SMTP.",
|
||||
"admin.config.smtp.host": "Servidor",
|
||||
"admin.config.smtp.host.description": "Nome do Servidor SMTP",
|
||||
"admin.config.smtp.port": "Porta",
|
||||
"admin.config.smtp.port.description": "Porta do Servidor SMTP",
|
||||
"admin.config.smtp.email": "E-mail",
|
||||
"admin.config.smtp.email.description": "Endereço de e-mail do qual os e-mails são enviados",
|
||||
"admin.config.smtp.username": "Nome do usuário",
|
||||
"admin.config.smtp.username": "Nome de usuário",
|
||||
"admin.config.smtp.username.description": "Nome de usuário do servidor SMTP",
|
||||
"admin.config.smtp.password": "Senha",
|
||||
"admin.config.smtp.password.description": "Senha do servidor SMTP",
|
||||
|
||||
@@ -1,438 +1,324 @@
|
||||
export default {
|
||||
// Navbar
|
||||
"navbar.upload": "Upload",
|
||||
"navbar.signin": "Sign in",
|
||||
"navbar.home": "Home",
|
||||
"navbar.signup": "Sign Up",
|
||||
|
||||
"navbar.links.shares": "My shares",
|
||||
"navbar.links.reverse": "Reverse shares",
|
||||
|
||||
"navbar.avatar.account": "My account",
|
||||
"navbar.avatar.admin": "Administration",
|
||||
"navbar.avatar.signout": "Sign out",
|
||||
"navbar.upload": "Загрузить",
|
||||
"navbar.signin": "Вход",
|
||||
"navbar.home": "Главная",
|
||||
"navbar.signup": "Зарегистрироваться",
|
||||
"navbar.links.shares": "Мои загрузки",
|
||||
"navbar.links.reverse": "Обратные загрузки",
|
||||
"navbar.avatar.account": "Мой аккаунт",
|
||||
"navbar.avatar.admin": "Администрирование",
|
||||
"navbar.avatar.signout": "Выйти",
|
||||
// END navbar
|
||||
|
||||
// /
|
||||
"home.title": "A <h>self-hosted</h> file sharing platform.",
|
||||
|
||||
"home.description":
|
||||
"Do you really want to give your personal files in the hand of third parties like WeTransfer?",
|
||||
"home.bullet.a.name": "Self-Hosted",
|
||||
"home.bullet.a.description": "Host Pingvin Share on your own machine.",
|
||||
"home.bullet.b.name": "Privacy",
|
||||
"home.bullet.b.description":
|
||||
"Your files are your files and should never get into the hands of third parties.",
|
||||
"home.bullet.c.name": "No annoying file size limit",
|
||||
"home.bullet.c.description":
|
||||
"Upload as big files as you want. Only your hard drive will be your limit.",
|
||||
|
||||
"home.button.start": "Get started",
|
||||
"home.button.source": "Source code",
|
||||
"home.title": "Платформа для обмена файлами с <h>собственного хостинга</h>.",
|
||||
"home.description": "Вы действительно хотите предоставить свои личные файлы в руки третьих лиц, таких как WeTransfer?",
|
||||
"home.bullet.a.name": "На собственном сервере",
|
||||
"home.bullet.a.description": "Pingvin Share работает на вашей машине.",
|
||||
"home.bullet.b.name": "Конфиденциальность",
|
||||
"home.bullet.b.description": "Ваши файлы - это ваши файлы и никогда не должны попадать в руки третьих лиц.",
|
||||
"home.bullet.c.name": "Без раздражающего ограничения размера файла",
|
||||
"home.bullet.c.description": "Загружайте файлы с любым размером. Только ваш жесткий диск будет пределом.",
|
||||
"home.button.start": "Начнем",
|
||||
"home.button.source": "Исходный код",
|
||||
// END /
|
||||
|
||||
// /auth/signin
|
||||
"signin.title": "Welcome back",
|
||||
"signin.description": "You don't have an account yet?",
|
||||
"signin.button.signup": "Sign up",
|
||||
"signin.input.email-or-username": "Email or username",
|
||||
"signin.input.email-or-username.placeholder": "Your email or username",
|
||||
"signin.input.password": "Password",
|
||||
"signin.input.password.placeholder": "Your password",
|
||||
"signin.button.submit": "Sign in",
|
||||
"signIn.notify.totp-required.title": "Two-factor authentication required",
|
||||
"signIn.notify.totp-required.description":
|
||||
"Please enter your two-factor authentication code",
|
||||
|
||||
"signin.title": "С возвращением",
|
||||
"signin.description": "У вас еще нет учетной записи?",
|
||||
"signin.button.signup": "Зарегистрироваться",
|
||||
"signin.input.email-or-username": "Email или логин",
|
||||
"signin.input.email-or-username.placeholder": "Эл. почта или логин",
|
||||
"signin.input.password": "Пароль",
|
||||
"signin.input.password.placeholder": "Ваш пароль",
|
||||
"signin.button.submit": "Вход",
|
||||
"signIn.notify.totp-required.title": "Требуется двухфакторная аутентификация",
|
||||
"signIn.notify.totp-required.description": "Пожалуйста, введите код Вашей 2-х факторной аутентификации",
|
||||
// END /auth/signin
|
||||
|
||||
// /auth/signup
|
||||
"signup.title": "Create an account",
|
||||
"signup.description": "Already have an account?",
|
||||
"signup.button.signin": "Sign in",
|
||||
"signup.input.username": "Username",
|
||||
"signup.input.username.placeholder": "Your username",
|
||||
"signup.input.email": "Email",
|
||||
"signup.input.email.placeholder": "Your email",
|
||||
"signup.button.submit": "Let's get started",
|
||||
|
||||
"signup.title": "Создать аккаунт",
|
||||
"signup.description": "Уже есть учётная запись?",
|
||||
"signup.button.signin": "Вход",
|
||||
"signup.input.username": "Логин",
|
||||
"signup.input.username.placeholder": "Ваш логин (имя пользователя)",
|
||||
"signup.input.email": "Электронная почта",
|
||||
"signup.input.email.placeholder": "Адрес эл. почты",
|
||||
"signup.button.submit": "Давайте начнём",
|
||||
// END /auth/signup
|
||||
|
||||
// /auth/reset-password
|
||||
"resetPassword.title": "Forgot your password?",
|
||||
"resetPassword.description": "Enter your email to reset your password.",
|
||||
"resetPassword.notify.success":
|
||||
"An email has been sent with a link to reset your password.",
|
||||
"resetPassword.button.back": "Back to sign in page",
|
||||
"resetPassword.text.resetPassword": "Reset password",
|
||||
"resetPassword.text.enterNewPassword": "Enter your new password",
|
||||
"resetPassword.input.password": "New password",
|
||||
"resetPassword.notify.passwordReset":
|
||||
"Your password has been reset successfully.",
|
||||
|
||||
"resetPassword.title": "Забыли пароль?",
|
||||
"resetPassword.description": "Введите ваш email для восстановления пароля.",
|
||||
"resetPassword.notify.success": "Вам направлено письмо со ссылкой для сброса пароля.",
|
||||
"resetPassword.button.back": "Вернуться на страницу входа",
|
||||
"resetPassword.text.resetPassword": "Сбросить пароль",
|
||||
"resetPassword.text.enterNewPassword": "Введите новый пароль",
|
||||
"resetPassword.input.password": "Новый пароль",
|
||||
"resetPassword.notify.passwordReset": "Ваш пароль был успешно сброшен!",
|
||||
// /account
|
||||
"account.title": "My account",
|
||||
|
||||
"account.card.info.title": "Account info",
|
||||
"account.card.info.username": "Username",
|
||||
"account.card.info.email": "Email",
|
||||
"account.notify.info.success": "Account updated successfully",
|
||||
|
||||
"account.card.password.title": "Password",
|
||||
"account.card.password.old": "Old password",
|
||||
"account.card.password.new": "New password",
|
||||
"account.notify.password.success": "Password changed successfully",
|
||||
|
||||
"account.card.security.title": "Security",
|
||||
"account.card.security.totp.enable.description":
|
||||
"Enter your current password to start enabling TOTP",
|
||||
"account.card.security.totp.disable.description":
|
||||
"Enter your current password to disable TOTP",
|
||||
"account.card.security.totp.button.start": "Start",
|
||||
"account.modal.totp.title": "Enable TOTP",
|
||||
"account.modal.totp.step1": "Step 1: Add your authenticator",
|
||||
"account.modal.totp.step2": "Step 2: Validate your code",
|
||||
"account.modal.totp.enterManually": "Enter manually",
|
||||
"account.modal.totp.code": "Code",
|
||||
"account.modal.totp.clickToCopy": "Click to copy",
|
||||
"account.modal.totp.verify": "Verify",
|
||||
"account.notify.totp.disable": "TOTP disabled successfully",
|
||||
"account.notify.totp.enable": "TOTP enabled successfully",
|
||||
|
||||
"account.card.language.title": "Language",
|
||||
"account.card.language.description":
|
||||
"The project is translated by the community. Some languages might be incomplete.",
|
||||
"account.card.color.title": "Color scheme",
|
||||
|
||||
"account.title": "Мой аккаунт",
|
||||
"account.card.info.title": "Информация об аккаунте",
|
||||
"account.card.info.username": "Логин",
|
||||
"account.card.info.email": "Электронная почта",
|
||||
"account.notify.info.success": "Настройки учетной записи успешно обновлены",
|
||||
"account.card.password.title": "Пароль",
|
||||
"account.card.password.old": "Старый пароль",
|
||||
"account.card.password.new": "Новый пароль",
|
||||
"account.notify.password.success": "Пароль успешно изменён",
|
||||
"account.card.security.title": "Безопасность",
|
||||
"account.card.security.totp.enable.description": "Введите ваш текущий пароль для начала включения TOTP",
|
||||
"account.card.security.totp.disable.description": "Введите ваш текущий пароль, чтобы отключить TOTP",
|
||||
"account.card.security.totp.button.start": "Начать",
|
||||
"account.modal.totp.title": "Включить TOTP",
|
||||
"account.modal.totp.step1": "Шаг 1: Добавьте свой аутентификатор",
|
||||
"account.modal.totp.step2": "Шаг 2: Проверка кода",
|
||||
"account.modal.totp.enterManually": "Ввести вручную",
|
||||
"account.modal.totp.code": "Код",
|
||||
"account.modal.totp.clickToCopy": "Нажмите, чтобы скопировать",
|
||||
"account.modal.totp.verify": "Подтвердить",
|
||||
"account.notify.totp.disable": "TOTP успешно отключен",
|
||||
"account.notify.totp.enable": "TOTP успешно включен",
|
||||
"account.card.language.title": "Язык",
|
||||
"account.card.language.description": "Проект переведен сообществом. Некоторые языки могут быть неполными.",
|
||||
"account.card.color.title": "Цветовая схема",
|
||||
// ThemeSwitcher.tsx
|
||||
"account.theme.dark": "Dark",
|
||||
"account.theme.light": "Light",
|
||||
"account.theme.system": "System",
|
||||
|
||||
"account.button.delete": "Delete Account",
|
||||
"account.modal.delete.title": "Delete Account",
|
||||
"account.modal.delete.description":
|
||||
"Do you really want to delete your account including all your active shares?",
|
||||
"account.theme.dark": "Тёмная",
|
||||
"account.theme.light": "Светлая",
|
||||
"account.theme.system": "Системная",
|
||||
"account.button.delete": "Удалить аккаунт",
|
||||
"account.modal.delete.title": "Удалить аккаунт",
|
||||
"account.modal.delete.description": "Вы действительно хотите удалить свою учетную запись, включая все ваши загрузки?",
|
||||
// END /account
|
||||
|
||||
// /account/shares
|
||||
"account.shares.title": "My shares",
|
||||
"account.shares.title.empty": "It's empty here 👀",
|
||||
"account.shares.description.empty": "You don't have any shares.",
|
||||
"account.shares.button.create": "Create one",
|
||||
|
||||
"account.shares.info.title": "Share informations",
|
||||
"account.shares.title": "Мои загрузки",
|
||||
"account.shares.title.empty": "Тут пусто 👀",
|
||||
"account.shares.description.empty": "У вас нет загрузок.",
|
||||
"account.shares.button.create": "Создать одну",
|
||||
"account.shares.info.title": "Сведения",
|
||||
"account.shares.table.id": "ID",
|
||||
"account.shares.table.name": "Name",
|
||||
"account.shares.table.description": "Description",
|
||||
"account.shares.table.visitors": "Visitors",
|
||||
"account.shares.table.expiresAt": "Expires at",
|
||||
"account.shares.table.createdAt": "Created at",
|
||||
"account.shares.table.size": "Size",
|
||||
|
||||
"account.shares.modal.share-informations": "Share informations",
|
||||
"account.shares.modal.share-link": "Share link",
|
||||
|
||||
"account.shares.modal.delete.title": "Delete share {share}",
|
||||
"account.shares.modal.delete.description":
|
||||
"Do you really want to delete this share?",
|
||||
|
||||
"account.shares.table.name": "Название",
|
||||
"account.shares.table.description": "Описание",
|
||||
"account.shares.table.visitors": "Посетителей",
|
||||
"account.shares.table.expiresAt": "Действительно до",
|
||||
"account.shares.table.createdAt": "Создано",
|
||||
"account.shares.table.size": "Размер",
|
||||
"account.shares.modal.share-informations": "Сведения",
|
||||
"account.shares.modal.share-link": "Поделиться ссылкой",
|
||||
"account.shares.modal.delete.title": "Удалить загрузку {share}",
|
||||
"account.shares.modal.delete.description": "Вы действительно хотите удалить эту загрузку?",
|
||||
// END /account/shares
|
||||
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Reverse shares",
|
||||
"account.reverseShares.description":
|
||||
"A reverse share allows you to generate a unique URL that allows external users to create a share.",
|
||||
|
||||
"account.reverseShares.title.empty": "It's empty here 👀",
|
||||
"account.reverseShares.description.empty":
|
||||
"You don't have any reverse shares.",
|
||||
|
||||
"account.reverseShares.title": "Обратные загрузки",
|
||||
"account.reverseShares.description": "Обратная загрузка позволяет генерировать уникальный URL, позволяющий внешним пользователям загружать файлы.",
|
||||
"account.reverseShares.title.empty": "Тут пусто 👀",
|
||||
"account.reverseShares.description.empty": "У вас пока нет обратных загрузок.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.expiration.label": "Expiration",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Minute",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Minutes",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "Hour",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "Hours",
|
||||
"account.reverseShares.modal.expiration.day-singular": "Day",
|
||||
"account.reverseShares.modal.expiration.day-plural": "Days",
|
||||
"account.reverseShares.modal.expiration.week-singular": "Week",
|
||||
"account.reverseShares.modal.expiration.week-plural": "Weeks",
|
||||
"account.reverseShares.modal.expiration.month-singular": "Month",
|
||||
"account.reverseShares.modal.expiration.month-plural": "Months",
|
||||
"account.reverseShares.modal.expiration.year-singular": "Year",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Years",
|
||||
|
||||
"account.reverseShares.modal.max-size.label": "Max share size",
|
||||
|
||||
"account.reverseShares.modal.send-email": "Send email notification",
|
||||
"account.reverseShares.modal.send-email.description":
|
||||
"Send an email notification when a share is created with this reverse share link.",
|
||||
|
||||
"account.reverseShares.modal.max-use.label": "Max uses",
|
||||
"account.reverseShares.modal.max-use.description":
|
||||
"The maximum amount of times this URL can be used to create a share.",
|
||||
"account.reverseShare.never-expires": "This reverse share will never expire.",
|
||||
"account.reverseShare.expires-on":
|
||||
"This reverse share will expire on {expiration}.",
|
||||
|
||||
"account.reverseShares.table.no-shares": "No shares created yet",
|
||||
"account.reverseShares.table.count.singular": "share",
|
||||
"account.reverseShares.table.count.plural": "shares",
|
||||
"account.reverseShares.table.shares": "Shares",
|
||||
"account.reverseShares.table.remaining": "Remaining uses",
|
||||
"account.reverseShares.table.max-size": "Max share size",
|
||||
"account.reverseShares.table.expires": "Expires at",
|
||||
|
||||
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
|
||||
|
||||
"account.reverseShares.modal.delete.title": "Delete reverse share",
|
||||
"account.reverseShares.modal.delete.description":
|
||||
"Do you really want to delete this reverse share? If you do, the associated shares will be deleted as well.",
|
||||
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "Истекает",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Минута",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Минут(ы)",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "Час",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "Часов",
|
||||
"account.reverseShares.modal.expiration.day-singular": "День",
|
||||
"account.reverseShares.modal.expiration.day-plural": "Дней",
|
||||
"account.reverseShares.modal.expiration.week-singular": "Неделя",
|
||||
"account.reverseShares.modal.expiration.week-plural": "Недель",
|
||||
"account.reverseShares.modal.expiration.month-singular": "Месяц",
|
||||
"account.reverseShares.modal.expiration.month-plural": "Месяца(-ев)",
|
||||
"account.reverseShares.modal.expiration.year-singular": "Год",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Года (лет)",
|
||||
"account.reverseShares.modal.max-size.label": "Макс. размер загрузки",
|
||||
"account.reverseShares.modal.send-email": "Отправить уведомление по эл. почте",
|
||||
"account.reverseShares.modal.send-email.description": "Отправлять уведомление по электронной почте, когда загрузка создается с помощью этой обратной ссылки.",
|
||||
"account.reverseShares.modal.max-use.label": "Максимум использований",
|
||||
"account.reverseShares.modal.max-use.description": "Максимальное количество раз, когда URL может быть использован для создания загрузки.",
|
||||
"account.reverseShare.never-expires": "Эта обратная загрузка никогда не устареет.",
|
||||
"account.reverseShare.expires-on": "Эта обратная загрузка устареет {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "Нет созданных загрузок",
|
||||
"account.reverseShares.table.count.singular": "загрузка",
|
||||
"account.reverseShares.table.count.plural": "загрузки",
|
||||
"account.reverseShares.table.shares": "Загрузки",
|
||||
"account.reverseShares.table.remaining": "Осталось использований",
|
||||
"account.reverseShares.table.max-size": "Макс. размер загрузки",
|
||||
"account.reverseShares.table.expires": "Действительно до",
|
||||
"account.reverseShares.modal.reverse-share-link": "Ссылка обратной загрузки",
|
||||
"account.reverseShares.modal.delete.title": "Удалить обратную загрузку",
|
||||
"account.reverseShares.modal.delete.description": "Вы действительно хотите удалить эту обратную загрузку? Если вы это сделаете, то все связанные обратные загрузки будут также удалены.",
|
||||
// END /account/reverseShares
|
||||
|
||||
// /admin
|
||||
"admin.title": "Administration",
|
||||
"admin.button.users": "User management",
|
||||
"admin.button.config": "Configuration",
|
||||
"admin.version": "Version",
|
||||
"admin.title": "Администрирование",
|
||||
"admin.button.users": "Управление пользователями",
|
||||
"admin.button.config": "Конфигурация",
|
||||
"admin.version": "Версия",
|
||||
// END /admin
|
||||
|
||||
// /admin/users
|
||||
"admin.users.title": "User management",
|
||||
"admin.users.table.username": "Username",
|
||||
"admin.users.table.email": "Email",
|
||||
"admin.users.table.admin": "Admin",
|
||||
|
||||
"admin.users.edit.update.title": "Update user {username}",
|
||||
"admin.users.edit.update.admin-privileges": "Admin privileges",
|
||||
"admin.users.edit.update.change-password.title": "Change password",
|
||||
"admin.users.edit.update.change-password.field": "New password",
|
||||
"admin.users.edit.update.change-password.button": "Save new password",
|
||||
"admin.users.edit.update.notify.password.success":
|
||||
"Password changed successfully",
|
||||
|
||||
"admin.users.edit.delete.title": "Delete user {username}",
|
||||
"admin.users.edit.delete.description":
|
||||
"Do you really want to delete this user and all his shares?",
|
||||
|
||||
"admin.users.title": "Управление пользователями",
|
||||
"admin.users.table.username": "Логин",
|
||||
"admin.users.table.email": "Электронная почта",
|
||||
"admin.users.table.admin": "Администратор",
|
||||
"admin.users.edit.update.title": "Обновить пользователя {username}",
|
||||
"admin.users.edit.update.admin-privileges": "Права администратора",
|
||||
"admin.users.edit.update.change-password.title": "Изменить пароль",
|
||||
"admin.users.edit.update.change-password.field": "Новый пароль",
|
||||
"admin.users.edit.update.change-password.button": "Сохранить новый пароль",
|
||||
"admin.users.edit.update.notify.password.success": "Пароль успешно изменён",
|
||||
"admin.users.edit.delete.title": "Удалить пользователя {username}",
|
||||
"admin.users.edit.delete.description": "Вы действительно хотите удалить этого пользователя и все его загрузки?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "Create user",
|
||||
"admin.users.modal.create.username": "Username",
|
||||
"admin.users.modal.create.email": "Email",
|
||||
"admin.users.modal.create.password": "Password",
|
||||
"admin.users.modal.create.manual-password": "Set password manually",
|
||||
"admin.users.modal.create.manual-password.description":
|
||||
"If not checked, the user will receive an email with a link to set their password.",
|
||||
"admin.users.modal.create.admin": "Admin privileges",
|
||||
"admin.users.modal.create.admin.description":
|
||||
"If checked, the user will be able to access the admin panel.",
|
||||
|
||||
"admin.users.modal.create.title": "Создать пользователя",
|
||||
"admin.users.modal.create.username": "Логин",
|
||||
"admin.users.modal.create.email": "Электронная почта",
|
||||
"admin.users.modal.create.password": "Пароль",
|
||||
"admin.users.modal.create.manual-password": "Установить пароль вручную",
|
||||
"admin.users.modal.create.manual-password.description": "Если флажок не установлен, пользователь получит письмо со ссылкой для установки пароля.",
|
||||
"admin.users.modal.create.admin": "Права администратора",
|
||||
"admin.users.modal.create.admin.description": "Если отмечено, пользователь будет иметь доступ к панели администратора.",
|
||||
// END /admin/users
|
||||
|
||||
// /upload
|
||||
"upload.title": "Upload",
|
||||
|
||||
"upload.notify.generic-error":
|
||||
"An error occurred while finishing your share.",
|
||||
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
|
||||
|
||||
"upload.title": "Загрузить",
|
||||
"upload.notify.generic-error": "Произошла ошибка при завершении вашей загрузки.",
|
||||
"upload.notify.count-failed": "Не удалось загрузить файлы {count}. Повтор попытки.",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "Upload files",
|
||||
"upload.dropzone.description":
|
||||
"Drag'n'drop files here to start your share. We can accept only files that are less than {maxSize} in total.",
|
||||
"upload.dropzone.notify.file-too-big":
|
||||
"Your files exceed the maximum share size of {maxSize}.",
|
||||
|
||||
"upload.dropzone.title": "Загрузить файлы",
|
||||
"upload.dropzone.description": "Перетащите сюда файлы для начала загрузки. Мы можем принимать только файлы, которые меньше {maxSize}.",
|
||||
"upload.dropzone.notify.file-too-big": "Ваши файлы превышают максимальный размер в {maxSize}.",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "Name",
|
||||
"upload.filelist.size": "Size",
|
||||
|
||||
"upload.filelist.name": "Название",
|
||||
"upload.filelist.size": "Размер",
|
||||
// showCreateUploadModal.tsx
|
||||
"upload.modal.title": "Create Share",
|
||||
"upload.modal.link.error.invalid":
|
||||
"Can only contain letters, numbers, underscores, and hyphens",
|
||||
"upload.modal.link.error.taken": "This link is already in use",
|
||||
"upload.modal.not-signed-in": "You're not signed in",
|
||||
"upload.modal.not-signed-in-description":
|
||||
"You will be unable to delete your share manually and view the visitor count.",
|
||||
|
||||
"upload.modal.expires.never": "never",
|
||||
"upload.modal.expires.never-long": "Never Expires",
|
||||
|
||||
"upload.modal.link.label": "Link",
|
||||
"upload.modal.expires.label": "Expiration",
|
||||
"upload.modal.expires.minute-singular": "Minute",
|
||||
"upload.modal.expires.minute-plural": "Minutes",
|
||||
"upload.modal.expires.hour-singular": "Hour",
|
||||
"upload.modal.expires.hour-plural": "Hours",
|
||||
"upload.modal.expires.day-singular": "Day",
|
||||
"upload.modal.expires.day-plural": "Days",
|
||||
"upload.modal.expires.week-singular": "Week",
|
||||
"upload.modal.expires.week-plural": "Weeks",
|
||||
"upload.modal.expires.month-singular": "Month",
|
||||
"upload.modal.expires.month-plural": "Months",
|
||||
"upload.modal.expires.year-singular": "Year",
|
||||
"upload.modal.expires.year-plural": "Years",
|
||||
|
||||
"upload.modal.accordion.description.title": "Description",
|
||||
"upload.modal.accordion.description.placeholder":
|
||||
"Note for the recipients of this share",
|
||||
|
||||
"upload.modal.accordion.email.title": "Email recipients",
|
||||
"upload.modal.accordion.email.placeholder": "Enter email recipients",
|
||||
"upload.modal.accordion.email.invalid-email": "Invalid email address",
|
||||
|
||||
"upload.modal.accordion.security.title": "Security options",
|
||||
"upload.modal.accordion.security.password.label": "Password protection",
|
||||
"upload.modal.accordion.security.password.placeholder": "No password",
|
||||
"upload.modal.accordion.security.max-views.label": "Maximum views",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "No limit",
|
||||
|
||||
"upload.modal.title": "Загрузить",
|
||||
"upload.modal.link.error.invalid": "Имя пользователя должно состоять только из букв, цифр, подчёркиваний и дефисов",
|
||||
"upload.modal.link.error.taken": "Эта ссылка уже используется",
|
||||
"upload.modal.not-signed-in": "Вы не авторизованы",
|
||||
"upload.modal.not-signed-in-description": "Вы не сможете удалить свои файлы вручную и просмотреть количество посетителей.",
|
||||
"upload.modal.expires.never": "никогда",
|
||||
"upload.modal.expires.never-long": "Никогда не истекает",
|
||||
"upload.modal.link.label": "Ссылка",
|
||||
"upload.modal.expires.label": "Истекает",
|
||||
"upload.modal.expires.minute-singular": "Минута",
|
||||
"upload.modal.expires.minute-plural": "Минут(ы)",
|
||||
"upload.modal.expires.hour-singular": "Час",
|
||||
"upload.modal.expires.hour-plural": "Часов",
|
||||
"upload.modal.expires.day-singular": "День",
|
||||
"upload.modal.expires.day-plural": "Дней",
|
||||
"upload.modal.expires.week-singular": "Неделя",
|
||||
"upload.modal.expires.week-plural": "Недель",
|
||||
"upload.modal.expires.month-singular": "Месяц",
|
||||
"upload.modal.expires.month-plural": "Месяца(-ев)",
|
||||
"upload.modal.expires.year-singular": "Год",
|
||||
"upload.modal.expires.year-plural": "Года (лет)",
|
||||
"upload.modal.accordion.description.title": "Описание",
|
||||
"upload.modal.accordion.description.placeholder": "Примечание для получателей этой загрузки",
|
||||
"upload.modal.accordion.email.title": "Получатели письма",
|
||||
"upload.modal.accordion.email.placeholder": "Получатели e-mail",
|
||||
"upload.modal.accordion.email.invalid-email": "Недопустимый адрес электронной почты",
|
||||
"upload.modal.accordion.security.title": "Параметры безопасности",
|
||||
"upload.modal.accordion.security.password.label": "Защита паролем",
|
||||
"upload.modal.accordion.security.password.placeholder": "Без пароля",
|
||||
"upload.modal.accordion.security.max-views.label": "Максимум просмотров",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "Без ограничения",
|
||||
// showCompletedUploadModal.tsx
|
||||
"upload.modal.completed.never-expires": "This share will never expire.",
|
||||
"upload.modal.completed.expires-on":
|
||||
"This share will expire on {expiration}.",
|
||||
"upload.modal.completed.share-ready": "Share ready",
|
||||
|
||||
"upload.modal.completed.never-expires": "Эта загрузка никогда не устареет.",
|
||||
"upload.modal.completed.expires-on": "Эта загрузка устареет {expiration}.",
|
||||
"upload.modal.completed.share-ready": "Готово",
|
||||
// END /upload
|
||||
|
||||
// /share/[id]
|
||||
"share.title": "Share {shareId}",
|
||||
"share.description": "Look what I've shared with you!",
|
||||
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
|
||||
"share.error.visitor-limit-exceeded.description":
|
||||
"The visitor limit from this share has been exceeded.",
|
||||
"share.error.removed.title": "Share removed",
|
||||
"share.error.not-found.title": "Share not found",
|
||||
"share.error.not-found.description":
|
||||
"The share you're looking for doesn't exist.",
|
||||
|
||||
"share.modal.password.title": "Password required",
|
||||
"share.modal.password.description":
|
||||
"To access this share please enter the password for the share.",
|
||||
"share.modal.password": "Password",
|
||||
"share.modal.error.invalid-password": "Invalid password",
|
||||
|
||||
"share.button.download-all": "Download all",
|
||||
"share.notify.download-all-preparing":
|
||||
"The share is preparing. Try again in a few minutes.",
|
||||
|
||||
"share.modal.file-link": "File link",
|
||||
"share.table.name": "Name",
|
||||
"share.table.size": "Size",
|
||||
|
||||
"share.modal.file-preview.error.not-supported.title": "Preview not supported",
|
||||
"share.modal.file-preview.error.not-supported.description":
|
||||
"A preview for thise file type is unsupported. Please download the file to view it.",
|
||||
|
||||
"share.title": "Загрузка {shareId}",
|
||||
"share.description": "Посмотрите, чем я поделился с вами!",
|
||||
"share.error.visitor-limit-exceeded.title": "Превышен лимит посетителей",
|
||||
"share.error.visitor-limit-exceeded.description": "Превышен лимит посетителей.",
|
||||
"share.error.removed.title": "Загрузка удалена",
|
||||
"share.error.not-found.title": "Загрузка не найдена",
|
||||
"share.error.not-found.description": "Страница, которую вы ищете, не существует.",
|
||||
"share.modal.password.title": "Требуется пароль",
|
||||
"share.modal.password.description": "Для доступа к этому ресурсу введите пароль для общего доступа.",
|
||||
"share.modal.password": "Пароль",
|
||||
"share.modal.error.invalid-password": "Неверный пароль",
|
||||
"share.button.download-all": "Скачать все",
|
||||
"share.notify.download-all-preparing": "Загрузка готовится. Повторите попытку через несколько минут.",
|
||||
"share.modal.file-link": "Ссылка на файл",
|
||||
"share.table.name": "Название",
|
||||
"share.table.size": "Размер",
|
||||
"share.modal.file-preview.error.not-supported.title": "Предпросмотр не поддерживается",
|
||||
"share.modal.file-preview.error.not-supported.description": "Предварительный просмотр этого типа файла не поддерживается. Пожалуйста, скачайте файл для его просмотра.",
|
||||
// END /share/[id]
|
||||
|
||||
// /admin/config
|
||||
"admin.config.title": "Configuration",
|
||||
"admin.config.category.general": "General",
|
||||
"admin.config.category.share": "Share",
|
||||
"admin.config.category.email": "Email",
|
||||
"admin.config.title": "Конфигурация",
|
||||
"admin.config.category.general": "Общее",
|
||||
"admin.config.category.share": "Загрузки",
|
||||
"admin.config.category.email": "Электронная почта",
|
||||
"admin.config.category.smtp": "SMTP",
|
||||
|
||||
"admin.config.general.app-name": "App name",
|
||||
"admin.config.general.app-name.description": "Name of the application",
|
||||
"admin.config.general.app-url": "App URL",
|
||||
"admin.config.general.app-url.description":
|
||||
"On which URL Pingvin Share is available",
|
||||
"admin.config.general.show-home-page": "Show home page",
|
||||
"admin.config.general.show-home-page.description":
|
||||
"Whether to show the home page",
|
||||
"admin.config.general.logo": "Logo",
|
||||
"admin.config.general.logo.description":
|
||||
"Change your logo by uploading a new image. The image must be a PNG and should have the format 1:1.",
|
||||
"admin.config.general.logo.placeholder": "Pick image",
|
||||
|
||||
"admin.config.email.enable-share-email-recipients":
|
||||
"Enable share email recipients",
|
||||
"admin.config.email.enable-share-email-recipients.description":
|
||||
"Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
|
||||
"admin.config.email.share-recipients-subject": "Share recipients subject",
|
||||
"admin.config.email.share-recipients-subject.description":
|
||||
"Subject of the email which gets sent to the share recipients.",
|
||||
"admin.config.email.share-recipients-message": "Share recipients message",
|
||||
"admin.config.email.share-recipients-message.description":
|
||||
"Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n The variables will be replaced with the actual value.",
|
||||
"admin.config.email.reverse-share-subject": "Reverse share subject",
|
||||
"admin.config.email.reverse-share-subject.description":
|
||||
"Subject of the email which gets sent when someone created a share with your reverse share link.",
|
||||
"admin.config.email.reverse-share-message": "Reverse share message",
|
||||
"admin.config.email.reverse-share-message.description":
|
||||
"Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
|
||||
"admin.config.email.reset-password-subject": "Reset password subject",
|
||||
"admin.config.email.reset-password-subject.description":
|
||||
"Subject of the email which gets sent when a user requests a password reset.",
|
||||
"admin.config.email.reset-password-message": "Reset password message",
|
||||
"admin.config.email.reset-password-message.description":
|
||||
"Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
|
||||
"admin.config.email.invite-subject": "Invite subject",
|
||||
"admin.config.email.invite-subject.description":
|
||||
"Subject of the email which gets sent when an admin invites a user.",
|
||||
"admin.config.email.invite-message": "Invite message",
|
||||
"admin.config.email.invite-message.description":
|
||||
"Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL and {password} with the password.",
|
||||
"admin.config.share.allow-registration": "Allow registration",
|
||||
"admin.config.share.allow-registration.description":
|
||||
"Whether registration is allowed",
|
||||
"admin.config.share.allow-unauthenticated-shares":
|
||||
"Allow unauthenticated shares",
|
||||
"admin.config.share.allow-unauthenticated-shares.description":
|
||||
"Whether unauthenticated users can create shares",
|
||||
"admin.config.share.max-size": "Max size",
|
||||
"admin.config.share.max-size.description": "Maximum share size in bytes",
|
||||
"admin.config.share.zip-compression-level": "Zip compression level",
|
||||
"admin.config.share.zip-compression-level.description":
|
||||
"Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
|
||||
|
||||
"admin.config.smtp.enabled": "Enabled",
|
||||
"admin.config.smtp.enabled.description":
|
||||
"Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
|
||||
"admin.config.smtp.host": "Host",
|
||||
"admin.config.smtp.host.description": "Host of the SMTP server",
|
||||
"admin.config.smtp.port": "Port",
|
||||
"admin.config.smtp.port.description": "Port of the SMTP server",
|
||||
"admin.config.smtp.email": "Email",
|
||||
"admin.config.smtp.email.description":
|
||||
"Email address which the emails get sent from",
|
||||
"admin.config.smtp.username": "Username",
|
||||
"admin.config.smtp.username.description": "Username of the SMTP server",
|
||||
"admin.config.smtp.password": "Password",
|
||||
"admin.config.smtp.password.description": "Password of the SMTP server",
|
||||
"admin.config.smtp.button.test": "Send test email",
|
||||
|
||||
"admin.config.general.app-name": "Название приложения",
|
||||
"admin.config.general.app-name.description": "Видимое название приложения",
|
||||
"admin.config.general.app-url": "URL-адрес приложения",
|
||||
"admin.config.general.app-url.description": "Адрес на котором доступен Pingvin Share",
|
||||
"admin.config.general.show-home-page": "Показывать домашнюю страницу",
|
||||
"admin.config.general.show-home-page.description": "Показывать ли домашнюю страницу или нет",
|
||||
"admin.config.general.logo": "Логотип",
|
||||
"admin.config.general.logo.description": "Измените свой логотип, загрузив новое изображение. Изображение должно быть PNG и должно иметь формат 1:1.",
|
||||
"admin.config.general.logo.placeholder": "Выберите изображение",
|
||||
"admin.config.email.enable-share-email-recipients": "Включить обмен с получателями электронной почты",
|
||||
"admin.config.email.enable-share-email-recipients.description": "Разрешить ли отправку писем получателям. Включите, только если вы включили SMTP.",
|
||||
"admin.config.email.share-recipients-subject": "Заголовок письма (загрузка)",
|
||||
"admin.config.email.share-recipients-subject.description": "Тема письма, которое отправляется получателям акции.",
|
||||
"admin.config.email.share-recipients-message": "Сообщение письма загрузки",
|
||||
"admin.config.email.share-recipients-message.description": "Сообщение, которое отправляется получателям публикации. Доступные переменные:\n {creator} - Имя пользователя создателя загрузки\n {shareUrl} - URL загрузки\n {desc} - Описание загрузки\n {expires} - Дата истечения загрузки\n Переменные будут заменены на фактическое значение.",
|
||||
"admin.config.email.reverse-share-subject": "Заголовок письма (обратная загрузка)",
|
||||
"admin.config.email.reverse-share-subject.description": "Тема письма, которое отправляется, когда кто-то создал загрузку с вашей обратной ссылкой.",
|
||||
"admin.config.email.reverse-share-message": "Сообщение письма обратной загрузки",
|
||||
"admin.config.email.reverse-share-message.description": "Сообщение, которое отправляется, когда кто-то создал загрузку с вашей обратной ссылкой. {shareUrl} будет заменен именем создателя и URL-адресом общего доступа.",
|
||||
"admin.config.email.reset-password-subject": "Тема сброса пароля",
|
||||
"admin.config.email.reset-password-subject.description": "Тема письма, которое отправляется, когда пользователь запрашивает сброс пароля.",
|
||||
"admin.config.email.reset-password-message": "Сообщение о сбросе пароля",
|
||||
"admin.config.email.reset-password-message.description": "Сообщение, которое отправляется при запросе сброса пароля. {url} будет заменён ссылкой.",
|
||||
"admin.config.email.invite-subject": "Тема приглашения",
|
||||
"admin.config.email.invite-subject.description": "Тема письма, которое отправляется, когда администратор приглашает пользователя.",
|
||||
"admin.config.email.invite-message": "Сообщение с приглашением",
|
||||
"admin.config.email.invite-message.description": "Сообщение приглашения. {url} будет заменён ссылкой приглашения, а {password} паролем.",
|
||||
"admin.config.share.allow-registration": "Разрешить регистрацию",
|
||||
"admin.config.share.allow-registration.description": "Разрешена ли регистрация",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Разрешить неавторизованные загрузки",
|
||||
"admin.config.share.allow-unauthenticated-shares.description": "Могут ли неавторизованные пользователи создавать загрузки",
|
||||
"admin.config.share.max-size": "Максимальный размер",
|
||||
"admin.config.share.max-size.description": "Максимальный размер файла в байтах",
|
||||
"admin.config.share.zip-compression-level": "Уровень сжатия Zip",
|
||||
"admin.config.share.zip-compression-level.description": "Регулировка уровня баланса между размером файла и скоростью сжатия. Допустимые значения от 0 до 9, с 0 без сжатия, а 9 - максимальное сжатие. ",
|
||||
"admin.config.smtp.enabled": "Включено",
|
||||
"admin.config.smtp.enabled.description": "Включено ли SMTP. Установите значение true только если вы ввели хост, порт, email, пользователь и пароль вашего SMTP-сервера.",
|
||||
"admin.config.smtp.host": "Хост",
|
||||
"admin.config.smtp.host.description": "Сервер SMTP сервера",
|
||||
"admin.config.smtp.port": "Порт",
|
||||
"admin.config.smtp.port.description": "Порт SMTP сервера",
|
||||
"admin.config.smtp.email": "Электронная почта",
|
||||
"admin.config.smtp.email.description": "Адрес электронной почты, от которого отправляются письма",
|
||||
"admin.config.smtp.username": "Логин",
|
||||
"admin.config.smtp.username.description": "Имя пользователя SMTP сервера",
|
||||
"admin.config.smtp.password": "Пароль",
|
||||
"admin.config.smtp.password.description": "Пароль SMTP-сервера",
|
||||
"admin.config.smtp.button.test": "Отправить тестовое письмо",
|
||||
// 404
|
||||
"404.description": "Oops this page doesn't exist.",
|
||||
"404.button.home": "Bring me back home",
|
||||
|
||||
"404.description": "Упс, этой страницы не существует.",
|
||||
"404.button.home": "Верните меня домой",
|
||||
// Common translations
|
||||
"common.button.save": "Save",
|
||||
"common.button.create": "Create",
|
||||
"common.button.submit": "Submit",
|
||||
"common.button.delete": "Delete",
|
||||
"common.button.cancel": "Cancel",
|
||||
"common.button.confirm": "Confirm",
|
||||
"common.button.disable": "Disable",
|
||||
"common.button.share": "Share",
|
||||
"common.button.generate": "Generate",
|
||||
"common.button.done": "Done",
|
||||
"common.text.link": "Link",
|
||||
"common.text.or": "or",
|
||||
"common.button.go-back": "Go back",
|
||||
"common.notify.copied": "Your link was copied to the clipboard",
|
||||
"common.success": "Success",
|
||||
|
||||
"common.error": "Error",
|
||||
"common.error.unknown": "An unknown error occurred",
|
||||
"common.error.invalid-email": "Invalid email address",
|
||||
"common.error.too-short": "Must be at least {length} characters",
|
||||
"common.error.too-long": "Must be at most {length} characters",
|
||||
"common.error.exact-length": "Must be exactly {length} characters",
|
||||
"common.error.invalid-number": "Must be a number",
|
||||
"common.error.field-required": "This field is required",
|
||||
};
|
||||
"common.button.save": "Сохранить",
|
||||
"common.button.create": "Создать",
|
||||
"common.button.submit": "Отправить",
|
||||
"common.button.delete": "Удалить",
|
||||
"common.button.cancel": "Отмена",
|
||||
"common.button.confirm": "Подтвердить",
|
||||
"common.button.disable": "Отключить",
|
||||
"common.button.share": "Поделиться",
|
||||
"common.button.generate": "Сгенерировать",
|
||||
"common.button.done": "Готово",
|
||||
"common.text.link": "Ссылка",
|
||||
"common.text.or": "или",
|
||||
"common.button.go-back": "Назад",
|
||||
"common.notify.copied": "Ваша ссылка скопирована в буфер обмена",
|
||||
"common.success": "Успешно",
|
||||
"common.error": "Ошибочка",
|
||||
"common.error.unknown": "Произошла неизвестная ошибка",
|
||||
"common.error.invalid-email": "Недопустимый адрес электронной почты",
|
||||
"common.error.too-short": "Должно быть не менее {length} символов",
|
||||
"common.error.too-long": "Должно быть не больше {length} символов",
|
||||
"common.error.exact-length": "Должно быть ровно {length} символов",
|
||||
"common.error.invalid-number": "Должно быть числом",
|
||||
"common.error.field-required": "Поле обязательно для заполнения"
|
||||
};
|
||||
324
frontend/src/i18n/translations/sr-SP.ts
Normal file
324
frontend/src/i18n/translations/sr-SP.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
export default {
|
||||
// Navbar
|
||||
"navbar.upload": "Пошаљи",
|
||||
"navbar.signin": "Пријавите се",
|
||||
"navbar.home": "Почетна",
|
||||
"navbar.signup": "Региструј се",
|
||||
"navbar.links.shares": "Moja дељења",
|
||||
"navbar.links.reverse": "Обрнуто дељење",
|
||||
"navbar.avatar.account": "Мој налог",
|
||||
"navbar.avatar.admin": "Администрација",
|
||||
"navbar.avatar.signout": "Одјави се",
|
||||
// END navbar
|
||||
// /
|
||||
"home.title": "<h>self-hosted</h> платформа за дељење фајлова.",
|
||||
"home.description": "Да ли заиста желите да дате своје личне датотеке у руке трећих страна као што је WeTransfer?",
|
||||
"home.bullet.a.name": "Личан хостинг",
|
||||
"home.bullet.a.description": "Хостујте Pingvin Share на својој машини.",
|
||||
"home.bullet.b.name": "Privatnost",
|
||||
"home.bullet.b.description": "Ваше датотеке су ваше датотеке и никада не би требало да дођу у руке трећих лица.",
|
||||
"home.bullet.c.name": "Нема досадног ограничења величине датотеке",
|
||||
"home.bullet.c.description": "Отпремите онолико великих датотека колико желите. Само ваш чврсти диск ће бити ваше ограничење.",
|
||||
"home.button.start": "Почети",
|
||||
"home.button.source": "Изворни код",
|
||||
// END /
|
||||
// /auth/signin
|
||||
"signin.title": "Добродошли назад",
|
||||
"signin.description": "Још увек немате налог?",
|
||||
"signin.button.signup": "Региструј се",
|
||||
"signin.input.email-or-username": "Е-пошта или корисничко име",
|
||||
"signin.input.email-or-username.placeholder": "Ваша Е-пошта или корисничко име",
|
||||
"signin.input.password": "Лозинка",
|
||||
"signin.input.password.placeholder": "Ваша лозинка",
|
||||
"signin.button.submit": "Пријавите се",
|
||||
"signIn.notify.totp-required.title": "Потребна је двофакторска аутентификација",
|
||||
"signIn.notify.totp-required.description": "Унесите свој двофакторски код за аутентификацију",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "Направи налог",
|
||||
"signup.description": "Већ имате налог?",
|
||||
"signup.button.signin": "Пријавите се",
|
||||
"signup.input.username": "Корисничко име",
|
||||
"signup.input.username.placeholder": "Ваше корисничко име",
|
||||
"signup.input.email": "E-пошта",
|
||||
"signup.input.email.placeholder": "Ваш и-мејл",
|
||||
"signup.button.submit": "Хајде да почнемо",
|
||||
// END /auth/signup
|
||||
// /auth/reset-password
|
||||
"resetPassword.title": "Заборавили сте лозинку?",
|
||||
"resetPassword.description": "Унесите своју е-пошту да бисте ресетовали лозинку.",
|
||||
"resetPassword.notify.success": "Послат је емаил са везом за ресетовање ваше лозинке.",
|
||||
"resetPassword.button.back": "Назад на страницу за пријаву",
|
||||
"resetPassword.text.resetPassword": "Обнови лозинку",
|
||||
"resetPassword.text.enterNewPassword": "Унесите вашу нову лозинку",
|
||||
"resetPassword.input.password": "Нова лозинка",
|
||||
"resetPassword.notify.passwordReset": "Ваша лозинка је успешно ресетована.",
|
||||
// /account
|
||||
"account.title": "Мој налог",
|
||||
"account.card.info.title": "Подаци о налогу",
|
||||
"account.card.info.username": "Корисничко име",
|
||||
"account.card.info.email": "E-пошта",
|
||||
"account.notify.info.success": "Налог је успешно ажуриран",
|
||||
"account.card.password.title": "Лозинка",
|
||||
"account.card.password.old": "Стара лозинка",
|
||||
"account.card.password.new": "Нова лозинка",
|
||||
"account.notify.password.success": "Лозинка је успешно промењена",
|
||||
"account.card.security.title": "Безбедност",
|
||||
"account.card.security.totp.enable.description": "Унесите своју тренутну лозинку да бисте почели да омогућавате ТОТП",
|
||||
"account.card.security.totp.disable.description": "Унесите своју тренутну лозинку да бисте онемогућили ТОТП",
|
||||
"account.card.security.totp.button.start": "Започни",
|
||||
"account.modal.totp.title": "Омогући ТОТП",
|
||||
"account.modal.totp.step1": "Корак 1: Додајте свој аутентификатор",
|
||||
"account.modal.totp.step2": "Корак 2: Потврдите свој код",
|
||||
"account.modal.totp.enterManually": "Унесите ручно",
|
||||
"account.modal.totp.code": "Код",
|
||||
"account.modal.totp.clickToCopy": "Кликните за копирање",
|
||||
"account.modal.totp.verify": "Верификуј",
|
||||
"account.notify.totp.disable": "ТОТП је успешно онемогућен",
|
||||
"account.notify.totp.enable": "ТОТП је успешно омогућен",
|
||||
"account.card.language.title": "Језик",
|
||||
"account.card.language.description": "Пројекат је преведен од стране заједнице. Неки језици могу бити непотпуни.",
|
||||
"account.card.color.title": "Шема боја",
|
||||
// ThemeSwitcher.tsx
|
||||
"account.theme.dark": "Тамно",
|
||||
"account.theme.light": "Светло",
|
||||
"account.theme.system": "Систем",
|
||||
"account.button.delete": "Избриши Налог",
|
||||
"account.modal.delete.title": "Избриши Налог",
|
||||
"account.modal.delete.description": "Да ли заиста желите да избришете свој налог укључујући све ваше активне дељења?",
|
||||
// END /account
|
||||
// /account/shares
|
||||
"account.shares.title": "Moja дељења",
|
||||
"account.shares.title.empty": "Овде је празно 👀",
|
||||
"account.shares.description.empty": "Немате никаква дељења.",
|
||||
"account.shares.button.create": "Направите",
|
||||
"account.shares.info.title": "Делите информације",
|
||||
"account.shares.table.id": "ИД",
|
||||
"account.shares.table.name": "Назив",
|
||||
"account.shares.table.description": "Опис",
|
||||
"account.shares.table.visitors": "Посетици",
|
||||
"account.shares.table.expiresAt": "Истиче у",
|
||||
"account.shares.table.createdAt": "Направљено у",
|
||||
"account.shares.table.size": "Величина",
|
||||
"account.shares.modal.share-informations": "Делите информације",
|
||||
"account.shares.modal.share-link": "Дели везу",
|
||||
"account.shares.modal.delete.title": "Избриши дељење {share}",
|
||||
"account.shares.modal.delete.description": "Да ли заиста желите да избришете ово дељење?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "Обрнуто дељење",
|
||||
"account.reverseShares.description": "Обрнуто дељење вам омогућава да генеришете јединствени URL који омогућава спољним корисницима да креирају дељење.",
|
||||
"account.reverseShares.title.empty": "Овде је празно 👀",
|
||||
"account.reverseShares.description.empty": "Немате обрнутих дељења.",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Креирајте обрнуто дељење",
|
||||
"account.reverseShares.modal.expiration.label": "Истиче",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "Минут",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "Минуте",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "Сат",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "Сати",
|
||||
"account.reverseShares.modal.expiration.day-singular": "Дан",
|
||||
"account.reverseShares.modal.expiration.day-plural": "Дани",
|
||||
"account.reverseShares.modal.expiration.week-singular": "Седмица",
|
||||
"account.reverseShares.modal.expiration.week-plural": "Седмице",
|
||||
"account.reverseShares.modal.expiration.month-singular": "Месец",
|
||||
"account.reverseShares.modal.expiration.month-plural": "Месеци",
|
||||
"account.reverseShares.modal.expiration.year-singular": "Година",
|
||||
"account.reverseShares.modal.expiration.year-plural": "Године",
|
||||
"account.reverseShares.modal.max-size.label": "Максимална величина дељења",
|
||||
"account.reverseShares.modal.send-email": "Пошаљите обавештење путем е-поште",
|
||||
"account.reverseShares.modal.send-email.description": "Пошаљите обавештење е-поштом када се креира дељење помоћу ове обрнуте везе за дељење.",
|
||||
"account.reverseShares.modal.max-use.label": "Максималан број коришћења",
|
||||
"account.reverseShares.modal.max-use.description": "Максималан број пута који овај URL може да се користи за прављење дељења.",
|
||||
"account.reverseShare.never-expires": "Ово обрнуто дељење никада неће истећи.",
|
||||
"account.reverseShare.expires-on": "Ово обрнуто дељење ће истећи {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "Још нема креираних дељења",
|
||||
"account.reverseShares.table.count.singular": "дељење",
|
||||
"account.reverseShares.table.count.plural": "дељења",
|
||||
"account.reverseShares.table.shares": "Дељења",
|
||||
"account.reverseShares.table.remaining": "Преостала употреба",
|
||||
"account.reverseShares.table.max-size": "Максимална величина дељења",
|
||||
"account.reverseShares.table.expires": "Истиче у",
|
||||
"account.reverseShares.modal.reverse-share-link": "Линк за обрнуто дељење",
|
||||
"account.reverseShares.modal.delete.title": "Обришите обрнуто дељење",
|
||||
"account.reverseShares.modal.delete.description": "Да ли заиста желите да избришете ово обрнуто дељење? Ако то учините, придружена дељења ће такође бити избрисана.",
|
||||
// END /account/reverseShares
|
||||
// /admin
|
||||
"admin.title": "Администрација",
|
||||
"admin.button.users": "Управљање корисницима",
|
||||
"admin.button.config": "Конфигурација",
|
||||
"admin.version": "Верзија",
|
||||
// END /admin
|
||||
// /admin/users
|
||||
"admin.users.title": "Управљање корисницима",
|
||||
"admin.users.table.username": "Корисничко име",
|
||||
"admin.users.table.email": "E-пошта",
|
||||
"admin.users.table.admin": "Админ",
|
||||
"admin.users.edit.update.title": "Ажурирајте корисника {username}",
|
||||
"admin.users.edit.update.admin-privileges": "Администраторске привилегије",
|
||||
"admin.users.edit.update.change-password.title": "Промените лозинку",
|
||||
"admin.users.edit.update.change-password.field": "Нова лозинка",
|
||||
"admin.users.edit.update.change-password.button": "Снимите нову лозинку",
|
||||
"admin.users.edit.update.notify.password.success": "Лозинка је успешно промењена",
|
||||
"admin.users.edit.delete.title": "Обришите корисника {username}",
|
||||
"admin.users.edit.delete.description": "Да ли заиста желите да избришете овог корисника и сва његова дељења?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "Креирај корисника",
|
||||
"admin.users.modal.create.username": "Корисничко име",
|
||||
"admin.users.modal.create.email": "E-пошта",
|
||||
"admin.users.modal.create.password": "Лозинка",
|
||||
"admin.users.modal.create.manual-password": "Поставите лозинку ручно",
|
||||
"admin.users.modal.create.manual-password.description": "Ако није означено, корисник ће добити е-пошту са везом за постављање лозинке.",
|
||||
"admin.users.modal.create.admin": "Администраторске привилегије",
|
||||
"admin.users.modal.create.admin.description": "Ако је означено, корисник ће моћи да приступи административном панелу.",
|
||||
// END /admin/users
|
||||
// /upload
|
||||
"upload.title": "Пошаљи",
|
||||
"upload.notify.generic-error": "Дошло је до грешке при довршавању дељења.",
|
||||
"upload.notify.count-failed": "Отпремање {count} датотека није успело. Покушавам поново.",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "Пошаљи датотеке",
|
||||
"upload.dropzone.description": "Превуците и отпустите датотеке овде да бисте започели дељење. Можемо да прихватимо само датотеке које су укупно мање од {maxSize}.",
|
||||
"upload.dropzone.notify.file-too-big": "Ваше датотеке премашују максималну величину дељења од {maxSize}.",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "Назив",
|
||||
"upload.filelist.size": "Величина",
|
||||
// showCreateUploadModal.tsx
|
||||
"upload.modal.title": "Направи дељење",
|
||||
"upload.modal.link.error.invalid": "Може да садржи само слова, бројеве, доње црте и цртице",
|
||||
"upload.modal.link.error.taken": "Овај линк је већ у употреби",
|
||||
"upload.modal.not-signed-in": "Нисте пријављени",
|
||||
"upload.modal.not-signed-in-description": "Нећете моћи ручно да избришете своје дељење и видите број посетилаца.",
|
||||
"upload.modal.expires.never": "никад",
|
||||
"upload.modal.expires.never-long": "Никада не истиче",
|
||||
"upload.modal.link.label": "Линк",
|
||||
"upload.modal.expires.label": "Истиче",
|
||||
"upload.modal.expires.minute-singular": "Минут",
|
||||
"upload.modal.expires.minute-plural": "Минуте",
|
||||
"upload.modal.expires.hour-singular": "Сат",
|
||||
"upload.modal.expires.hour-plural": "Сати",
|
||||
"upload.modal.expires.day-singular": "Дан",
|
||||
"upload.modal.expires.day-plural": "Дани",
|
||||
"upload.modal.expires.week-singular": "Седмица",
|
||||
"upload.modal.expires.week-plural": "Седмице",
|
||||
"upload.modal.expires.month-singular": "Месец",
|
||||
"upload.modal.expires.month-plural": "Месеци",
|
||||
"upload.modal.expires.year-singular": "Година",
|
||||
"upload.modal.expires.year-plural": "Године",
|
||||
"upload.modal.accordion.description.title": "Опис",
|
||||
"upload.modal.accordion.description.placeholder": "Напомена за примаоце овог дељења",
|
||||
"upload.modal.accordion.email.title": "Примаоци е-поште",
|
||||
"upload.modal.accordion.email.placeholder": "Унесите примаоце е-поште",
|
||||
"upload.modal.accordion.email.invalid-email": "Неисправна адреса е-поште",
|
||||
"upload.modal.accordion.security.title": "Сигурносна подешавања",
|
||||
"upload.modal.accordion.security.password.label": "Заштита лозинком",
|
||||
"upload.modal.accordion.security.password.placeholder": "Нема лозинке",
|
||||
"upload.modal.accordion.security.max-views.label": "Максималан број прегледа",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "Без лимита",
|
||||
// showCompletedUploadModal.tsx
|
||||
"upload.modal.completed.never-expires": "Ово дељење никада неће истећи.",
|
||||
"upload.modal.completed.expires-on": "Ово дељење ће истећи {expiration}.",
|
||||
"upload.modal.completed.share-ready": "Дељење је спремно",
|
||||
// END /upload
|
||||
// /share/[id]
|
||||
"share.title": "Дељење {shareId}",
|
||||
"share.description": "Погледајте шта сам поделио са вама!",
|
||||
"share.error.visitor-limit-exceeded.title": "Прекорачено је ограничење посетилаца",
|
||||
"share.error.visitor-limit-exceeded.description": "Ограничење посетилаца из овог дељења је премашено.",
|
||||
"share.error.removed.title": "Дељење је уклоњено",
|
||||
"share.error.not-found.title": "Дељење није пронађено",
|
||||
"share.error.not-found.description": "Удео који тражите не постоји.",
|
||||
"share.modal.password.title": "Обавезна лозинка",
|
||||
"share.modal.password.description": "Да бисте приступили овом дељењу, унесите лозинку за дељење.",
|
||||
"share.modal.password": "Лозинка",
|
||||
"share.modal.error.invalid-password": "Неисправна лозинка",
|
||||
"share.button.download-all": "Преузми све",
|
||||
"share.notify.download-all-preparing": "Дељење се спрема. Покушајте поново за неколико минута.",
|
||||
"share.modal.file-link": "Веза датотеке",
|
||||
"share.table.name": "Назив",
|
||||
"share.table.size": "Величина",
|
||||
"share.modal.file-preview.error.not-supported.title": "Преглед није подржан",
|
||||
"share.modal.file-preview.error.not-supported.description": "Преглед за овај тип датотеке није подржан. Преузмите датотеку да бисте је видели.",
|
||||
// END /share/[id]
|
||||
// /admin/config
|
||||
"admin.config.title": "Конфигурација",
|
||||
"admin.config.category.general": "Опште",
|
||||
"admin.config.category.share": "Дељење",
|
||||
"admin.config.category.email": "E-пошта",
|
||||
"admin.config.category.smtp": "SMTP",
|
||||
"admin.config.general.app-name": "Назив апликације",
|
||||
"admin.config.general.app-name.description": "Назив апликације",
|
||||
"admin.config.general.app-url": "URL апликације",
|
||||
"admin.config.general.app-url.description": "На којој URL адреси је доступан Pingvin Share",
|
||||
"admin.config.general.show-home-page": "Прикажи почетну страницу",
|
||||
"admin.config.general.show-home-page.description": "Да ли да се прикаже почетна страница",
|
||||
"admin.config.general.logo": "Лого",
|
||||
"admin.config.general.logo.description": "Промените свој логотип тако што ћете отпремити нову слику. Слика мора да буде PNG и треба да има формат 1:1.",
|
||||
"admin.config.general.logo.placeholder": "Изабери слику",
|
||||
"admin.config.email.enable-share-email-recipients": "Омогућите дељење прималаца е-поште",
|
||||
"admin.config.email.enable-share-email-recipients.description": "Да ли да дозволите да имејлови деле примаоце. Омогућите ово само ако сте омогућили SMTP.",
|
||||
"admin.config.email.share-recipients-subject": "Поделите наслов примаоца",
|
||||
"admin.config.email.share-recipients-subject.description": "Тема е-поруке која се шаље примаоцима дељења.",
|
||||
"admin.config.email.share-recipients-message": "Делите поруку примаоца",
|
||||
"admin.config.email.share-recipients-message.description": "Порука која се шаље примаоцима дељења. Доступне променљиве:\n {creator} – корисничко име креатора дељења\n {shareUrl} – URL дељења\n {desc} – Опис дељења\n {expires} - Датум истека акције\n Променљиве ће бити замењене стварном вредношћу.",
|
||||
"admin.config.email.reverse-share-subject": "Тема обрнутог дељења",
|
||||
"admin.config.email.reverse-share-subject.description": "Тема е-поруке која се шаље када је неко направио дељење са вашом обрнутим линком за дељење.",
|
||||
"admin.config.email.reverse-share-message": "Обрнуто дељење порука",
|
||||
"admin.config.email.reverse-share-message.description": "Порука која се шаље када је неко направио дељење са вашом обрнутом везом за дељење. {shareUrl} ће бити замењен именом креатора и УРЛ-ом за дељење.",
|
||||
"admin.config.email.reset-password-subject": "Ресетујте тему лозинке",
|
||||
"admin.config.email.reset-password-subject.description": "Предмет е-поште која се шаље када корисник затражи ресетовање лозинке.",
|
||||
"admin.config.email.reset-password-message": "Порука за ресетовање лозинке",
|
||||
"admin.config.email.reset-password-message.description": "Порука која се шаље када корисник затражи ресетовање лозинке. {url} ће бити замењен УРЛ-ом за ресетовање лозинке.",
|
||||
"admin.config.email.invite-subject": "Позивна тема",
|
||||
"admin.config.email.invite-subject.description": "Тема е-поруке која се шаље када администратор позове корисника.",
|
||||
"admin.config.email.invite-message": "Позивна порука",
|
||||
"admin.config.email.invite-message.description": "Порука која се шаље када администратор позове корисника. {url} ће бити замењен УРЛ-ом позива, а {password} лозинком.",
|
||||
"admin.config.share.allow-registration": "Дозволи регистрацију",
|
||||
"admin.config.share.allow-registration.description": "Да ли је регистрација дозвољена",
|
||||
"admin.config.share.allow-unauthenticated-shares": "Дозволи дељење без аутентификације",
|
||||
"admin.config.share.allow-unauthenticated-shares.description": "Да ли корисници без аутентификације могу да креирају дељења",
|
||||
"admin.config.share.max-size": "Максимална величина",
|
||||
"admin.config.share.max-size.description": "Максимална величина дељења у бајтовима",
|
||||
"admin.config.share.zip-compression-level": "Ниво zip компресије",
|
||||
"admin.config.share.zip-compression-level.description": "Подесите ниво да бисте балансирали између величине датотеке и брзине компресије. Важеће вредности се крећу од 0 до 9, при чему је 0 без компресије, а 9 је максимална компресија. ",
|
||||
"admin.config.smtp.enabled": "Омогућено",
|
||||
"admin.config.smtp.enabled.description": "Да ли је SMTP омогућен. Поставите ово на тачно само ако сте унели хост, порт, е-пошту, корисника и лозинку вашег SMTP сервера.",
|
||||
"admin.config.smtp.host": "Домаћин",
|
||||
"admin.config.smtp.host.description": "Домаћин SMTP сервера",
|
||||
"admin.config.smtp.port": "Порт",
|
||||
"admin.config.smtp.port.description": "Порт домаћина SMTP сервера",
|
||||
"admin.config.smtp.email": "E-пошта",
|
||||
"admin.config.smtp.email.description": "Адреса е-поште са које се е-поруке шаљу",
|
||||
"admin.config.smtp.username": "Корисничко име",
|
||||
"admin.config.smtp.username.description": "Корисничко име SMTP сервера",
|
||||
"admin.config.smtp.password": "Лозинка",
|
||||
"admin.config.smtp.password.description": "Лозинка SMTP сервера",
|
||||
"admin.config.smtp.button.test": "Пошаљи тестну е-пошту",
|
||||
// 404
|
||||
"404.description": "Опа - Ова страна не постоји.",
|
||||
"404.button.home": "Врати ме на почетак",
|
||||
// Common translations
|
||||
"common.button.save": "Сачувај",
|
||||
"common.button.create": "Направи",
|
||||
"common.button.submit": "Пошаљи",
|
||||
"common.button.delete": "Обриши",
|
||||
"common.button.cancel": "Откажи",
|
||||
"common.button.confirm": "Потврди",
|
||||
"common.button.disable": "Онемогући",
|
||||
"common.button.share": "Дељење",
|
||||
"common.button.generate": "Генериши",
|
||||
"common.button.done": "Готово",
|
||||
"common.text.link": "Линк",
|
||||
"common.text.or": "или",
|
||||
"common.button.go-back": "Иди назад",
|
||||
"common.notify.copied": "Ваша веза је копирана у clipboard",
|
||||
"common.success": "Успешно",
|
||||
"common.error": "Грешка",
|
||||
"common.error.unknown": "Дошло је до непознате грешке",
|
||||
"common.error.invalid-email": "Неисправна адреса е-поште",
|
||||
"common.error.too-short": "Мора да има најмање {length} знакова",
|
||||
"common.error.too-long": "Мора да има највише {length} знакова",
|
||||
"common.error.exact-length": "Мора да има тачно {length} знакова",
|
||||
"common.error.invalid-number": "Мора бити број",
|
||||
"common.error.field-required": "Поље је обавезно"
|
||||
};
|
||||
324
frontend/src/i18n/translations/th-TH.ts
Normal file
324
frontend/src/i18n/translations/th-TH.ts
Normal file
@@ -0,0 +1,324 @@
|
||||
export default {
|
||||
// Navbar
|
||||
"navbar.upload": "อัพโหลด",
|
||||
"navbar.signin": "เข้าสู่ระบบ",
|
||||
"navbar.home": "หน้าหลัก",
|
||||
"navbar.signup": "สมัครบัญชี",
|
||||
"navbar.links.shares": "แชร์ของฉัน",
|
||||
"navbar.links.reverse": "รีเวิร์สแชร์",
|
||||
"navbar.avatar.account": "บัญชีของฉัน",
|
||||
"navbar.avatar.admin": "แผงควบคุมระบบ",
|
||||
"navbar.avatar.signout": "ออกจากระบบ",
|
||||
// END navbar
|
||||
// /
|
||||
"home.title": "แพลตฟอร์มสำหรับแชร์ไฟล์ที่คุณสามารถโฮสต์ด้วยตนเอง.",
|
||||
"home.description": "คุณอยากให้บริษัทภายนอกเช่น WeTransfer เข้าถึงไฟล์ส่วนตัวของคุณหรือเปล่า?",
|
||||
"home.bullet.a.name": "Self-Hosted",
|
||||
"home.bullet.a.description": "โฮสต์ Pingvin Share บนเครื่องของคุณเอง.",
|
||||
"home.bullet.b.name": "ความเป็นส่วนตัว",
|
||||
"home.bullet.b.description": "ไฟล์ของคุณคือไฟล์ของคุณ มันไม่ควรตกอยู่บนมือของบุคคลที่สาม",
|
||||
"home.bullet.c.name": "ไม่มีการจำกัดขนาดไฟล์ที่น่ารำคาญ",
|
||||
"home.bullet.c.description": "อัพโหลดใหญ่เท่าไหนก็ได้ จำกัดอย่างเดียวคือความจุของเคื่องคุณ",
|
||||
"home.button.start": "เริ่มต้น",
|
||||
"home.button.source": "ซอร์สโค้ด",
|
||||
// END /
|
||||
// /auth/signin
|
||||
"signin.title": "ยินดีต้อนรับกลับ",
|
||||
"signin.description": "ยังไม่มีบัญชี?",
|
||||
"signin.button.signup": "สมัครบัญชี",
|
||||
"signin.input.email-or-username": "อีเมล์์์์์์์์หรือชื่อผู้ใช้",
|
||||
"signin.input.email-or-username.placeholder": "อีเมล์์์์์์์หรือชื่อผู้ใช้ของคุณ",
|
||||
"signin.input.password": "รหัสผ่าน",
|
||||
"signin.input.password.placeholder": "รหัสผ่านของคุณ",
|
||||
"signin.button.submit": "เข้าสู่ระบบ",
|
||||
"signIn.notify.totp-required.title": "ยืนยันตรวจสอบสิทธิ์สองปัจจัย",
|
||||
"signIn.notify.totp-required.description": "กรุณาใส่รหัสยืนยันตัวตนสองปัจจัย",
|
||||
// END /auth/signin
|
||||
// /auth/signup
|
||||
"signup.title": "สมัครบัญชี",
|
||||
"signup.description": "มีบัญชีแล้ว?",
|
||||
"signup.button.signin": "เข้าสู่ระบบ",
|
||||
"signup.input.username": "ชื่อผู้ใช้",
|
||||
"signup.input.username.placeholder": "ชื่อผู้ใช้ของคุณ",
|
||||
"signup.input.email": "อีเมล์์์์์์์์",
|
||||
"signup.input.email.placeholder": "อีเมล์์์์์์์์ของคุณ",
|
||||
"signup.button.submit": "เริ่มต้นกัน",
|
||||
// END /auth/signup
|
||||
// /auth/reset-password
|
||||
"resetPassword.title": "ลืมรหัสผ่าน?",
|
||||
"resetPassword.description": "กรุณาใส่อีเมล์์์์์์์ของคุณเพื่อรีเซ็ตรหัสผ่าน",
|
||||
"resetPassword.notify.success": "ส่งอีเมล์์์์์์์พร้อมลิงค์เพื่อรีเซ็ตรหัสผ่านของคุณแล้ว",
|
||||
"resetPassword.button.back": "กลับไปที่หน้าลงชื่อเข้าใช้",
|
||||
"resetPassword.text.resetPassword": "รีเซ็ตรหัสผ่าน",
|
||||
"resetPassword.text.enterNewPassword": "ป้อนรหัสผ่านใหม่ของคุณ",
|
||||
"resetPassword.input.password": "รหัสผ่านใหม่",
|
||||
"resetPassword.notify.passwordReset": "รหัสผ่านของคุณรีเซ็ตเรียบร้อยแล้ว",
|
||||
// /account
|
||||
"account.title": "บัญชีของฉัน",
|
||||
"account.card.info.title": "ข้อมูลบัญชี",
|
||||
"account.card.info.username": "ชื่อผู้ใช้",
|
||||
"account.card.info.email": "อีเมล์์์์์์์",
|
||||
"account.notify.info.success": "อัปเดตบัญชีเรียบร้อยแล้ว",
|
||||
"account.card.password.title": "รหัสผ่าน",
|
||||
"account.card.password.old": "รหัสผ่านเก่า",
|
||||
"account.card.password.new": "รหัสผ่านใหม่",
|
||||
"account.notify.password.success": "อัปเดตรหัสผ่านเรียบร้อยแล้ว",
|
||||
"account.card.security.title": "ความปลอดภัย",
|
||||
"account.card.security.totp.enable.description": "ใส่รหัสผ่านปัจจุบันของคุณเพื่อเริ่มต้นการเปิดใช้งาน TOTP",
|
||||
"account.card.security.totp.disable.description": "ใส่รหัสผ่านปัจจุบันของคุณเพื่อปิดใช้งาน TOTP",
|
||||
"account.card.security.totp.button.start": "เริ่มต้น",
|
||||
"account.modal.totp.title": "เปิดใช้งาน TOTP",
|
||||
"account.modal.totp.step1": "ขั้นตอนที่ 1: สแกนรหัส QR",
|
||||
"account.modal.totp.step2": "ขั้นตอนที่ 2: ป้อนรหัสยืนยันตัวตน",
|
||||
"account.modal.totp.enterManually": "ป้อนด้วยตนเอง",
|
||||
"account.modal.totp.code": "รหัส",
|
||||
"account.modal.totp.clickToCopy": "คลิกเพื่อคัดลอก",
|
||||
"account.modal.totp.verify": "ยืนยัน",
|
||||
"account.notify.totp.disable": "TOTP ถูกปิดใช้งานเรียบร้อยแล้ว",
|
||||
"account.notify.totp.enable": "TOTP ถูกเปิดใช้งานเรียบร้อยแล้ว",
|
||||
"account.card.language.title": "ภาษา",
|
||||
"account.card.language.description": "โปรเจคนี้ถูกแปลโดยชุมชน บางภาษาอาจยังไม่สมบูรณ์",
|
||||
"account.card.color.title": "ธีมสี",
|
||||
// ThemeSwitcher.tsx
|
||||
"account.theme.dark": "มืด",
|
||||
"account.theme.light": "สว่าง",
|
||||
"account.theme.system": "ตามระบบ",
|
||||
"account.button.delete": "ลบบัญชี",
|
||||
"account.modal.delete.title": "ลบบัญชีของคุณ",
|
||||
"account.modal.delete.description": "คุณต้องการลบบัญชีของคุณหรือไม่ รวมถึงแชร์ที่คุณสร้างไว้ทั้งหมด?",
|
||||
// END /account
|
||||
// /account/shares
|
||||
"account.shares.title": "แชร์ของฉัน",
|
||||
"account.shares.title.empty": "มันว่างเปล่าที่นี่ 👀",
|
||||
"account.shares.description.empty": "คุณยังไม่ได้สร้างแชร์ใดๆ",
|
||||
"account.shares.button.create": "สร้างแชร์",
|
||||
"account.shares.info.title": "ข้อมูลแชร์",
|
||||
"account.shares.table.id": "ID",
|
||||
"account.shares.table.name": "ชื่อ",
|
||||
"account.shares.table.description": "คำอธิบาย",
|
||||
"account.shares.table.visitors": "ผู้เข้าชม",
|
||||
"account.shares.table.expiresAt": "หมดอายุ",
|
||||
"account.shares.table.createdAt": "สร้างเมื่อ",
|
||||
"account.shares.table.size": "ขนาด",
|
||||
"account.shares.modal.share-informations": "ข้อมูลแชร์",
|
||||
"account.shares.modal.share-link": "แชร์ลิงค์",
|
||||
"account.shares.modal.delete.title": "ลบแชร์ {share}",
|
||||
"account.shares.modal.delete.description": "คุณต้องการลบแชร์นี้หรือไม่?",
|
||||
// END /account/shares
|
||||
// /account/reverseShares
|
||||
"account.reverseShares.title": "รีเวิร์สแชร์ของฉัน",
|
||||
"account.reverseShares.description": "รีเวิร์สแชร์สร้างลิงค์สำหรับคนภายนอกเพื่อที่จะแชร์ไฟล์ให้คุณ",
|
||||
"account.reverseShares.title.empty": "มันว่างเปล่าที่นี่ 👀",
|
||||
"account.reverseShares.description.empty": "คุณยังไม่ได้สร้างรีเวิร์สแชร์ใดๆ",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "ลิงค์หมดอายุใน",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "นาที",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "นาที",
|
||||
"account.reverseShares.modal.expiration.hour-singular": "ชั่วโมง",
|
||||
"account.reverseShares.modal.expiration.hour-plural": "ชั่วโมง",
|
||||
"account.reverseShares.modal.expiration.day-singular": "วัน",
|
||||
"account.reverseShares.modal.expiration.day-plural": "วัน",
|
||||
"account.reverseShares.modal.expiration.week-singular": "สัปดาห์",
|
||||
"account.reverseShares.modal.expiration.week-plural": "สัปดาห์",
|
||||
"account.reverseShares.modal.expiration.month-singular": "เดือน",
|
||||
"account.reverseShares.modal.expiration.month-plural": "เดือน",
|
||||
"account.reverseShares.modal.expiration.year-singular": "ปี",
|
||||
"account.reverseShares.modal.expiration.year-plural": "ปี",
|
||||
"account.reverseShares.modal.max-size.label": "ขนาดสูงสุดของแชร์",
|
||||
"account.reverseShares.modal.send-email": "ส่งอีเมล์์์์์์์แจ้งเตือน",
|
||||
"account.reverseShares.modal.send-email.description": "ส่งอีเมล์์์์์์์แจ้งเตือนเมื่อมีการสร้างแชร์ด้วยลิงค์รีเวิร์สนี้",
|
||||
"account.reverseShares.modal.max-use.label": "จำนวนการใช้งานสูงสุด",
|
||||
"account.reverseShares.modal.max-use.description": "จำนวนครั้งสูงสุดที่ลิงค์นี้สามารถใช้งานได้",
|
||||
"account.reverseShare.never-expires": "ลิงค์นี้ไม่มีวันหมดอายุ",
|
||||
"account.reverseShare.expires-on": "ลิงค์นี้จะหมดอายุใน {expiration}.",
|
||||
"account.reverseShares.table.no-shares": "ยังไม่มีการสร้างแชร์",
|
||||
"account.reverseShares.table.count.singular": "แชร์",
|
||||
"account.reverseShares.table.count.plural": "แชร์",
|
||||
"account.reverseShares.table.shares": "แชร์",
|
||||
"account.reverseShares.table.remaining": "เหลืออีก {count} ครั้ง",
|
||||
"account.reverseShares.table.max-size": "ขนาดสูงสุดของแชร์",
|
||||
"account.reverseShares.table.expires": "หมดอายุ",
|
||||
"account.reverseShares.modal.reverse-share-link": "ลิงค์รีเวิร์สแชร์",
|
||||
"account.reverseShares.modal.delete.title": "ลบลิงค์รีเวิร์สแชร์",
|
||||
"account.reverseShares.modal.delete.description": "คุณต้องการลบลิงค์รีเวิร์สแชร์นี้หรือไม่? หากคุณทำเช่นนั้นแชร์ที่เกี่ยวข้องจะถูกลบด้วย",
|
||||
// END /account/reverseShares
|
||||
// /admin
|
||||
"admin.title": "แผงควบคุมระบบ",
|
||||
"admin.button.users": "การจัดการผู้ใช้",
|
||||
"admin.button.config": "การตั้งค่า",
|
||||
"admin.version": "เวอร์ชัน",
|
||||
// END /admin
|
||||
// /admin/users
|
||||
"admin.users.title": "การจัดการผู้ใช้",
|
||||
"admin.users.table.username": "ชื่อผู้ใช้",
|
||||
"admin.users.table.email": "อีเมล์์์์์์์",
|
||||
"admin.users.table.admin": "ผู้ดูแลระบบ",
|
||||
"admin.users.edit.update.title": "อัปเดตผู้ใช้ {username}",
|
||||
"admin.users.edit.update.admin-privileges": "สิทธิ์ของผู้ดูแลระบบ",
|
||||
"admin.users.edit.update.change-password.title": "เปลี่ยนรหัสผ่าน",
|
||||
"admin.users.edit.update.change-password.field": "รหัสผ่านใหม่",
|
||||
"admin.users.edit.update.change-password.button": "บันทึกรหัสผ่านใหม่",
|
||||
"admin.users.edit.update.notify.password.success": "รหัสผ่านเปลี่ยนเรียบร้อยแล้ว",
|
||||
"admin.users.edit.delete.title": "ลบผู้ใช้ {username}",
|
||||
"admin.users.edit.delete.description": "คุณต้องการลบผู้ใช้นี้และการแชร์ทั้งหมดของเขาหรือไม่?",
|
||||
// showCreateUserModal.tsx
|
||||
"admin.users.modal.create.title": "สร้างผู้ใช้",
|
||||
"admin.users.modal.create.username": "ชื่อผู้ใช้",
|
||||
"admin.users.modal.create.email": "อีเมล์",
|
||||
"admin.users.modal.create.password": "รหัสผ่าน",
|
||||
"admin.users.modal.create.manual-password": "ตั้งรหัสผ่านด้วยตนเอง",
|
||||
"admin.users.modal.create.manual-password.description": "หากไม่ติ๊กเลือก ผู้ใช้จะได้รับอีเมล์พร้อมลิงก์เพื่อตั้งรหัสผ่านด้วยตนเอง",
|
||||
"admin.users.modal.create.admin": "สิทธิ์ของผู้ดูแลระบบ",
|
||||
"admin.users.modal.create.admin.description": "หากติ๊กเลือก ผู้ใช้จะสามารถเข้าถึงแผงควบคุมระบบได้",
|
||||
// END /admin/users
|
||||
// /upload
|
||||
"upload.title": "อัปโหลด",
|
||||
"upload.notify.generic-error": "เกิดข้อผิดพลาดขณะที่กำลังจัดการการแชร์ของคุณ",
|
||||
"upload.notify.count-failed": "มีไฟล์จำนวน {count} ไฟล์ที่ไม่สามารถอัปโหลดได้ กำลังลองอีกครั้ง",
|
||||
// Dropzone.tsx
|
||||
"upload.dropzone.title": "อัปโหลดไฟล์",
|
||||
"upload.dropzone.description": "ลากและวางไฟล์ที่นี่เพื่อเริ่มการแชร์ของคุณ สามารถรับไฟล์ที่มีขนาดรวมไม่เกิน {maxSize} เท่านั้น",
|
||||
"upload.dropzone.notify.file-too-big": "ไฟล์ของคุณเกินขนาดสูงสุดของการแชร์ {maxSize}",
|
||||
// FileList.tsx
|
||||
"upload.filelist.name": "ชื่อ",
|
||||
"upload.filelist.size": "ขนาด",
|
||||
// showCreateUploadModal.tsx
|
||||
"upload.modal.title": "สร้างการแชร์",
|
||||
"upload.modal.link.error.invalid": "สามารถใช้ได้เฉพาะตัวอักษร ตัวเลข ขีดล่าง และขีดเส้น",
|
||||
"upload.modal.link.error.taken": "ลิงก์นี้ถูกใช้งานแล้ว",
|
||||
"upload.modal.not-signed-in": "คุณยังไม่ได้เข้าสู่ระบบ",
|
||||
"upload.modal.not-signed-in-description": "คุณจะไม่สามารถลบการแชร์ของคุณด้วยตนเองและดูจำนวนผู้เข้าชมได้",
|
||||
"upload.modal.expires.never": "ไม่มีกำหนด",
|
||||
"upload.modal.expires.never-long": "ไม่มีกำหนดหมดอายุ",
|
||||
"upload.modal.link.label": "ลิงค์",
|
||||
"upload.modal.expires.label": "หมดอายุ",
|
||||
"upload.modal.expires.minute-singular": "นาที",
|
||||
"upload.modal.expires.minute-plural": "นาที",
|
||||
"upload.modal.expires.hour-singular": "ชั่วโมง",
|
||||
"upload.modal.expires.hour-plural": "ชั่วโมง",
|
||||
"upload.modal.expires.day-singular": "วัน",
|
||||
"upload.modal.expires.day-plural": "วัน",
|
||||
"upload.modal.expires.week-singular": "สัปดาห์",
|
||||
"upload.modal.expires.week-plural": "สัปดาห์",
|
||||
"upload.modal.expires.month-singular": "เดือน",
|
||||
"upload.modal.expires.month-plural": "เดือน",
|
||||
"upload.modal.expires.year-singular": "ปี",
|
||||
"upload.modal.expires.year-plural": "ปี",
|
||||
"upload.modal.accordion.description.title": "คำอธิบาย",
|
||||
"upload.modal.accordion.description.placeholder": "หมายเหตุสำหรับผู้รับการแชร์นี้",
|
||||
"upload.modal.accordion.email.title": "ผู้รับอีเมล์",
|
||||
"upload.modal.accordion.email.placeholder": "ป้อนผู้รับอีเมล์",
|
||||
"upload.modal.accordion.email.invalid-email": "ที่อยู่อีเมล์ไม่ถูกต้อง",
|
||||
"upload.modal.accordion.security.title": "ตัวเลือกความปลอดภัย",
|
||||
"upload.modal.accordion.security.password.label": "การป้องกันรหัสผ่าน",
|
||||
"upload.modal.accordion.security.password.placeholder": "ไม่มีรหัสผ่าน",
|
||||
"upload.modal.accordion.security.max-views.label": "จำนวนการเข้าชมสูงสุด",
|
||||
"upload.modal.accordion.security.max-views.placeholder": "ไม่จำกัด",
|
||||
// showCompletedUploadModal.tsx
|
||||
"upload.modal.completed.never-expires": "การแชร์นี้จะไม่มีวันหมดอายุ",
|
||||
"upload.modal.completed.expires-on": "การแชร์นี้จะหมดอายุเมื่อวันที่ {expiration}",
|
||||
"upload.modal.completed.share-ready": "แชร์พร้อมใช้งาน",
|
||||
// END /upload
|
||||
// /share/[id]
|
||||
"share.title": "แชร์ {shareId}",
|
||||
"share.description": "ดูสิ่งที่ฉันแชร์กับคุณ!",
|
||||
"share.error.visitor-limit-exceeded.title": "เกินขีดจำกัดผู้เข้าชม",
|
||||
"share.error.visitor-limit-exceeded.description": "การแชร์นี้ได้เกินขีดจำกัดผู้เข้าชมแล้ว",
|
||||
"share.error.removed.title": "การแชร์ถูกลบ",
|
||||
"share.error.not-found.title": "ไม่พบการแชร์นี้",
|
||||
"share.error.not-found.description": "การแชร์ที่คุณกำลังมองหาไม่มีอยู่จริง",
|
||||
"share.modal.password.title": "ต้องการรหัสผ่าน",
|
||||
"share.modal.password.description": "กรุณาป้อนรหัสผ่านเพื่อเข้าถึงการแชร์นี้",
|
||||
"share.modal.password": "รหัสผ่าน",
|
||||
"share.modal.error.invalid-password": "รหัสผ่านไม่ถูกต้อง",
|
||||
"share.button.download-all": "ดาวน์โหลดทั้งหมด",
|
||||
"share.notify.download-all-preparing": "กำลังเตรียมการแชร์นี้ โปรดลองอีกครั้งในไม่กี่นาที",
|
||||
"share.modal.file-link": "ลิงค์ไฟล์",
|
||||
"share.table.name": "ชื่อ",
|
||||
"share.table.size": "ขนาด",
|
||||
"share.modal.file-preview.error.not-supported.title": "ไม่รองรับการแสดงตัวอย่าง",
|
||||
"share.modal.file-preview.error.not-supported.description": "ไม่รองรับการแสดงตัวอย่างสำหรับไฟล์ประเภทนี้ โปรดดาวน์โหลดไฟล์เพื่อดู",
|
||||
// END /share/[id]
|
||||
// /admin/config
|
||||
"admin.config.title": "การตั้งค่า",
|
||||
"admin.config.category.general": "ทั่วไป",
|
||||
"admin.config.category.share": "การแชร์",
|
||||
"admin.config.category.email": "อีเมล์์์์์์",
|
||||
"admin.config.category.smtp": "SMTP",
|
||||
"admin.config.general.app-name": "ชื่อแอพ",
|
||||
"admin.config.general.app-name.description": "ชื่อแอพพลิเคชัน",
|
||||
"admin.config.general.app-url": "URL ของแอพ",
|
||||
"admin.config.general.app-url.description": "URL ที่สามารถเข้าถึงแอพพลิเคชัน Pingvin Share ได้",
|
||||
"admin.config.general.show-home-page": "แสดงหน้าแรก",
|
||||
"admin.config.general.show-home-page.description": "หากติ๊ก เว็บไซต์จะแสดงหน้าหลักเวลาเข้าถึง URL หลัก",
|
||||
"admin.config.general.logo": "โลโก้",
|
||||
"admin.config.general.logo.description": "เปลี่ยนโลโก้โดยอัปโหลดรูปภาพใหม่ รูปภาพต้องเป็น PNG และควรมีขนาดอัตราส่วน 1:1",
|
||||
"admin.config.general.logo.placeholder": "คลิกที่นี่หรือลากไฟล์มา",
|
||||
"admin.config.email.enable-share-email-recipients": "เปิดใช้งานผู้รับอีเมล์์์์์์ของการแชร์",
|
||||
"admin.config.email.enable-share-email-recipients.description": "เมื่อเปิดใช้งานผู้รับอีเมล์์์์์์ของการแชร์ ผู้ใช้ที่สร้างการแชร์จะสามารถเพิ่มผู้รับอีเมล์์์์์์์ของการแชร์ได้ เปิดเฉพาะเมื่อคุณเปิดใช้งาน SMTP",
|
||||
"admin.config.email.share-recipients-subject": "หัวเรื่องผู้รับอีเมล์์์์์์ของการแชร์",
|
||||
"admin.config.email.share-recipients-subject.description": "หัวเรื่องของอีเมล์์์์์์์์ที่ส่งไปยังผู้รับอีเมล์์์์์์ของการแชร์",
|
||||
"admin.config.email.share-recipients-message": "ข้อความผู้รับอีเมล์์์์์์ของการแชร์",
|
||||
"admin.config.email.share-recipients-message.description": "ข้อความที่ส่งไปยังผู้รับอีเมล์์์์์์ของการแชร์ ตัวแปรที่ใช้ได้:\n {creator} - ชื่อผู้สร้างการแชร์\n {shareUrl} - URL ของการแชร์\n {desc} - คำอธิบายของการแชร์\n {expires} - วันหมดอายุของการแชร์\n ตัวแปรจะถูกแทนที่ด้วยค่าจริง",
|
||||
"admin.config.email.reverse-share-subject": "หัวเรื่องการแชร์รีเวิร์ส",
|
||||
"admin.config.email.reverse-share-subject.description": "หัวเรื่องของอีเมล์์์์์์ที่ส่งไปยังผู้สร้างการแชร์รีเวิร์ส",
|
||||
"admin.config.email.reverse-share-message": "ข้อความการแชร์รีเวิร์ส",
|
||||
"admin.config.email.reverse-share-message.description": "ข้อความที่ส่งไปยังผู้สร้างการแชร์รีเวิร์ส ตัวแปรที่ใช้ได้:\n {shareUrl} - ชื่อผู้สร้างแชร์รีเวิร์สและ URL ของการแชร์\n ตัวแปรจะถูกแทนที่ด้วยค่าจริง",
|
||||
"admin.config.email.reset-password-subject": "หัวเรื่องการรีเซ็ตรหัสผ่าน",
|
||||
"admin.config.email.reset-password-subject.description": "หัวเรื่องของอีเมล์์์์์์ที่ส่งไปยังผู้ใช้เมื่อขอรีเซ็ตรหัสผ่าน",
|
||||
"admin.config.email.reset-password-message": "ข้อความการรีเซ็ตรหัสผ่าน",
|
||||
"admin.config.email.reset-password-message.description": "ข้อความที่ส่งไปยังผู้ใช้เมื่อขอรีเซ็ตรหัสผ่าน ตัวแปรที่ใช้ได้:\n {url} - URL สำหรับรีเซ็ตรหัสผ่าน\n ตัวแปรจะถูกแทนที่ด้วยค่าจริง",
|
||||
"admin.config.email.invite-subject": "หัวเรื่องการเชิญ",
|
||||
"admin.config.email.invite-subject.description": "หัวเรื่องของอีเมล์์์์์์ที่ส่งไปยังผู้ใช้เมื่อผู้ดูแลระบบเชิญผู้ใช้",
|
||||
"admin.config.email.invite-message": "ข้อความการเชิญ",
|
||||
"admin.config.email.invite-message.description": "ข้อความที่ส่งไปยังผู้ใช้เมื่อผู้ดูแลระบบเชิญผู้ใช้ ตัวแปรที่ใช้ได้:\n {url} - URL สำหรับเชิญผู้ใช้\n {password} - รหัสผ่านของผู้ใช้\n ตัวแปรจะถูกแทนที่ด้วยค่าจริง",
|
||||
"admin.config.share.allow-registration": "อนุญาตให้ลงทะเบียนด้วยตัวเอง",
|
||||
"admin.config.share.allow-registration.description": "อนุญาตให้ผู้ใช้ลงทะเบียนด้วยตัวเองเพื่อสร้างแชร์",
|
||||
"admin.config.share.allow-unauthenticated-shares": "อนุญาตให้แชร์โดยไม่ต้องเข้าสู่ระบบ",
|
||||
"admin.config.share.allow-unauthenticated-shares.description": "อนุญาตให้ผู้ใช้ที่ไม่ได้เข้าสู่ระบบสร้างแชร์",
|
||||
"admin.config.share.max-size": "ขนาดสูงสุด",
|
||||
"admin.config.share.max-size.description": "ขนาดสูงสุดของแชร์",
|
||||
"admin.config.share.zip-compression-level": "ระดับการบีบอัดไฟล์ Zip",
|
||||
"admin.config.share.zip-compression-level.description": "ปรับระดับเพื่อปรับความสมดุลระหว่างขนาดไฟล์และความเร็วในการบีบอัด ค่าอยู่ระหว่าง 0-9 โดย 0 คือไม่มีการบีบอัดและ 9 คือการบีบอัดสูงสุด",
|
||||
"admin.config.smtp.enabled": "เปิด",
|
||||
"admin.config.smtp.enabled.description": "เปิดใช้งาน SMTP สำหรับการส่งอีเมล์์์์์์ เปิดได้เท่านั้นต่อเมื่อคุณใส่ข้อมูลโฮสต์ พอร์ต อีเมล์ ผู้ใช้ และรหัสผ่านของเซิร์ฟเวอร์ SMTP ของคุณ",
|
||||
"admin.config.smtp.host": "โฮสต์",
|
||||
"admin.config.smtp.host.description": "โฮสต์ของเซิร์ฟเวอร์ SMTP",
|
||||
"admin.config.smtp.port": "พอร์ต",
|
||||
"admin.config.smtp.port.description": "พอร์ตของเซิร์ฟเวอร์ SMTP",
|
||||
"admin.config.smtp.email": "อีเมล์์์์์์์",
|
||||
"admin.config.smtp.email.description": "อีเมล์์์์์์์ที่ใช้สำหรับการส่งอีเมล์์์์์์์",
|
||||
"admin.config.smtp.username": "ชื่อผู้ใช้",
|
||||
"admin.config.smtp.username.description": "ชื่อผู้ใช้ของเซิร์ฟเวอร์ SMTP",
|
||||
"admin.config.smtp.password": "รหัสผ่าน",
|
||||
"admin.config.smtp.password.description": "รหัสผ่านของเซิร์ฟเวอร์ SMTP",
|
||||
"admin.config.smtp.button.test": "ส่งอีเมล์์์์์์ทดสอบ",
|
||||
// 404
|
||||
"404.description": "ไม่พบหน้าที่คุณกำลังมองหา",
|
||||
"404.button.home": "หน้าแรก",
|
||||
// Common translations
|
||||
"common.button.save": "บันทึก",
|
||||
"common.button.create": "สร้าง",
|
||||
"common.button.submit": "ส่ง",
|
||||
"common.button.delete": "ลบ",
|
||||
"common.button.cancel": "ยกเลิก",
|
||||
"common.button.confirm": "ยืนยัน",
|
||||
"common.button.disable": "ปิดการใช้งาน",
|
||||
"common.button.share": "แชร์",
|
||||
"common.button.generate": "สุ่ม",
|
||||
"common.button.done": "เสร็จสิ้น",
|
||||
"common.text.link": "ลิงค์",
|
||||
"common.text.or": "หรือ",
|
||||
"common.button.go-back": "ย้อนกลับ",
|
||||
"common.notify.copied": "คัดลอกไปยังคลิปบอร์ดแล้ว",
|
||||
"common.success": "สำเร็จ",
|
||||
"common.error": "เกิดข้อผิดพลาด",
|
||||
"common.error.unknown": "เกิดข้อผิดพลาดที่ไม่รู้จัก",
|
||||
"common.error.invalid-email": "ที่อยู่อีเมล์ไม่ถูกต้อง",
|
||||
"common.error.too-short": "ต้องมีอย่างน้อย {length} ตัวอักษร",
|
||||
"common.error.too-long": "ต้องมีไม่เกิน {length} ตัวอักษร",
|
||||
"common.error.exact-length": "ต้องมีความยาว {length} ตัวอักษร",
|
||||
"common.error.invalid-number": "ต้องเป็นตัวเลข",
|
||||
"common.error.field-required": "ต้องกรอกข้อมูลนี้"
|
||||
};
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"account.reverseShares.title.empty": "这里空空如也 👀",
|
||||
"account.reverseShares.description.empty": "你没有创建任何预留共享",
|
||||
// showCreateReverseShareModal.tsx
|
||||
"account.reverseShares.modal.title": "Create reverse share",
|
||||
"account.reverseShares.modal.expiration.label": "过期时间",
|
||||
"account.reverseShares.modal.expiration.minute-singular": "1 分钟",
|
||||
"account.reverseShares.modal.expiration.minute-plural": "分钟",
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function middleware(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const claims = jwtDecode<{ exp: number; isAdmin: boolean }>(
|
||||
accessToken as string
|
||||
accessToken as string,
|
||||
);
|
||||
if (claims.exp * 1000 > Date.now()) {
|
||||
user = claims;
|
||||
|
||||
@@ -43,9 +43,7 @@ const ErrorNotFound = () => {
|
||||
<>
|
||||
<Meta title="Not found" />
|
||||
<Container className={classes.root}>
|
||||
<div className={classes.label}>
|
||||
404
|
||||
</div>
|
||||
<div className={classes.label}>404</div>
|
||||
<Title align="center" order={3}>
|
||||
<FormattedMessage id="404.description" />
|
||||
</Title>
|
||||
|
||||
@@ -41,7 +41,7 @@ function App({ Component, pageProps }: AppProps) {
|
||||
const [route, setRoute] = useState<string>(pageProps.route);
|
||||
|
||||
const [configVariables, setConfigVariables] = useState<Config[]>(
|
||||
pageProps.configVariables
|
||||
pageProps.configVariables,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -165,7 +165,7 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => {
|
||||
pageProps.route = ctx.req.url;
|
||||
|
||||
const requestLanguage = i18nUtil.getLanguageFromAcceptHeader(
|
||||
ctx.req.headers["accept-language"]
|
||||
ctx.req.headers["accept-language"],
|
||||
);
|
||||
|
||||
pageProps.language = ctx.req.cookies["language"] ?? requestLanguage;
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Tb2Fa } from "react-icons/tb";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import * as yup from "yup";
|
||||
import Meta from "../../components/Meta";
|
||||
import LanguagePicker from "../../components/account/LanguagePicker";
|
||||
import ThemeSwitcher from "../../components/account/ThemeSwitcher";
|
||||
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
||||
import useTranslate from "../../hooks/useTranslate.hook";
|
||||
@@ -24,7 +25,6 @@ import useUser from "../../hooks/user.hook";
|
||||
import authService from "../../services/auth.service";
|
||||
import userService from "../../services/user.service";
|
||||
import toast from "../../utils/toast.util";
|
||||
import LanguagePicker from "../../components/account/LanguagePicker";
|
||||
|
||||
const Account = () => {
|
||||
const { user, refreshUser } = useUser();
|
||||
@@ -42,7 +42,7 @@ const Account = () => {
|
||||
username: yup
|
||||
.string()
|
||||
.min(3, t("common.error.too-short", { length: 3 })),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ const Account = () => {
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ const Account = () => {
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ const Account = () => {
|
||||
.min(6, t("common.error.exact-length", { length: 6 }))
|
||||
.max(6, t("common.error.exact-length", { length: 6 }))
|
||||
.matches(/^[0-9]+$/, { message: t("common.error.invalid-number") }),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ const Account = () => {
|
||||
email: values.email,
|
||||
})
|
||||
.then(() => toast.success(t("account.notify.info.success")))
|
||||
.catch(toast.axiosError)
|
||||
.catch(toast.axiosError),
|
||||
)}
|
||||
>
|
||||
<Stack>
|
||||
@@ -147,7 +147,7 @@ const Account = () => {
|
||||
toast.success(t("account.notify.password.success"));
|
||||
passwordForm.reset();
|
||||
})
|
||||
.catch(toast.axiosError)
|
||||
.catch(toast.axiosError),
|
||||
)}
|
||||
>
|
||||
<Stack>
|
||||
@@ -181,7 +181,7 @@ const Account = () => {
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="totp" pt="xs">
|
||||
{user!.totpVerified ? (
|
||||
{user?.totpVerified ? (
|
||||
<>
|
||||
<form
|
||||
onSubmit={disableTotpForm.onSubmit((values) => {
|
||||
@@ -199,7 +199,7 @@ const Account = () => {
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
description={t(
|
||||
"account.card.security.totp.disable.description"
|
||||
"account.card.security.totp.disable.description",
|
||||
)}
|
||||
label={t("account.card.password.title")}
|
||||
{...disableTotpForm.getInputProps("password")}
|
||||
@@ -241,7 +241,7 @@ const Account = () => {
|
||||
<PasswordInput
|
||||
label={t("account.card.password.title")}
|
||||
description={t(
|
||||
"account.card.security.totp.enable.description"
|
||||
"account.card.security.totp.enable.description",
|
||||
)}
|
||||
{...enableTotpForm.getInputProps("password")}
|
||||
/>
|
||||
|
||||
@@ -77,7 +77,7 @@ const MyShares = () => {
|
||||
showCreateReverseShareModal(
|
||||
modals,
|
||||
config.get("smtp.enabled"),
|
||||
getReverseShares
|
||||
getReverseShares,
|
||||
)
|
||||
}
|
||||
leftIcon={<TbPlus size={20} />}
|
||||
@@ -134,10 +134,10 @@ const MyShares = () => {
|
||||
<Text size="sm">
|
||||
{reverseShare.shares.length == 1
|
||||
? `1 ${t(
|
||||
"account.reverseShares.table.count.singular"
|
||||
"account.reverseShares.table.count.singular",
|
||||
)}`
|
||||
: `${reverseShare.shares.length} ${t(
|
||||
"account.reverseShares.table.count.plural"
|
||||
"account.reverseShares.table.count.plural",
|
||||
)}`}
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
@@ -158,15 +158,13 @@ const MyShares = () => {
|
||||
size={25}
|
||||
onClick={() => {
|
||||
if (window.isSecureContext) {
|
||||
clipboard.copy(
|
||||
`${appUrl}/s/${share.id}`
|
||||
);
|
||||
clipboard.copy(`${appUrl}/s/${share.id}`);
|
||||
toast.success(t("common.notify.copied"));
|
||||
} else {
|
||||
showShareLinkModal(
|
||||
modals,
|
||||
share.id,
|
||||
config.get("general.appUrl")
|
||||
config.get("general.appUrl"),
|
||||
);
|
||||
}
|
||||
}}
|
||||
@@ -200,14 +198,14 @@ const MyShares = () => {
|
||||
clipboard.copy(
|
||||
`${config.get("general.appUrl")}/upload/${
|
||||
reverseShare.token
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
toast.success(t("common.notify.copied"));
|
||||
} else {
|
||||
showReverseShareLinkModal(
|
||||
modals,
|
||||
reverseShare.token,
|
||||
config.get("general.appUrl")
|
||||
config.get("general.appUrl"),
|
||||
);
|
||||
}
|
||||
}}
|
||||
@@ -221,7 +219,7 @@ const MyShares = () => {
|
||||
onClick={() => {
|
||||
modals.openConfirmModal({
|
||||
title: t(
|
||||
"account.reverseShares.modal.delete.title"
|
||||
"account.reverseShares.modal.delete.title",
|
||||
),
|
||||
children: (
|
||||
<Text size="sm">
|
||||
@@ -239,8 +237,8 @@ const MyShares = () => {
|
||||
shareService.removeReverseShare(reverseShare.id);
|
||||
setReverseShares(
|
||||
reverseShares.filter(
|
||||
(item) => item.id !== reverseShare.id
|
||||
)
|
||||
(item) => item.id !== reverseShare.id,
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ const MyShares = () => {
|
||||
modals,
|
||||
share,
|
||||
config.get("general.appUrl"),
|
||||
parseInt(config.get("share.maxSize"))
|
||||
parseInt(config.get("share.maxSize")),
|
||||
);
|
||||
}}
|
||||
>
|
||||
@@ -132,16 +132,14 @@ const MyShares = () => {
|
||||
onClick={() => {
|
||||
if (window.isSecureContext) {
|
||||
clipboard.copy(
|
||||
`${config.get("general.appUrl")}/s/${
|
||||
share.id
|
||||
}`
|
||||
`${config.get("general.appUrl")}/s/${share.id}`,
|
||||
);
|
||||
toast.success(t("common.notify.copied"));
|
||||
} else {
|
||||
showShareLinkModal(
|
||||
modals,
|
||||
share.id,
|
||||
config.get("general.appUrl")
|
||||
config.get("general.appUrl"),
|
||||
);
|
||||
}
|
||||
}}
|
||||
@@ -172,7 +170,7 @@ const MyShares = () => {
|
||||
onConfirm: () => {
|
||||
shareService.remove(share.id);
|
||||
setShares(
|
||||
shares.filter((item) => item.id !== share.id)
|
||||
shares.filter((item) => item.id !== share.id),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function AppShellDemo() {
|
||||
.then(() => {
|
||||
setLogo(null);
|
||||
toast.success(
|
||||
"Logo updated successfully. It may take a few minutes to update on the website."
|
||||
"Logo updated successfully. It may take a few minutes to update on the website.",
|
||||
);
|
||||
})
|
||||
.catch(toast.axiosError);
|
||||
@@ -76,7 +76,7 @@ export default function AppShellDemo() {
|
||||
|
||||
const updateConfigVariable = (configVariable: UpdateConfig) => {
|
||||
const index = updatedConfigVariables.findIndex(
|
||||
(item) => item.key === configVariable.key
|
||||
(item) => item.key === configVariable.key,
|
||||
);
|
||||
|
||||
if (index > -1) {
|
||||
@@ -139,7 +139,7 @@ export default function AppShellDemo() {
|
||||
<Title order={6}>
|
||||
<FormattedMessage
|
||||
id={`admin.config.${camelToKebab(
|
||||
configVariable.key
|
||||
configVariable.key,
|
||||
)}`}
|
||||
/>
|
||||
</Title>
|
||||
@@ -154,7 +154,7 @@ export default function AppShellDemo() {
|
||||
>
|
||||
<FormattedMessage
|
||||
id={`admin.config.${camelToKebab(
|
||||
configVariable.key
|
||||
configVariable.key,
|
||||
)}.description`}
|
||||
values={{ br: <br /> }}
|
||||
/>
|
||||
|
||||
@@ -11,8 +11,10 @@ export const config = {
|
||||
|
||||
const { apiURL } = getConfig().serverRuntimeConfig;
|
||||
|
||||
// A proxy to the API server only used in development.
|
||||
// In production this route gets overridden by nginx.
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return httpProxyMiddleware(req, res, {
|
||||
httpProxyMiddleware(req, res, {
|
||||
headers: {
|
||||
"X-Forwarded-For": req.socket?.remoteAddress ?? "",
|
||||
},
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import getConfig from "next/config";
|
||||
|
||||
const { apiURL } = getConfig().serverRuntimeConfig;
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const apiStatus = await axios
|
||||
.get(`${apiURL}/api/configs`)
|
||||
.then(() => "OK")
|
||||
.catch(() => "ERROR");
|
||||
|
||||
res.status(apiStatus == "OK" ? 200 : 500).send(apiStatus);
|
||||
};
|
||||
@@ -39,7 +39,7 @@ const ResetPassword = () => {
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ const ResetPassword = () => {
|
||||
.string()
|
||||
.email(t("common.error.invalid-email"))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ const ResetPassword = () => {
|
||||
toast.success(t("resetPassword.notify.success"));
|
||||
router.push("/auth/signIn");
|
||||
})
|
||||
.catch(toast.axiosError)
|
||||
.catch(toast.axiosError),
|
||||
)}
|
||||
>
|
||||
<TextInput
|
||||
|
||||
@@ -36,7 +36,7 @@ const Share = ({ shareId }: { shareId: string }) => {
|
||||
showErrorModal(
|
||||
modals,
|
||||
t("share.error.visitor-limit-exceeded.title"),
|
||||
t("share.error.visitor-limit-exceeded.description")
|
||||
t("share.error.visitor-limit-exceeded.description"),
|
||||
);
|
||||
} else {
|
||||
toast.axiosError(e);
|
||||
@@ -57,13 +57,13 @@ const Share = ({ shareId }: { shareId: string }) => {
|
||||
showErrorModal(
|
||||
modals,
|
||||
t("share.error.removed.title"),
|
||||
e.response.data.message
|
||||
e.response.data.message,
|
||||
);
|
||||
} else {
|
||||
showErrorModal(
|
||||
modals,
|
||||
t("share.error.not-found.title"),
|
||||
t("share.error.not-found.description")
|
||||
t("share.error.not-found.description"),
|
||||
);
|
||||
}
|
||||
} else if (error == "share_password_required") {
|
||||
|
||||
@@ -29,7 +29,7 @@ const Share = ({ reverseShareToken }: { reverseShareToken: string }) => {
|
||||
showErrorModal(
|
||||
modals,
|
||||
"Invalid Link",
|
||||
"This link is invalid. Please check your link."
|
||||
"This link is invalid. Please check your link.",
|
||||
);
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ const Upload = ({
|
||||
|
||||
maxShareSize ??= parseInt(config.get("share.maxSize"));
|
||||
|
||||
const uploadFiles = async (share: CreateShare) => {
|
||||
const uploadFiles = async (share: CreateShare, files: FileUpload[]) => {
|
||||
setisUploading(true);
|
||||
createdShare = await shareService.create(share);
|
||||
|
||||
@@ -120,6 +120,24 @@ const Upload = ({
|
||||
Promise.all(fileUploadPromises);
|
||||
};
|
||||
|
||||
const showCreateUploadModalCallback = (files: FileUpload[]) => {
|
||||
setFiles(files);
|
||||
showCreateUploadModal(
|
||||
modals,
|
||||
{
|
||||
isUserSignedIn: user ? true : false,
|
||||
isReverseShare,
|
||||
appUrl: config.get("general.appUrl"),
|
||||
allowUnauthenticatedShares: config.get(
|
||||
"share.allowUnauthenticatedShares"
|
||||
),
|
||||
enableEmailRecepients: config.get("email.enableShareEmailRecipients"),
|
||||
},
|
||||
files,
|
||||
uploadFiles
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Check if there are any files that failed to upload
|
||||
const fileErrorCount = files.filter(
|
||||
@@ -166,31 +184,14 @@ const Upload = ({
|
||||
<Button
|
||||
loading={isUploading}
|
||||
disabled={files.length <= 0}
|
||||
onClick={() => {
|
||||
showCreateUploadModal(
|
||||
modals,
|
||||
{
|
||||
isUserSignedIn: user ? true : false,
|
||||
isReverseShare,
|
||||
appUrl: config.get("general.appUrl"),
|
||||
allowUnauthenticatedShares: config.get(
|
||||
"share.allowUnauthenticatedShares"
|
||||
),
|
||||
enableEmailRecepients: config.get(
|
||||
"email.enableShareEmailRecipients"
|
||||
),
|
||||
},
|
||||
uploadFiles
|
||||
);
|
||||
}}
|
||||
onClick={() => showCreateUploadModalCallback(files)}
|
||||
>
|
||||
<FormattedMessage id="common.button.share" />
|
||||
</Button>
|
||||
</Group>
|
||||
<Dropzone
|
||||
maxShareSize={maxShareSize}
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
showCreateUploadModalCallback={showCreateUploadModalCallback}
|
||||
isUploading={isUploading}
|
||||
/>
|
||||
{files.length > 0 && <FileList files={files} setFiles={setFiles} />}
|
||||
|
||||
@@ -19,7 +19,7 @@ const signInTotp = async (
|
||||
emailOrUsername: string,
|
||||
password: string,
|
||||
totp: string,
|
||||
loginToken: string
|
||||
loginToken: string,
|
||||
) => {
|
||||
const emailOrUsernameBody = emailOrUsername.includes("@")
|
||||
? { email: emailOrUsername }
|
||||
|
||||
@@ -18,7 +18,7 @@ const get = (key: string, configVariables: Config[]): any => {
|
||||
if (!configVariables) return null;
|
||||
|
||||
const configVariable = configVariables.filter(
|
||||
(variable) => variable.key == key
|
||||
(variable) => variable.key == key,
|
||||
)[0];
|
||||
|
||||
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
||||
@@ -42,7 +42,7 @@ const sendTestEmail = async (email: string) => {
|
||||
const isNewReleaseAvailable = async () => {
|
||||
const response = (
|
||||
await axios.get(
|
||||
"https://api.github.com/repos/stonith404/pingvin-share/releases/latest"
|
||||
"https://api.github.com/repos/stonith404/pingvin-share/releases/latest",
|
||||
)
|
||||
).data;
|
||||
return response.tag_name.replace("v", "") != process.env.VERSION;
|
||||
|
||||
@@ -71,7 +71,7 @@ const uploadFile = async (
|
||||
name: string;
|
||||
},
|
||||
chunkIndex: number,
|
||||
totalChunks: number
|
||||
totalChunks: number,
|
||||
): Promise<FileUploadResponse> => {
|
||||
const data = readerEvent.target!.result;
|
||||
|
||||
@@ -92,7 +92,7 @@ const createReverseShare = async (
|
||||
shareExpiration: string,
|
||||
maxShareSize: number,
|
||||
maxUseCount: number,
|
||||
sendEmailNotification: boolean
|
||||
sendEmailNotification: boolean,
|
||||
) => {
|
||||
return (
|
||||
await api.post("reverseShares", {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user