1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-01-16 20:58:22 +01:00

Merge branch 'main' into dev-v2

# Conflicts:
#	.github/workflows/build-docker.yml
#	app/Actions/ImportHtmlBookmarks.php
#	app/Helper/functions.php
#	app/Http/Controllers/Guest/LinkController.php
#	app/Http/Controllers/Guest/ListController.php
#	app/Http/Controllers/Guest/TagController.php
#	app/Http/Controllers/Models/LinkController.php
#	app/Http/Controllers/Models/ListController.php
#	app/Http/Controllers/Models/TagController.php
#	app/Http/Controllers/Traits/SearchesLinks.php
#	app/Http/Middleware/TrustProxies.php
#	composer.json
#	composer.lock
#	config/app.php
#	resources/views/partials/configure-darkmode.blade.php
#	tests/Controller/Models/LinkControllerTest.php
#	tests/Controller/Models/ListControllerTest.php
#	tests/Controller/Models/TagControllerTest.php
This commit is contained in:
Kovah 2023-09-28 19:42:45 +02:00
commit c084752b95
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
89 changed files with 2490 additions and 8115 deletions

6
.codeclimate.yml Normal file
View File

@ -0,0 +1,6 @@
engines:
sonar-php:
enabled: true
checks:
php:S1192:
enabled: false

View File

@ -14,7 +14,7 @@ APP_DEBUG=true
## Configuration of the database connection
## Attention: Those settings are configured during the web setup, please do not modify them now.
# Set the database driver (mysql, pgsql, sqlsrv)
# Set the database driver (mysql, pgsql, sqlsrv, sqlite)
DB_CONNECTION=mysql
# Set the host of your database here
DB_HOST=db

View File

@ -10,7 +10,7 @@ APP_KEY=someRandomStringWith32Characters
## Configuration of the database connection
## Attention: Those settings are configured during the web setup, please do not modify them now.
# Set the database driver (mysql, pgsql, sqlsrv)
# Set the database driver (mysql, pgsql, sqlsrv, sqlite)
DB_CONNECTION=mysql
# Set the host of your database here
DB_HOST=db

View File

@ -6,7 +6,7 @@ APP_KEY=someRandomStringWith32Characters
## Configuration of the database connection
## Attention: Those settings are configured during the web setup, please do not modify them now.
# Set the database driver (mysql, pgsql, sqlsrv)
# Set the database driver (mysql, pgsql, sqlsrv, sqlite)
DB_CONNECTION=mysql
# Set the host of your database here
DB_HOST=127.0.0.1

View File

@ -13,7 +13,7 @@ jobs:
continue-on-error: true
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Use Node.js 18.x
uses: actions/setup-node@v2
@ -37,7 +37,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -50,7 +50,7 @@ jobs:
run: composer install --prefer-dist --no-progress --no-suggest
- name: Test & publish code coverage
uses: paambaati/codeclimate-action@v2.6.0
uses: paambaati/codeclimate-action@v3.2.0
env:
CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
with:

View File

@ -26,6 +26,22 @@ jobs:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to DockerHub
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare tags
id: prep
run: |
DOCKER_IMAGE=linkace/linkace
DOCKER_IMAGE_GITUHB=ghcr.io/kovah/linkace
VERSION=${GITHUB_REF#refs/tags/}
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:latest,${DOCKER_IMAGE_GITUHB}:${VERSION},${DOCKER_IMAGE_GITUHB}:latest"
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- name: Build and push advanced image
uses: docker/build-push-action@v5
with:
@ -42,7 +58,7 @@ jobs:
DOCKER_IMAGE_GITHUB=ghcr.io/kovah/linkace
VERSION=${GITHUB_REF#refs/tags/}
TAGS="${DOCKER_IMAGE}:${VERSION}-php-nginx,${DOCKER_IMAGE}:php-nginx,${DOCKER_IMAGE}:${VERSION}-simple,${DOCKER_IMAGE}:simple,${DOCKER_IMAGE_GITHUB}:${VERSION}-php-nginx,${DOCKER_IMAGE_GITHUB}:php-nginx,${DOCKER_IMAGE_GITHUB}:${VERSION}-simple,${DOCKER_IMAGE_GITHUB}:simple"
echo ::set-output name=tags::${TAGS}
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- name: Build and push simple image
uses: docker/build-push-action@v5

View File

@ -29,13 +29,13 @@ jobs:
npm run production
- name: Upload built assets
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: assets
path: public/assets/dist
- name: Upload mix manifest
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: mix-manifest
path: public/mix-manifest.json
@ -47,12 +47,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: assets
path: public/assets/dist
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: mix-manifest
path: public
@ -65,7 +65,7 @@ jobs:
- id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- uses: actions/cache@v1
- uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -91,7 +91,7 @@ jobs:
filename: linkace.zip
exclusions: '*.git*'
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: linkace-package
path: linkace.zip
@ -112,7 +112,7 @@ jobs:
with:
args: zip -qq linkace-package-docker-simple.zip docker-compose.yml .env LICENSE.md README.md
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: linkace-package-docker-simple
path: linkace-package-docker-simple.zip
@ -124,7 +124,7 @@ jobs:
with:
args: zip -qq linkace-package-docker-advanced.zip docker-compose.yml .env nginx.conf nginx-ssl.conf LICENSE.md README.md
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: linkace-package-docker-advanced
path: linkace-package-docker-advanced.zip

View File

@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Use Node.js 18 LTS
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: 18
@ -23,13 +23,13 @@ jobs:
npm run production
- name: Upload built assets
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: assets
path: public/assets/dist
- name: Upload mix manifest
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: mix-manifest
path: public/mix-manifest.json
@ -47,12 +47,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v1
- uses: actions/download-artifact@v3
with:
name: assets
path: public/assets/dist
- uses: actions/download-artifact@v1
- uses: actions/download-artifact@v3
with:
name: mix-manifest
path: public
@ -68,7 +68,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -89,13 +89,13 @@ jobs:
- name: Run PHPunit
run: composer run test
- uses: actions/upload-artifact@v1
- uses: actions/upload-artifact@v3
if: failure()
with:
name: application-logs
path: storage/logs
- uses: actions/upload-artifact@v1
- uses: actions/upload-artifact@v3
if: failure()
with:
name: application-public

4
.gitignore vendored
View File

@ -16,6 +16,10 @@ Homestead.yaml
npm-debug.log
yarn-error.log
.env
.env.*
!.env.docker
!.env.docker.production
!.env.example
.phpunit.result.cache
_ide_helper.php
_ide_helper_models.php

View File

@ -17,10 +17,7 @@ class ImportHtmlBookmarks
public function run(string $data, string $userId, bool $generateMeta = true): bool
{
$parser = new NetscapeBookmarkParser(
defaultPub: usersettings('links_private_default'),
logDir: storage_path('logs')
);
$parser = new NetscapeBookmarkParser(logger: Log::channel('import'));
try {
$links = $parser->parseString($data);
@ -30,29 +27,30 @@ class ImportHtmlBookmarks
}
foreach ($links as $link) {
if (Link::whereUrl($link['uri'])->first()) {
if (Link::whereUrl($link['url'])->first()) {
$this->skipped++;
continue;
}
if ($generateMeta) {
$linkMeta = (new HtmlMeta)->getFromUrl($link['uri']);
$title = $link['title'] ?: $linkMeta['title'];
$description = $link['note'] ?: $linkMeta['description'];
$linkMeta = (new HtmlMeta)->getFromUrl($link['url']);
$title = $link['name'] ?: $linkMeta['title'];
$description = $link['description'] ?: $linkMeta['description'];
} else {
$title = $link['title'];
$description = $link['note'];
$title = $link['name'];
$description = $link['description'];
}
$isPublic = $link['public'] ?? true;
$newLink = new Link([
'user_id' => $userId,
'url' => $link['uri'],
'url' => $link['url'],
'title' => $title,
'description' => $description,
'icon' => LinkIconMapper::getIconForUrl($link['uri']),
'is_private' => $link['pub']
'is_private' => usersettings('tags_private_default') === '1' ? true : $isPublic,
]);
$newLink->created_at = Carbon::createFromTimestamp($link['time']);
$newLink->created_at = Carbon::createFromTimestamp($link['dateCreated']);
$newLink->updated_at = Carbon::now();
$newLink->timestamps = false;
$newLink->save();

View File

@ -72,7 +72,7 @@ class HtmlMeta
$this->fallback = [
'success' => false,
'title' => parse_url($this->url, PHP_URL_HOST) ?? $this->url,
'description' => false,
'description' => null,
'thumbnail' => null,
];
}

View File

@ -261,3 +261,13 @@ function bookmarkletUrl(): string
return str_replace('##URL##', route('bookmarklet-add'), $bmCode);
}
/**
* Get a list of all routes that are accessible by guests.
*
* @return string[]
*/
function guestRoutes(): array
{
return ['guest/*', 'login', 'forgot-password', 'reset-password/*', 'two-factor-challenge', 'email/verify'];
}

View File

@ -27,6 +27,7 @@ class SearchController extends Controller
'only_lists' => '',
'only_tags' => '',
'order_by' => $this->orderByOptions[0],
'performed_search' => false,
]);
}
@ -49,6 +50,7 @@ class SearchController extends Controller
'empty_tags' => $this->emptyTags,
'empty_lists' => $this->emptyLists,
'order_by' => $this->searchOrderBy,
'performed_search' => true,
]);
}
}

View File

@ -3,24 +3,35 @@
namespace App\Http\Controllers\Guest;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Models\Link;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
class LinkController extends Controller
{
use HandlesQueryOrder;
/**
* Display an overview of all links.
*
* @param Request $request
* @return View
*/
public function index(Request $request): View
{
$links = Link::publicOnly()
->with(['tags' => fn ($query) => $query->publicOnly()])
->orderBy(
$request->input('orderBy', 'created_at'),
$request->input('orderDir', 'desc')
)
->paginate(getPaginationLimit());
$links = Link::publicOnly()->with(['tags' => fn ($query) => $query->publicOnly()]);
$orderBy = $request->input('orderBy', 'created_at');
$orderDir = $this->getOrderDirection($request);
if ($orderBy === 'random') {
$links->inRandomOrder();
} else {
$links->orderBy($orderBy, $orderDir);
}
return view('guest.links.index', [
'links' => $links,
'links' => $links->paginate(getPaginationLimit()),
'route' => $request->getBaseUrl(),
'orderBy' => $request->input('orderBy', 'created_at'),
'orderDir' => $request->input('orderDir', 'desc'),

View File

@ -3,26 +3,29 @@
namespace App\Http\Controllers\Guest;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Models\LinkList;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
class ListController extends Controller
{
use HandlesQueryOrder;
public function index(Request $request): View
{
$lists = LinkList::publicOnly()
->withCount(['links' => fn ($query) => $query->publicOnly()])
->orderBy(
$request->input('orderBy', 'name'),
$request->input('orderDir', 'asc')
$this->getOrderDirection($request, 'asc')
)
->paginate(getPaginationLimit());
return view('guest.lists.index', [
'lists' => $lists,
'orderBy' => $request->input('orderBy', 'name'),
'orderDir' => $request->input('orderDir', 'asc'),
'orderDir' => $this->getOrderDirection($request, 'asc'),
]);
}
@ -34,7 +37,7 @@ class ListController extends Controller
->publicOnly()
->orderBy(
$request->input('orderBy', 'title'),
$request->input('orderDir', 'asc')
$this->getOrderDirection($request, 'asc')
)->paginate(getPaginationLimit());
return view('guest.lists.show', [
@ -42,7 +45,7 @@ class ListController extends Controller
'listLinks' => $links,
'route' => $request->getBaseUrl(),
'orderBy' => $request->input('orderBy', 'title'),
'orderDir' => $request->input('orderDir', 'asc'),
'orderDir' => $this->getOrderDirection($request, 'asc'),
]);
}
}

View File

@ -3,19 +3,22 @@
namespace App\Http\Controllers\Guest;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Models\Tag;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
class TagController extends Controller
{
use HandlesQueryOrder;
public function index(Request $request): View
{
$tags = Tag::publicOnly()
->withCount(['links' => fn ($query) => $query->publicOnly()])
->orderBy(
$request->input('orderBy', 'name'),
$request->input('orderDir', 'asc')
$this->getOrderDirection($request, 'asc')
)
->paginate(getPaginationLimit());
@ -23,7 +26,7 @@ class TagController extends Controller
'tags' => $tags,
'route' => $request->getBaseUrl(),
'orderBy' => $request->input('orderBy', 'name'),
'orderDir' => $request->input('orderDir', 'asc'),
'orderDir' => $this->getOrderDirection($request, 'asc'),
]);
}

View File

@ -4,9 +4,9 @@ namespace App\Http\Controllers\Models;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\ChecksOrdering;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Http\Requests\Models\LinkStoreRequest;
use App\Http\Requests\Models\LinkUpdateRequest;
use App\Http\Requests\Models\MarkLinkWorkingRequest;
use App\Http\Requests\Models\ToggleLinkCheckRequest;
use App\Models\Link;
use App\Repositories\LinkRepository;
@ -17,6 +17,7 @@ use Illuminate\Http\Request;
class LinkController extends Controller
{
use ChecksOrdering;
use HandlesQueryOrder;
public function __construct()
{
@ -26,8 +27,8 @@ class LinkController extends Controller
public function index(Request $request): View
{
$this->orderBy = $request->input('orderBy', session()->get('links.index.orderBy', 'created_at'));
$this->orderDir = $request->input('orderDir', session()->get('links.index.orderDir', 'desc'));
$orderBy = $request->input('orderBy', session()->get('links.index.orderBy', 'created_at'));
$orderDir = $this->getOrderDirection($request, session()->get('links.index.orderDir', 'desc'));
$this->checkOrdering();
@ -36,12 +37,16 @@ class LinkController extends Controller
$links = Link::query()
->visibleForUser()
->with('tags')
->orderBy($this->orderBy, $this->orderDir)
->paginate(getPaginationLimit());
->with('tags');
if ($orderBy === 'random') {
$links->inRandomOrder();
} else {
$links->orderBy($orderBy, $orderDir);
}
return view('models.links.index', [
'links' => $links,
'links' => $links->paginate(getPaginationLimit()),
'route' => $request->getBaseUrl(),
'orderBy' => $this->orderBy,
'orderDir' => $this->orderDir,

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Models;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\ChecksOrdering;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Http\Requests\Models\ListStoreRequest;
use App\Http\Requests\Models\ListUpdateRequest;
use App\Models\LinkList;
@ -15,6 +16,7 @@ use Illuminate\Http\Request;
class ListController extends Controller
{
use ChecksOrdering;
use HandlesQueryOrder;
public function __construct()
{
@ -24,13 +26,12 @@ class ListController extends Controller
public function index(Request $request): View
{
$this->orderBy = $request->input('orderBy', session()->get('lists.index.orderBy', 'name'));
$this->orderDir = $request->input('orderDir', session()->get('lists.index.orderDir', 'asc'));
$orderBy = $request->input('orderBy', session()->get('lists.index.orderBy', 'name'));
$orderDir = $this->getOrderDirection($request, session()->get('lists.index.orderDir', 'asc'));
$this->checkOrdering();
session()->put('lists.index.orderBy', $this->orderBy);
session()->put('lists.index.orderDir', $this->orderDir);
session()->put('lists.index.orderBy', $orderBy);
session()->put('lists.index.orderDir', $orderDir);
$lists = LinkList::query()
->visibleForUser()

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Models;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\ChecksOrdering;
use App\Http\Controllers\Traits\HandlesQueryOrder;
use App\Http\Requests\Models\TagStoreRequest;
use App\Http\Requests\Models\TagUpdateRequest;
use App\Models\Link;
@ -26,8 +27,8 @@ class TagController extends Controller
public function index(Request $request): View
{
$this->orderBy = $request->input('orderBy', session()->get('tags.index.orderBy', 'name'));
$this->orderDir = $request->input('orderDir', session()->get('tags.index.orderDir', 'asc'));
$orderBy = $request->input('orderBy', session()->get('tags.index.orderBy', 'name'));
$orderDir = $this->getOrderDirection($request, session()->get('tags.index.orderDir', 'asc'));
$this->checkOrdering();
@ -76,7 +77,7 @@ class TagController extends Controller
{
$this->allowedOrderBy = Link::$allowOrderBy;
$this->orderBy = $request->input('orderBy', 'created_at');
$this->orderDir = $request->input('orderDir', 'desc');
$this->orderDir = $this->getOrderDirection($request);
$this->checkOrdering();
@ -90,7 +91,7 @@ class TagController extends Controller
'tagLinks' => $links,
'route' => $request->getBaseUrl(),
'orderBy' => $request->input('orderBy', 'created_at'),
'orderDir' => $request->input('orderDir', 'desc'),
'orderDir' => $this->getOrderDirection($request),
]);
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Http\Controllers\Traits;
use Illuminate\Http\Request;
trait HandlesQueryOrder
{
protected function getOrderDirection(Request $request, $default = 'desc'): string
{
$dir = $request->input('orderDir');
return in_array($dir, ['asc', 'desc']) ? $dir : $default;
}
}

View File

@ -90,8 +90,12 @@ trait SearchesLinks
// Order the results if applicable and only allow predefined ordering
if ($this->searchOrderBy = $request->input('order_by')) {
$this->searchOrderBy = in_array($this->searchOrderBy, $this->orderByOptions)
? $this->searchOrderBy : $this->orderByOptions[0];
if ($this->searchOrderBy === 'random') {
$search->inRandomOrder();
} else {
$this->searchOrderBy = in_array($this->searchOrderBy, $this->orderByOptions)
? $this->searchOrderBy : $this->orderByOptions[0];
}
$search->orderBy(...explode(':', $this->searchOrderBy));
}

View File

@ -3,6 +3,7 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
@ -12,7 +13,12 @@ class TrustProxies extends Middleware
*
* @var array<int, string>|string|null
*/
protected $proxies = '*';
protected $proxies = null;
public function __construct(Repository $config)
{
$this->proxies = config('app.trusted_proxies');
}
/**
* The headers that should be used to detect proxies.

View File

@ -194,6 +194,11 @@ class LinkRepository
{
$newEntries = collect();
$privateSetting = match ($model) {
Tag::class => usersettings('tags_private_default') === '1',
LinkList::class => usersettings('lists_private_default') === '1',
};
foreach ($entries as $entry) {
if ((int)$entry > 0) {
$newEntry = $model::find($entry);
@ -201,6 +206,10 @@ class LinkRepository
$newEntry = $model::firstOrCreate([
'user_id' => auth()->id(),
'name' => trim($entry),
], [
'user_id' => auth()->id(),
'name' => trim($entry),
'is_private' => $privateSetting,
]);
}

View File

@ -97,6 +97,7 @@ return [
'it_IT' => 'Italiano',
'no_NO' => 'Norsk',
'pl_PL' => 'Polski',
'ro_RO' => 'Română',
'vi_VN' => 'Tiếng Việt',
'zh_CN' => '简体中文',
],
@ -140,6 +141,19 @@ return [
'guest_access' => (bool)env('GUEST_ACCESS', false),
/*
|--------------------------------------------------------------------------
| Trusted Proxies
|--------------------------------------------------------------------------
|
| Specify which proxies should be trusted by default. As it's unknown which
| proxy ist used in front of LinkAce, all are allowed by default.
| Also see App\Http\Middleware\TrustProxies
|
*/
'trusted_proxies' => env('TRUSTED_PROXIES', '*'),
/*
|--------------------------------------------------------------------------
| Encryption Key

View File

@ -15,7 +15,7 @@ return [
|
*/
'driver' => 'bcrypt',
'driver' => env('HASHING_DRIVER', 'bcrypt'),
/*
|--------------------------------------------------------------------------
@ -29,7 +29,7 @@ return [
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
'rounds' => env('BCRYPT_ROUNDS', 12),
],
/*

View File

@ -67,6 +67,12 @@ return [
'days' => 14,
],
'import' => [
'driver' => 'single',
'path' => storage_path('logs/import.log'),
'level' => 'debug',
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),

View File

@ -15,7 +15,7 @@ return [
'password' => 'Les contrasenyes han de tenir com a mínim sis caràcters i han de coincidir amb la confirmació.',
'reset' => 'La contrasenya s\'ha restablert!',
'sent' => 'T\'hem enviat un correu electrònic amb un enllaç per a reiniciar la teva contrasenya!',
'sent' => 'Si existeix un compte amb la vostra adreça de correu electrònic, hem enviat per correu electrònic l\'enllaç de restabliment de la contrasenya.',
'token' => 'Aquest codi de recuperació de contrasenya és invàlid.',
'user' => "No hi ha cap usuari amb aquest correu.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'sense Llistes',
'order_by' => 'Ordenar per',
'order_by.title:asc' => 'Títol ascendent',
'order_by.title:desc' => 'Títol descendent',
'order_by.url:asc' => 'URL ascendent',
'order_by.url:desc' => 'URL descendent',
'order_by.created_at:asc' => 'Data de creació ascendent',
'order_by.created_at:desc' => 'Data de creació descendent',
'order_by.number_links:asc' => 'Nombre d\'enllaços ascendent',
'order_by.number_links:desc' => 'Nombre d\'enllaços descendent',
'order_by.title:asc' => 'Títol A-Z',
'order_by.title:desc' => 'Títol Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Més antigues',
'order_by.created_at:desc' => 'Més recents',
'order_by.number_links:asc' => 'Menys enllaços',
'order_by.number_links:desc' => 'La majoria d\'enllaços',
'order_by.random' => 'Aleatori',
'no_results' => 'No s\'han trobat resultats.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Passwörter müssen mindestens sechs Zeichen lang sein und übereinstimmen.',
'reset' => 'Ihr Passwort wurde zurückgesetzt!',
'sent' => 'Wir haben Ihren Link zum Zurücksetzen des Passworts per E-Mail gesendet!',
'sent' => 'Falls ein Konto mit der angegebenen E-Mail-Adresse existiert, wurde ein Link zum Zurücksetzen des Passworts per E-Mail verschickt.',
'token' => 'Das Passwort Reset Token ist ungültig.',
'user' => "Wir konnten keinen Benutzer mit dieser E-Mail-Adresse finden.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'ohne Listen',
'order_by' => 'Sortieren nach',
'order_by.title:asc' => 'Titel aufsteigend',
'order_by.title:desc' => 'Titel absteigend',
'order_by.url:asc' => 'URL aufsteigend',
'order_by.url:desc' => 'URL absteigend',
'order_by.created_at:asc' => 'Erstellungsdatum aufsteigend',
'order_by.created_at:desc' => 'Erstellungsdatum absteigend',
'order_by.number_links:asc' => 'Anzahl der Links aufsteigend',
'order_by.number_links:desc' => 'Anzahl der Links absteigend',
'order_by.title:asc' => 'Titel A-Z',
'order_by.title:desc' => 'Title Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Älteste',
'order_by.created_at:desc' => 'Neueste',
'order_by.number_links:asc' => 'Wenigste Links',
'order_by.number_links:desc' => 'Meiste Links',
'order_by.random' => 'Zufällig',
'no_results' => 'Keine Ergebnisse gefunden.',

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'without Lists',
'order_by' => 'Order by',
'order_by.title:asc' => 'Title ascending',
'order_by.title:desc' => 'Title descending',
'order_by.url:asc' => 'URL ascending',
'order_by.url:desc' => 'URL descending',
'order_by.created_at:asc' => 'Creation Date ascending',
'order_by.created_at:desc' => 'Creation Date descending',
'order_by.number_links:asc' => 'Number of Links ascending',
'order_by.number_links:desc' => 'Number of Links descending',
'order_by.title:asc' => 'Title A-Z',
'order_by.title:desc' => 'Title Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Oldest',
'order_by.created_at:desc' => 'Newest',
'order_by.number_links:asc' => 'Fewest Links',
'order_by.number_links:desc' => 'Most Links',
'order_by.random' => 'Random',
'no_results' => 'No results found.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Las contraseñas deben tener al menos seis caracteres y coincidir con la confirmación.',
'reset' => '¡Tu contraseña ha sido restablecida!',
'sent' => '¡Hemos enviado un correo electrónico a tu email para restablecer la contraseña!',
'sent' => 'Si existe una cuenta con su dirección de correo electrónico, hemos enviado un correo electrónico a su enlace de restablecimiento de contraseña.',
'token' => 'Este token de restablecimiento de contraseña no es válido.',
'user' => "No podemos encontrar un usuario con esa dirección de correo electrónico.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'sin listas',
'order_by' => 'Ordenar por',
'order_by.title:asc' => 'Título ascendente',
'order_by.title:desc' => 'Título descendente',
'order_by.url:asc' => 'URL ascendente',
'order_by.url:desc' => 'URL descendente',
'order_by.created_at:asc' => 'Fecha de creación ascendente',
'order_by.created_at:desc' => 'Fecha de creación descendente',
'order_by.number_links:asc' => 'Número de enlaces ascendentes',
'order_by.number_links:desc' => 'Número de enlaces descendente',
'order_by.title:asc' => 'Título A-Z',
'order_by.title:desc' => 'Título Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Más antiguos',
'order_by.created_at:desc' => 'Más recientes',
'order_by.number_links:asc' => 'Menos enlaces',
'order_by.number_links:desc' => 'Más enlaces',
'order_by.random' => 'Aleatorio',
'no_results' => 'No se han encontrado resultados.',

View File

@ -30,7 +30,7 @@ return [
'delete' => 'Supprimer',
'menu' => 'Menu',
'entries' => 'Éntrées',
'entries' => 'Entrées',
'feed' => 'Feed',
'continue_adding' => 'Continuer l\'Ajout',

View File

@ -3,7 +3,7 @@ return [
'list' => 'Liste',
'lists' => 'Listes',
'all_lists' => 'Toutes les Listes',
'recent_lists' => 'Liens Récents',
'recent_lists' => 'Listes Récentes',
'add' => 'Ajouter Liste',
'show' => 'Afficher Liste',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Les mots de passe doivent comporter au moins six caractères et correspondre à la confirmation.',
'reset' => 'Votre mot de passe a été réinitialisé !',
'sent' => 'Nous avons envoyé votre lien de réinitialisation de mot de passe !',
'sent' => 'Si un compte avec cette adresse e-mail existe, nous avons envoyé votre lien de réinitialisation de mot de passe.',
'token' => 'Ce jeton de réinitialisation de mot de passe est invalide.',
'user' => "Nous ne trouvons pas d'utilisateur avec cette adresse e-mail.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'sans listes',
'order_by' => 'Trier par',
'order_by.title:asc' => 'Titre ascendant',
'order_by.title:desc' => 'Titre descendant',
'order_by.url:asc' => 'URL ascendante',
'order_by.url:desc' => 'URL descendante',
'order_by.created_at:asc' => 'Date de Création ascendante',
'order_by.created_at:desc' => 'Date de Création descendante',
'order_by.number_links:asc' => 'Nombre de Liens ascendants',
'order_by.number_links:desc' => 'Nombre de Liens descendants',
'order_by.title:asc' => 'Titre A-Z',
'order_by.title:desc' => 'Title Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Plus ancien',
'order_by.created_at:desc' => 'Plus récent',
'order_by.number_links:asc' => 'Moins de liens',
'order_by.number_links:desc' => 'Plus de liens',
'order_by.random' => 'Aléatoire',
'no_results' => 'Aucun résultat trouvé.',

View File

@ -45,7 +45,7 @@ return [
'sharing_toggle' => 'Activer/désactiver tout',
'darkmode' => 'Mode sombre',
'darkmode_help' => 'Vous pouvez choisir d\'activer définitivement ou automatiquement en fonction des paramètres de votre appareil. (<small>Cocher <a href="https://caniuse.com/#search=prefers-color-scheme">ici</a> si votre navigateur prend en charge la détection automatique</small>)',
'darkmode_help' => 'Vous pouvez choisir d\'activer définitivement ou automatiquement en fonction des paramètres de votre appareil. (<small>Vérifiez <a href="https://caniuse.com/#search=prefers-color-scheme">ici</a> si votre navigateur prend en charge la détection automatique</small>)',
'darkmode_disabled' => 'Désactivé',
'darkmode_auto' => 'Automatiquement',
'darkmode_permanent' => 'Permanent',

View File

@ -15,7 +15,7 @@ return [
'delete_warning' => 'Les entrées définitivement supprimées ne peuvent pas être restaurées !',
'delete_no_entries' => 'Aucune entrée à supprimer.',
'delete_success.links' => 'Supprimer définitivement tous les liens.',
'delete_success.links' => 'Tous les liens ont été définitivement supprimés.',
'delete_success.lists' => 'Supprimer définitivement toutes les listes.',
'delete_success.tags' => 'Supprimer définitivement toutes les étiquettes.',
'delete_success.notes' => 'Supprimer définitivement toutes les notes.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'A jelszónak legalább hat karakterből kell állnia, és meg kell egyeznie a megerősítéssel.',
'reset' => 'A jelszava visszaállításra került!',
'sent' => 'Elküldtük e-mailben a jelszó-visszaállítási hivatkozást!',
'sent' => 'Ha létezik fiók az Ön e-mail címével, e-mailben elküldtük a jelszó-visszaállítási linket.',
'token' => 'Ez a jelszó-visszaállítási jogkivonat érvénytelen.',
'user' => "Nem találtunk felhasználót ezzel az e-mail-címmel.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'lista nélkül',
'order_by' => 'Rendezés:',
'order_by.title:asc' => 'Növekvő sorrendben a cím szerint',
'order_by.title:desc' => 'Csökkenő sorrendben a cím szerint',
'order_by.url:asc' => 'Növekvő sorrendben az URL szerint',
'order_by.url:desc' => 'Csökkenő sorrendben az URL szerint',
'order_by.created_at:asc' => 'Növekvő sorrendben a létrehozás dátuma szerint',
'order_by.created_at:desc' => 'Csökkenő sorrendben a létrehozás dátuma szerint',
'order_by.number_links:asc' => 'Növekvő sorrendben a hivatkozások száma szerint',
'order_by.number_links:desc' => 'Csökkenő sorrendben a hivatkozások száma szerint',
'order_by.title:asc' => 'Cím A-Z',
'order_by.title:desc' => 'Cím Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Legrégebbi',
'order_by.created_at:desc' => 'Legújabb',
'order_by.number_links:asc' => 'A legkevesebb link',
'order_by.number_links:desc' => 'A legtöbb link',
'order_by.random' => 'Véletlen',
'no_results' => 'Nincs találat.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Le password devono essere di almeno 6 caratteri e devono coincidere.',
'reset' => 'La password è stata reimpostata!',
'sent' => 'Abbiamo inviato una e-mail al tuo link per reimpostare la password!',
'sent' => 'Se esiste un account con il tuo indirizzo e-mail, verrà inviata una e-mail con un link per reimpostare la password.',
'token' => 'Questo token per la reimpostazione della password non è valido.',
'user' => "Non esiste un utente associato a questo indirizzo e-mail.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'senza Liste',
'order_by' => 'Ordina per',
'order_by.title:asc' => 'Titolo ascendente',
'order_by.title:desc' => 'Titolo discendente',
'order_by.url:asc' => 'URL ascendente',
'order_by.url:desc' => 'URL discendente',
'order_by.created_at:asc' => 'Data creazione ascendente',
'order_by.created_at:desc' => 'Data creazione discendente',
'order_by.number_links:asc' => 'Numero di link ascendente',
'order_by.number_links:desc' => 'Numero di link discendente',
'order_by.title:asc' => 'Titolo A-Z',
'order_by.title:desc' => 'Titolo Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Meno Recenti',
'order_by.created_at:desc' => 'Più Recenti',
'order_by.number_links:asc' => 'Minor numero di Link',
'order_by.number_links:desc' => 'Maggior numero di Link',
'order_by.random' => 'Casuale',
'no_results' => 'Nessun risultato trovato.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Passord må være minst seks tegn og stemme med bekreftelsen.',
'reset' => 'Ditt passord har blitt tilbakestilt!',
'sent' => 'Vi har sendt deg en lenke for tilbakestilling av passordet ditt!',
'sent' => 'Hvis en konto med din e-postadresse finnes, har vi sendt en lenke for tilbakestilling av passordet ditt.',
'token' => 'Denne resettingen av passordet er ugyldig.',
'user' => "Vi finner ikke en bruker med denne e-postadressen.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'uten lister',
'order_by' => 'Sorter etter',
'order_by.title:asc' => 'Tittel stigende',
'order_by.title:desc' => 'Tittel synkende',
'order_by.url:asc' => 'Nettadresse stigende',
'order_by.url:desc' => 'Nettadresse synkende',
'order_by.created_at:asc' => 'Opprettet dato stigende',
'order_by.created_at:desc' => 'Opprettet dato synkende',
'order_by.number_links:asc' => 'Antall lenker stigende',
'order_by.number_links:desc' => 'Antall lenker synkende',
'order_by.title:asc' => 'Tittel A-Å',
'order_by.title:desc' => 'Tittel Å-A',
'order_by.url:asc' => 'Nettadresse A-Z',
'order_by.url:desc' => 'Nettadresse Z-A',
'order_by.created_at:asc' => 'Eldste',
'order_by.created_at:desc' => 'Nyeste',
'order_by.number_links:asc' => 'Færrest lenker',
'order_by.number_links:desc' => 'Fleste lenker',
'order_by.random' => 'Tilfeldig',
'no_results' => 'Ingen resultater.',

View File

@ -15,7 +15,7 @@ return [
'password' => 'Hasła muszą mieć co najmniej sześć znaków i muszą być zgodne z potwierdzeniem.',
'reset' => 'Twoje hasło zostało zresetowane!',
'sent' => 'Wysłaliśmy Twój link do resetowania hasła!',
'sent' => 'Jeśli konto z Twoim adresem e-mail istnieje, wysłaliśmy wiadomość e-mail z linkiem do resetowania hasła.',
'token' => 'Ten token resetowania hasła jest nieprawidłowy.',
'user' => "Nie możemy znaleźć użytkownika z tym adresem email.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'bez list',
'order_by' => 'Pokaż według',
'order_by.title:asc' => 'Tytuł rosnąco',
'order_by.title:desc' => 'Tytuł malejąco',
'order_by.url:asc' => 'Adres URL rosnąco',
'order_by.url:desc' => 'Adres URL malejąco',
'order_by.created_at:asc' => 'Data utworzenia rosnąco',
'order_by.created_at:desc' => 'Data utworzenia malejąco',
'order_by.number_links:asc' => 'Liczba linków rosnąco',
'order_by.number_links:desc' => 'Liczba linków malejąco',
'order_by.title:asc' => 'Tytuł A-Z',
'order_by.title:desc' => 'Tytuł Z-A',
'order_by.url:asc' => 'Adres URL A-Z',
'order_by.url:desc' => 'Adres URL Z-A',
'order_by.created_at:asc' => 'Najstarsze',
'order_by.created_at:desc' => 'Najnowsze',
'order_by.number_links:asc' => 'Najmniej linków',
'order_by.number_links:desc' => 'Najwięcej linków',
'order_by.random' => 'Losowo',
'no_results' => 'Nie znaleziono wyników.',

27
lang/ro_RO/auth.php Normal file
View File

@ -0,0 +1,27 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Aceste date de autentificare nu se potrivesc cu înregistrările noastre.',
'throttle' => 'Prea multe tentative de autentificare. Reîncearcă din nou în :seconds secunde.',
'confirm_title' => 'Confirmarea este necesară',
'confirm' => 'Confirmă această acțiune prin folosirea parolei actuale.',
'confirm_action' => 'Confirmă acțiunea',
'two_factor' => 'Autentificare în doi factori',
'two_factor_check' => 'Introdu acum parola de unică folosință furnizată de aplicația de autentificare în doi factori.',
'two_factor_with_recovery' => 'Autentifică-te cu codul de recuperare',
];

10
lang/ro_RO/export.php Normal file
View File

@ -0,0 +1,10 @@
<?php
return [
'export' => 'Exportare',
'start_export_html' => 'Exportare în HTML',
'start_export_csv' => 'Exportare în CSV',
'export_help' => 'Executarea funcției de exportare va salva toate marcajele existente într-un fișier obișnuit compatibil cu marcajele sau într-un fișier CSV, dacă se dorește acest lucru.',
'export_csv_error' => 'A apărut o eroare la generarea unui fișier CSV. Examinează fișierele de jurnal pentru mai multe detalii.',
];

14
lang/ro_RO/import.php Normal file
View File

@ -0,0 +1,14 @@
<?php
return [
'import' => 'Importare',
'start_import' => 'Pornire importare',
'import_running' => 'Se importă...',
'import_file' => 'Fișier pentru importat',
'import_help' => 'Aici îți poți importa marcajele de navigator existente. De regulă, marcajele sunt exportate într-un fișier .html de navigatorul tău. Selectează fișierul aici și pornește importarea.<br>Acest proces poate dura mai mult timp în funcție de numărul de marcaje.',
'import_networkerror' => 'A apărut o eroare la importarea marcajelor. Examinează consola navigatorului cu privire la detalii sau consultă jurnalele aplicației.',
'import_error' => 'A apărut o eroare la importarea marcajelor. Consultă jurnalele aplicației.',
'import_empty' => 'Niciun marcaj nu a putut fi importat. Fișierul încărcat este deteriorat sau gol.',
'import_successfully' => ':imported legături importate, :skipped omise.',
];

69
lang/ro_RO/link.php Normal file
View File

@ -0,0 +1,69 @@
<?php
return [
'link' => 'Legătură',
'links' => 'Legături',
'all_links' => 'Toate legăturile',
'recent_links' => 'Legături recente',
'no_links' => 'Nicio legătură',
'add' => 'Adăugare legătură',
'add_quick' => 'Adăugare rapid legătură',
'show' => 'Afișare legătură',
'details' => 'Detalii legătură',
'edit' => 'Editare legătură',
'update' => 'Actualizare legătură',
'delete' => 'Ștergere legătură',
'private' => 'Legătură privată',
'history' => 'Istoric',
'history_added' => 'S-a adăugat <code>:newvalue</code> la :fieldname.',
'history_changed' => 'S-a modificat :fieldname de la <code>:oldvalue</code> la <code>:newvalue</code>',
'history_removed' => 'S-a eliminat <code>:oldvalue</code> de la :fieldname.',
'history_deleted' => 'Legătura a fost ștearsă',
'history_restored' => 'Legătura a fost restaurată',
'history_created' => 'Legătura a fost creată',
'url' => 'URL',
'title' => 'Titlu',
'description' => 'Descriere',
'revtags' => 'Etichete',
'revlists' => 'Liste',
'is_private' => 'Stare privată',
'status' => 'Stare',
'stati' => [
'1' => 'Funcțională',
'2' => 'Mutată',
'3' => 'Nefuncțională',
],
'author' => 'de :user',
'external_link' => 'Legătură externă',
'wayback' => 'Arhiva legăturilor la Wayback Machine',
'check_disable' => 'Dezactivare verificare',
'check_disabled' => 'Verificarea este dezactivată',
'check_enable' => 'Activare verificare',
'check_enabled' => 'Verificarea este activată',
'status_is_broken' => 'Legătura este marcată ca fiind nefuncțională',
'status_mark_working' => 'Marchează ca în lucru',
'added_successfully' => 'Legătura a fost adăugată.',
'added_connection_error' => 'Legătura a fost adăugată, dar a apărut o eroare de conexiune la accesarea URL-ului. Detaliile se regăsesc în jurnale.',
'added_request_error' => 'Legătura a fost adăugată, dar a apărut o eroare de conexiune la solicitarea URL-ului, de exemplu, un certificat invalid. Detaliile se regăsesc în jurnale.',
'updated_successfully' => 'Legătura a fost actualizată.',
'deleted_successfully' => 'Legătura a fost ștearsă.',
'deletion_error' => 'Legătura nu a putut fi ștearsă.',
'duplicates_found' => 'LinkAce a găsit dubluri posibile ale URL-ului trimis:',
'existing_found' => 'Există deja o legătură cu acel URL.',
'notifications.linkcheck.errors' => 'LinkAce a descoperit erori la verificarea linkurilor tale.',
'notifications.linkcheck.errors.moved' => '⚠ Următoarele legături au fost mutate într-o nouă locație:',
'notifications.linkcheck.errors.broken' => '🚫 Următoarele legături nu mai sunt accesibile sau returnează o eroare:',
'happy_bookmarking' => 'La cât mai multe marcaje',
];

54
lang/ro_RO/linkace.php Normal file
View File

@ -0,0 +1,54 @@
<?php
return [
'linkace' => 'LinkAce',
'user' => 'Utilizator',
'username' => 'Nume de utilizator',
'email' => 'Poștă electronică',
'password' => 'Parolă',
'password_confirm' => 'Confirmare parolă',
'login' => 'Autentificare',
'logout' => 'Deconectare',
'remember_me' => 'Ține-mă minte',
'go_to_dashboard' => 'Deplasare la tabloul de bord',
'system_logs' => 'Jurnale de sistem',
'reset_password' => 'Resetare parolă',
'send_reset_email' => 'Trimite legătura de resetare a parolei',
'forgot_password_link' => 'Ți-ai uitat parola? <a href=":reset_url">Reseteaz-o aici</a>',
'added' => 'Adăugat',
'added_at' => 'Adăugat la',
'updated_at' => 'Actualizat la',
'last_update' => 'Ultima actualizare',
'deleted_at' => 'Şters la',
'add' => 'Adăugare',
'show' => 'Afișare',
'edit' => 'Editare',
'update' => 'Actualizare',
'delete' => 'Ștergere',
'menu' => 'Meniu',
'entries' => 'Înregistrări',
'feed' => 'Flux',
'continue_adding' => 'Continuă să adaugi',
'private' => 'Privat',
'is_private' => 'Este privat',
'yes' => 'Da',
'no' => 'Nu',
'more' => 'Mai mult',
'no_results_found' => 'Nicio :model găsită.',
'bookmarklet_close' => 'Această ferestră de marcaj-script se închide automat în <span class="bm-timer">5</span> secunde.',
'open_linkace' => 'Deschidere LinkAce',
'demo_login_hint' => 'Te poți autentifica imediat. Reține că această demonstrație va fi resetată la fiecare 2 ore.',
'project_of' => 'LinkAce este un proiect realizat de',
'version' => 'Versiunea actuală: :version',
];

31
lang/ro_RO/list.php Normal file
View File

@ -0,0 +1,31 @@
<?php
return [
'list' => 'Listă',
'lists' => 'Liste',
'all_lists' => 'Toate listele',
'recent_lists' => 'Liste recente',
'add' => 'Adăugare listă',
'show' => 'Afișare listă',
'edit' => 'Editare listă',
'update' => 'Actualizare listă',
'delete' => 'Ștergere listă',
'filter_lists' => 'Filtrare liste...',
'private' => 'Listă privată',
'name' => 'Denumirea listei',
'description' => 'Descrierea listei',
'author' => 'de :user',
'no_lists' => 'Nicio listă',
'number_links' => ':number legătură în această listă|:number legături în această listă',
'added_successfully' => 'Lista a fost adăugată.',
'updated_successfully' => 'Lista a fost actualizată.',
'deleted_successfully' => 'Lista a fost ștearsă.',
'deletion_error' => 'Lista nu a putut fi ștearsă.',
];

20
lang/ro_RO/note.php Normal file
View File

@ -0,0 +1,20 @@
<?php
return [
'note' => 'Observație',
'notes' => 'Observații',
'add' => 'Adăugare observație',
'show' => 'Afișare observație',
'edit' => 'Editare observație',
'update' => 'Actualizare observație',
'delete' => 'Ștergere observație',
'private' => 'Observație privată',
'note_content' => 'Conținutul observației',
'added_successfully' => 'Observația a fost adăugată.',
'updated_successfully' => 'Observația a fost actualizată.',
'deleted_successfully' => 'Observația a fost ștearsă.',
'deletion_error' => 'Observația nu a putut fi ștearsă.',
];

19
lang/ro_RO/pagination.php Normal file
View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Anterior',
'next' => 'Următoarea &raquo;',
];

22
lang/ro_RO/passwords.php Normal file
View File

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Parolele trebuie să aibă cel puțin șase caractere și să coincidă.',
'reset' => 'Parola a fost resetată!',
'sent' => 'Dacă există un cont cu adresa ta de poștă electronică, ți-am trimis un mesaj cu legătura de resetare a parolei.',
'token' => 'Acest token de resetare a parolei este invalid.',
'user' => "Nu putem găsi un utilizator cu această adresă de poștă electronică.",
];

View File

@ -0,0 +1,20 @@
<?php
return [
'username' => 'ionpopescu',
'email' => 'ion.popescu@exemplu.ro',
'password' => 'Parolă',
'password_confirmed' => 'Parolă confirmată',
'link_url' => 'https://github.com/Kovah/LinkAce',
'link_title' => 'Titlul paginii de internet',
'list_select' => 'Selectează o listă',
'tags_select' => 'Selectează câteva etichete',
'list_name' => 'Denumirea actuală a listei',
'tag_name' => 'Denumirea actuală a etichetei',
'two_factor_otp' => 'Parolă de unică folosință',
'two_factor_recovery_code' => 'Cod de recuperare',
];

30
lang/ro_RO/search.php Normal file
View File

@ -0,0 +1,30 @@
<?php
return [
'search' => 'Căutare',
'results' => 'rezultate',
'filter_by_list' => 'Filtrare după listă(e)',
'filter_by_tag' => 'Filtrare după etichetă(e)',
'query' => 'Căutare după...',
'search_title' => 'Căutare titlu',
'search_description' => 'Căutare descriere',
'private_only' => 'Doar legături private',
'broken_links' => 'Doar legături nefuncționale',
'empty_tags' => 'fără etichete',
'empty_lists' => 'fără liste',
'order_by' => 'Ordonare după',
'order_by.title:asc' => 'Titlul A-Z',
'order_by.title:desc' => 'Titlul Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Cele mai vechi',
'order_by.created_at:desc' => 'Cele mai noi',
'order_by.number_links:asc' => 'Link-uri cel mai puțin',
'order_by.number_links:desc' => 'Cele mai multe link-uri',
'order_by.random' => 'Aleatoriu',
'no_results' => 'Niciun rezultat găsit.',
'validation_query_missing' => 'Trebuie fie să introduci o interogare de căutare, fie să selectezi o listă, o etichetă sau să activezi căutarea pentru legături nefuncționale.',
];

106
lang/ro_RO/settings.php Normal file
View File

@ -0,0 +1,106 @@
<?php
return [
'settings' => 'Setări',
'user_settings' => 'Setări ale utilizatorului',
'account_settings' => 'Setări ale contului',
'app_settings' => 'Setări ale aplicației',
'system_settings' => 'Setări ale sistemului',
'guest_settings' => 'Setări pentru invitați',
'language' => 'Limbă',
'timezone' => 'Fus orar',
'date_format' => 'Format de dată',
'time_format' => 'Format orar',
'listitem_count' => 'Numărul de elemente din liste',
'links_new_tab' => 'Deschidere legături externe în file noi',
'markdown_for_text' => 'Activează Markdown pentru descrieri și note',
'privacy' => 'Confidențialitate',
'links_private_default' => 'Legături private în mod implicit',
'links_private_default_help' => 'Activarea acestei opțiuni va face ca toate legăturile noi să devină private în mod implicit',
'notes_private_default' => 'Observații private în mod implicit',
'notes_private_default_help' => 'Activarea acestei opțiuni va face ca toate observațiile noi să devină private în mod implicit',
'tags_private_default' => 'Etichete private în mod implicit',
'tags_private_default_help' => 'Activarea acestei opțiuni va face ca toate etichetele noi să devină private în mod implicit',
'lists_private_default' => 'Liste private în mod implicit',
'lists_private_default_help' => 'Activarea acestei opțiuni va face ca toate listele noi să devină private în mod implicit',
'archive_backups' => 'Copii de rezervă Wayback Machine',
'archive_backups_help' => 'Dacă opțiunea este activată, LinkAce va transmite serviciului <a href="https://archive.org/">Wayback Machine</a> să creeze copii de rezervă pentru legăturile tale. Serviciul Wayback Machine este administrat de Internet Archive, o organizație non-profit. Te îndemnăm să <a href="https://archive.org/donate/">donezi către Internet Archive</a>.',
'archive_backups_enabled' => 'Activare copii de rezervă',
'archive_backups_enabled_help' => 'Dacă opțiunea este activată, legăturile neprivate vor fi salvate de Internet Archive.',
'archive_private_backups_enabled' => 'Activare copii de rezervă pentru legături private',
'archive_private_backups_enabled_help' => 'Dacă opțiunea este activată, legăturile private vor fi, de asemenea, salvate. Copiile de rezervă trebuie să fie activate.',
'display_mode' => 'Afișare legături ca',
'display_mode_list_detailed' => 'listă cu multe detalii',
'display_mode_list_simple' => 'listă cu mai puține detalii',
'display_mode_cards' => 'cartonașe cu mai puține detalii',
'display_mode_cards_detailed' => 'cartonașe cu multe detalii',
'sharing' => 'Distribuire legătură',
'sharing_help' => 'Activează toate serviciile care dorești să fie afișate pentru legături, pentru a le putea distribui cu ușurință printr-un singur clic.',
'sharing_toggle' => 'Comutare toate pornit/oprit',
'darkmode' => 'Mod întunecat',
'darkmode_help' => 'Poți alege să se pornească permanent sau automat în funcție de setările dispozitivului tău. (<small>Vezi <a href="https://caniuse.com/#search=prefers-color-scheme">aici</a> dacă navigatorul tău este compatibil cu detectarea automată</small>)',
'darkmode_disabled' => 'Dezactivat',
'darkmode_auto' => 'Automat',
'darkmode_permanent' => 'Permanent',
'save_settings' => 'Salvare setări',
'settings_saved' => 'Setările au fost actualizate!',
'bookmarklet' => 'Marcaj-script',
'bookmarklet_button' => 'Trage acest script în marcajele tale sau dă clic dreapta și salvează-l ca marcaj',
'bookmarklet_help' => 'Adaugă acest marcaj-script în navigatorul tău pentru a adăuga rapid legături de pe paginile pe care le vizitezi, fără a fi nevoie să deschizi manual aplicația LinkAce.',
'change_password' => 'Modificare parolă',
'old_password' => 'Parolă veche',
'new_password' => 'Parolă nouă',
'new_password2' => 'Repetă noua parolă',
'password_updated' => 'Parola a fost modificată!',
'old_password_invalid' => 'Parola veche nu este validă!',
'two_factor_auth' => 'Autentificare în doi factori',
'two_factor_enable' => 'Activează autentificarea în doi factori',
'two_factor_disable' => 'Dezactivează autentificarea în doi factori',
'two_factor_setup_app' => 'Autentificarea în doi pași este activată. Configurează-ți acum dispozitivul de autentificare prin scanarea următorului cod QR.',
'two_factor_setup_url' => 'Codul QR nu funcționează? Mai ai posibilitatea de a utiliza direct acest URL.',
'two_factor_recovery_codes' => 'Stochează aceste coduri de recuperare într-un manager de parole securizat. Acestea pot fi utilizate pentru a-ți recupera accesul la cont în cazul în care ți-ai pierdut dispozitivul de autentificare în doi factori.',
'two_factor_recovery_codes_view' => 'Vizualizare coduri de recuperare',
'two_factor_regenerate_recovery_codes' => 'Generare coduri de recuperare noi',
'api_token' => 'Token API',
'api_token_generate' => 'Generare token',
'api_token_generate_confirm' => 'Sigur dorești să generezi un token nou?',
'api_token_help' => 'Tokenul API poate fi utilizat pentru a accesa LinkAce din alte aplicații sau scripturi.',
'api_token_generate_info' => 'Atenție: dacă ai deja un token API, generarea unuia nou va întrerupe toate integrările existente!',
'api_token_generate_failure' => 'Un nou token API nu a putut fi generat. Examinează consola navigatorului și jurnalele aplicației cu privire la mai multe informații.',
'sys_page_title' => 'Titlul paginii',
'sys_guest_access' => 'Activează accesul pentru invitați',
'sys_guest_access_help' => 'Dacă opțiunea este activată, invitații vor putea să vadă toate legăturile care nu sunt private.',
'sys_custom_header_content' => 'Conținut personalizat de antet',
'sys_custom_header_content_help' => 'Conținutul introdus aici va fi plasat înaintea etichetei &lt;/head&gt; pe toate paginile LinkAce. Această opțiune este utilă pentru a plasa scripturi de analiză sau personalizare. Atenție: conținutul nu este marcat cu secvențe escape și poate întrerupe paginile!',
'cron_token' => 'Token Cron',
'cron_token_generate' => 'Generare token',
'cron_token_generate_confirm' => 'Sigur dorești să generezi un token nou?',
'cron_token_help' => 'Tokenul cron este necesar pentru a executa serviciul cron care verifică dacă există legături nefuncționale sau copii de rezervă în desfășurare.',
'cron_token_url' => 'Direcționează-ți serviciul cron către următorul URL: <span class="cron-token-url">:route</span>',
'cron_token_generate_info' => 'Atenție: dacă ai deja un token cron, generarea unuia nou va întrerupe serviciul cron existent!',
'cron_token_generate_failure' => 'Un nou token cron nu a putut fi generat. Examinează consola navigatorului și jurnalele aplicației cu privire la mai multe informații.',
'cron_token_auth_failure' => 'Tokenul cron furnizat este invalid',
'cron_execute_successful' => 'Serviciul cron a fost executat',
'update_check' => 'Căutare actualizare',
'update_check_running' => 'Se caută actualizări...',
'update_check_version_found' => 'Actualizare găsită. Este disponibilă versiunea #VERSION#.',
'update_check_success' => 'Nu s-a găsit nicio actualizare.',
'update_check_failed' => 'Nu s-au putut căuta actualizări.',
'guest_settings_info' => 'Următoarele setări se vor aplica invitaților care îți vizitează pagina, dacă accesul pentru invitați este activat.',
];

52
lang/ro_RO/setup.php Normal file
View File

@ -0,0 +1,52 @@
<?php
return [
'setup' => 'Configurare',
'continue' => 'Continuare',
'try_again' => 'Încearcă din nou',
'welcome' => 'Bun venit la configurarea LinkAce',
'intro' => 'În următorii pași vei configura LinkAce pentru a fi gata de utilizare.',
'intro.step1' => 'Verifică dacă sunt îndeplinite toate cerințele.',
'intro.step2' => 'Configurează o bază de date și verifică dacă se realizează conexiunea la aceasta.',
'intro.step3' => 'Creează-ți contul de utilizator.',
'check_requirements' => 'Verificare cerințe',
'requirements.php_version' => 'Versiune PHP >= 7.4.0',
'requirements.extension_bcmath' => 'Extensie PHP: BCMath',
'requirements.extension_ctype' => 'Extensie PHP: Ctype',
'requirements.extension_json' => 'Extensie PHP: JSON',
'requirements.extension_mbstring' => 'Extensie PHP: Mbstring',
'requirements.extension_openssl' => 'Extensie PHP: OpenSSL',
'requirements.extension_pdo_mysql' => 'Extensie PHP: PDO',
'requirements.extension_tokenizer' => 'Extensie PHP: Tokenizer',
'requirements.extension_xml' => 'Extensie PHP: XML',
'requirements.env_writable' => 'Fișierul .env există și este inscripționabil',
'requirements.storage_writable' => 'Directoarele /storage și /storage/logs sunt inscripționabile',
'database_configuration' => 'Configurarea bazei de date',
'database_configure' => 'Configurare bază de date',
'database.intro' => 'Dacă ai completat deja detaliile bazei de date în fișierul .env, atunci câmpurile de introducere ar trebui să fie completate în prealabil. În caz contrar, completează câmpurile cu informațiile corespunzătoare pentru baza ta de date.',
'database.config_error' => 'Baza de date nu a putut fi configurată. Verifică detaliile conexiunii tale. Detalii:',
'database.db_host' => 'Gazda bazei de date',
'database.db_port' => 'Portul bazei de date',
'database.db_name' => 'Denumirea bazei de date',
'database.db_user' => 'Utilizatorul bazei de date',
'database.db_password' => 'Parola bazei de date',
'database.complete_hint' => 'Salvarea configurației bazei de date și pregătirea acesteia în vederea utilizării aplicației poate dura câteva secunde. Te rugăm să ai răbdare.',
'database.data_present' => 'Atenție! Am descoperit date în baza de date specificată! Asigură-te că ai o copie de rezervă pentru această bază de date și confirmă ștergerea tuturor datelor.',
'database.overwrite_data' => 'Confirm că toate datele trebuie șterse și suprascrise cu o nouă bază de date LinkAce',
'account_setup' => 'Configurare cont',
'account_setup.intro' => 'Înainte de a putea începe, trebuie să îți creezi contul de utilizator.',
'account_setup.name' => 'Introdu-ți numele',
'account_setup.email' => 'Introdu-ți adresa de poștă electronică',
'account_setup.password' => 'Introdu o parolă puternică',
'account_setup.password_requirements' => 'Lungime minimă: 10 caractere',
'account_setup.password_confirmed' => 'Confirmă-ți parola',
'account_setup.create' => 'Creare cont',
'complete' => 'Configurare finalizată!',
'outro' => 'Ai finalizat configurarea și acum poți utiliza LinkAce! Te-ai autentificat și poți începe să aplici marcaje imediat.',
];

33
lang/ro_RO/sharing.php Normal file
View File

@ -0,0 +1,33 @@
<?php
return [
'sharetext' => 'Am găsit această legătură uluitoare. Aruncă o privire: #URL#',
'subject' => 'Aruncă o privire peste această legătură grozavă',
'share' => 'Distribuie legătura prin :service',
'share_link' => 'Distribuie această legătură',
'service' => [
'email' => 'Poștă electronică',
'print' => 'Imprimare',
'facebook' => 'Facebook',
'twitter' => 'Twitter',
'reddit' => 'Reddit',
'pinterest' => 'Pinterest',
'whatsapp' => 'WhatsApp',
'telegram' => 'Telegram',
'fb-messenger' => 'Facebook Messenger',
'wechat' => 'WeChat',
'sms' => 'SMS',
'slack' => 'Slack',
'skype' => 'Skype',
'hackernews' => 'Hacker News',
'discord' => 'Discord',
'mastodon' => 'Mastodon',
'pocket' => 'Pocket',
'flipboard' => 'Flipboard',
'evernote' => 'Evernote',
'trello' => 'Trello',
'buffer' => 'Buffer',
'tumblr' => 'Tumblr',
'xing' => 'Xing',
'linkedin' => 'LinkedIn',
],
];

12
lang/ro_RO/stats.php Normal file
View File

@ -0,0 +1,12 @@
<?php
return [
'stats' => 'Statistici',
'total_links' => 'Legături totale',
'total_lists' => 'Liste totale',
'total_tags' => 'Etichete totale',
'total_notes' => 'Observații totale',
'total_broken_links' => 'Legături nefuncționale',
];

28
lang/ro_RO/tag.php Normal file
View File

@ -0,0 +1,28 @@
<?php
return [
'tag' => 'Etichetă',
'tags' => 'Etichete',
'all_tags' => 'Toate etichetele',
'recent_tags' => 'Etichete recente',
'add' => 'Adăugare etichetă',
'show' => 'Afișare etichetă',
'edit' => 'Editare etichetă',
'update' => 'Actualizare etichetă',
'delete' => 'Ștergere etichetă',
'filter_tags' => 'Filtrare etichete...',
'private' => 'Etichetă privată',
'name' => 'Denumirea etichetei',
'author' => 'de :user',
'no_tags' => 'Nu există etichete',
'added_successfully' => 'Eticheta a fost adăugată.',
'updated_successfully' => 'Eticheta a fost actualizată.',
'deleted_successfully' => 'Eticheta a fost ștearsă.',
'deletion_error' => 'Eticheta nu a putut fi ștearsă.',
];

31
lang/ro_RO/trash.php Normal file
View File

@ -0,0 +1,31 @@
<?php
return [
'trash' => 'Coș de gunoi',
'deleted_links' => 'Legături șterse',
'deleted_lists' => 'Liste șterse',
'deleted_tags' => 'Etichete șterse',
'deleted_notes' => 'Observații șterse',
'restore' => 'Restaurare înregistrare',
'clear_trash' => 'Golire coș de gunoi',
'delete_warning' => 'Înregistrările șterse permanent nu pot fi restaurate!',
'delete_no_entries' => 'Nu există înregistrări de șters.',
'delete_success.links' => 'Toate legăturile au fost șterse permanent.',
'delete_success.lists' => 'Toate listele au fost șterse permanent.',
'delete_success.tags' => 'Toate etichetele au fost șterse permanent.',
'delete_success.notes' => 'Toate observațiile au fost șterse permanent.',
'restore.link' => 'Legătura a fost restaurată din coșul de gunoi.',
'restore.list' => 'Lista a fost restaurată din coșul de gunoi.',
'restore.tag' => 'Eticheta a fost restaurată din coșul de gunoi.',
'restore.note' => 'Observația a fost restaurată din coșul de gunoi.',
'restore.not_found' => 'Elementul de restaurat nu a putut fi găsit.',
'restore.not_allowed' => 'Nu ai permisiunea de a restaura acest element.',
];

10
lang/ro_RO/user.php Normal file
View File

@ -0,0 +1,10 @@
<?php
return [
'user' => 'Utilizator',
'users' => 'Utilizatori',
'username' => 'Nume de utilizator',
'email' => 'Poștă electronică',
'hello' => 'Salut, :user!',
];

146
lang/ro_RO/validation.php Normal file
View File

@ -0,0 +1,146 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'Câmpul :attribute trebuie să fie acceptat.',
'active_url' => 'Câmpul :attribute nu este un URL valid.',
'after' => 'Câmpul :attribute trebuie să fie o dată după :date.',
'after_or_equal' => 'Câmpul :attribute trebuie să fie o dată ulterioară sau egală cu :date.',
'alpha' => 'Câmpul :attribute poate conține doar litere.',
'alpha_dash' => 'Câmpul :attribute poate conține doar litere, numere și cratime.',
'alpha_num' => 'Câmpul :attribute poate conține doar litere și numere.',
'array' => 'Câmpul :attribute trebuie să fie un array.',
'before' => 'Câmpul :attribute trebuie să fie o dată înainte de :date.',
'before_or_equal' => 'Câmpul :attribute trebuie să fie o dată înainte sau egală cu :date.',
'between' => [
'numeric' => ':attribute trebuie să fie între :min și :max.',
'file' => ':attribute trebuie să aibă dimensiunea cuprinsă între :min și :max kilobaiți.',
'string' => ':attribute trebuie să aibă între :min și :max caractere.',
'array' => ':attribute trebuie să aibă între :min și :max elemente.',
],
'boolean' => 'Câmpul :attribute trebuie să fie adevărat sau fals.',
'confirmed' => 'Confirmarea :attribute nu se potrivește.',
'date' => 'Câmpul :attribute nu este o dată validă.',
'date_format' => 'Câmpul :attribute trebuie să fie în formatul :format.',
'different' => 'Câmpurile :attribute și :other trebuie să fie diferite.',
'digits' => 'Câmpul :attribute trebuie să aibă :digits cifre.',
'digits_between' => 'Câmpul :attribute trebuie să aibă între :min și :max cifre.',
'dimensions' => 'Câmpul :attribute are dimensiuni de imagine nevalide.',
'distinct' => 'Câmpul :attribute are o valoare duplicat.',
'email' => 'Câmpul :attribute trebuie să fie o adresă de e-mail validă.',
'exists' => 'Câmpul :attribute selectat nu este valid.',
'file' => 'Câmpul :attribute trebuie să fie un fișier.',
'filled' => 'Câmpul :attribute trebuie completat.',
'gt' => [
'numeric' => ':attribute trebuie să fie mai mare decât :value.',
'file' => ':attribute trebuie să aibă dimensiunea mai mare decât :value kilobaiți.',
'string' => ':attribute trebuie să aibă mai mult decât :value caractere.',
'array' => ':attribute trebuie să aibă mai mult decât :value elemente.',
],
'gte' => [
'numeric' => ':attribute trebuie să fie mai mare sau egal cu :value.',
'file' => ':attribute trebuie să aibă dimensiunea egală cu sau mai mare decât :value kilobaiți.',
'string' => ':attribute trebuie să fie mai mare sau egal cu :value caractere.',
'array' => ':attribute trebuie să aibă :value elemente sau mai multe.',
],
'image' => 'Câmpul :attribute trebuie să fie o imagine.',
'in' => 'Câmpul :attribute selectat nu este valid.',
'in_array' => 'Câmpul :attribute nu există în :other.',
'integer' => 'Câmpul :attribute trebuie să fie un număr întreg.',
'ip' => 'Câmpul :attribute trebuie să fie o adresă IP validă.',
'ipv4' => 'Câmpul :attribute trebuie să fie o adresă IPv4 validă.',
'ipv6' => 'Câmpul :attribute trebuie să fie o adresă IPv6 validă.',
'json' => 'Câmpul :attribute trebuie să fie un string JSON valid.',
'lt' => [
'numeric' => ':attribute trebuie să fie mai mic decât :value.',
'file' => ':attribute trebuie să aibă dimensiunea mai mică decât :value kilobaiți.',
'string' => ':attribute trebuie să aibă mai puțin decât :value caractere.',
'array' => ':attribute trebuie să aibă mai puțin decât :value elemente.',
],
'lte' => [
'numeric' => ':attribute trebuie să fie mai mic sau egal cu :value.',
'file' => ':attribute trebuie să aibă dimensiunea egală cu sau mai mică decât :value kilobaiți.',
'string' => ':attribute trebuie să fie mai mic sau egal cu :value caractere.',
'array' => ':attribute nu trebuie să aibă mai mult decât :value elemente.',
],
'max' => [
'numeric' => ':attribute nu poate fi mai mare decât :max.',
'file' => ':attribute nu trebuie să aibă dimensiunea mai mare decât :max kilobaiți.',
'string' => ':attribute nu trebuie să aibă mai mult decât :max caractere.',
'array' => ':attribute nu trebuie să aibă mai mult decât :value elemente.',
],
'mimes' => 'Câmpul :attribute trebuie să fie un fișier de tipul: :values.',
'mimetypes' => 'Câmpul :attribute trebuie să fie un fișier de tipul: :values.',
'min' => [
'numeric' => ':attribute trebuie sa aiba cel putin :min.',
'file' => ':attribute trebuie să aibă dimensiunea de cel puțin :min kilobaiți.',
'string' => ':attribute trebuie să aibă cel puțin :min caractere.',
'array' => ':attribute trebuie să aibă cel puțin :min elemente.',
],
'not_in' => 'Câmpul :attribute selectat nu este valid.',
'not_regex' => 'Câmpul :attribute nu are un format valid.',
'numeric' => 'Câmpul :attribute trebuie să fie un număr.',
'present' => 'Câmpul :attribute trebuie să fie prezent.',
'regex' => 'Câmpul :attribute nu are un format valid.',
'required' => 'Câmpul :attribute este obligatoriu.',
'required_if' => 'Câmpul :attribute este necesar când :other este :value.',
'required_unless' => 'Câmpul :attribute este necesar, cu excepția cazului :other este in :values.',
'required_with' => 'Câmpul :attribute este necesar când există :values.',
'required_with_all' => 'Câmpul :attribute este necesar când există :values.',
'required_without' => 'Câmpul :attribute este necesar când nu există :values.',
'required_without_all' => 'Câmpul :attribute este necesar când niciuna dintre valorile :values nu există.',
'same' => 'Câmpurile :attribute și :other trebuie să fie identice.',
'size' => [
'numeric' => 'Câmpul :attribute trebuie să fie :size.',
'file' => ':attribute trebuie să aibă dimensiunea de :size kilobaiți.',
'string' => ':attribute trebuie sa aiba :size caractere.',
'array' => 'Câmpul :attribute trebuie să aibă :size elemente.',
],
'string' => 'Câmpul :attribute trebuie să fie string.',
'timezone' => 'Câmpul :attribute trebuie să fie un fus orar valid.',
'unique' => 'Câmpul :attribute a fost deja folosit.',
'uploaded' => 'Câmpul :attribute nu a reușit încărcarea.',
'url' => 'Câmpul :attribute nu este un URL valid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'mesaj-personalizat',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];

View File

@ -15,7 +15,7 @@ return [
'password' => 'Mật khẩu phải gồm 6 ký tự và khớp với phần xác nhận.',
'reset' => 'Mật khẩu của bạn đã được đặt lại!',
'sent' => 'Hướng dẫn đặt lại mật khẩu đã được gửi vào email của bạn!',
'sent' => 'Nếu một tài khoản với địa chỉ e-mail của bạn tồn tại, chúng tôi đã gửi e-mail liên kết đặt lại mật khẩu của bạn.',
'token' => 'Token khởi tạo mật khẩu này không hợp lệ.',
'user' => "Không tìm thấy người dùng với địa chỉ email này.",

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => 'không có danh sách',
'order_by' => 'Sắp xếp bởi',
'order_by.title:asc' => 'Tiêu đề tăng dần',
'order_by.title:desc' => 'Tiêu đề giảm dần',
'order_by.url:asc' => 'URL tăng dần',
'order_by.url:desc' => 'URL giảm dần',
'order_by.created_at:asc' => 'Ngày tạo giảm dần',
'order_by.created_at:desc' => 'Ngày tạo tăng dần',
'order_by.number_links:asc' => 'Số liên kết tăng dần',
'order_by.number_links:desc' => 'Số liên kết giảm dần',
'order_by.title:asc' => 'Tiêu đề A-Z',
'order_by.title:desc' => 'Tiêu đề Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => 'Cũ nhất',
'order_by.created_at:desc' => 'Mới nhất',
'order_by.number_links:asc' => 'Ít liên kết nhất',
'order_by.number_links:desc' => 'Hầu hết các liên kết',
'order_by.random' => 'Ngẫu Nhiên',
'no_results' => 'Không tìm thấy kết quả nào.',

View File

@ -14,14 +14,15 @@ return [
'empty_lists' => '没有列表',
'order_by' => '排序方式',
'order_by.title:asc' => '按标题升序排序',
'order_by.title:desc' => '按标题降序排序',
'order_by.url:asc' => '按网址升序排序',
'order_by.url:desc' => '按网址降序排序',
'order_by.created_at:asc' => '按创建日期升序排序',
'order_by.created_at:desc' => '按创建日期降序排序',
'order_by.number_links:asc' => '链接数,升序',
'order_by.number_links:desc' => '链接数,降序',
'order_by.title:asc' => '标题A-Z',
'order_by.title:desc' => '标题Z-A',
'order_by.url:asc' => 'URL A-Z',
'order_by.url:desc' => 'URL Z-A',
'order_by.created_at:asc' => '最早创建',
'order_by.created_at:desc' => '最新的',
'order_by.number_links:asc' => '最少链接',
'order_by.number_links:desc' => '最多链接',
'order_by.random' => '随机',
'no_results' => '未找到任何结果。',

9255
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "linkace",
"version": "1.11.1",
"version": "1.12.2",
"description": "A small, selfhosted bookmark manager with advanced features, built with Laravel and Docker",
"homepage": "https://github.com/Kovah/LinkAce",
"repository": {
@ -16,10 +16,10 @@
"laravel-mix": "^6.0.31",
"postcss": "^8.4.5",
"sass": "^1.46.0",
"sass-loader": "^12.4.0"
"sass-loader": "^13.2.0"
},
"dependencies": {
"bootstrap": "^5.1.3",
"bootstrap": "^5.2.3",
"tom-select": "^2.0.0"
},
"scripts": {

View File

@ -151,6 +151,11 @@ $card-cap-bg: $darkmode-bg;
$card-bg: $darkmode-bg;
// Inputs
$input-group-addon-bg: $input-bg !default;
$input-group-addon-border-color: $input-border-color !default;
// List group
$list-group-bg: $darkmode-bg;
$list-group-color: $body-color;
@ -167,10 +172,11 @@ $list-group-action-color: $white;
$list-group-action-active-color: $white;
$list-group-action-active-bg: $darkmode-bg-dark;
// Alerts
$alert-bg-level: +8;
$alert-border-level: +7;
$alert-color-level: -8;
$alert-bg-scale: 70%;
$alert-border-scale: 50%;
$alert-color-scale: -60%;
// Code
$code-background-color: $darkmode-bg;

View File

@ -87,14 +87,14 @@ $color-contrast-light: $white;
// Options
$enable-dark-mode: false;
$enable-caret: false;
// Body
$body-bg: $white;
$body-color: $black;
$body-color-pale: $gray-600;
$body-color-muted: $gray;
$body-bg: $white;
$body-secondary-color: $gray-600;
// Links
@ -106,6 +106,7 @@ $link-hover-decoration: none;
// Components
$border-width: 1px;
$border-radius-xs: .2rem;
$line-height-sm: 1.5;
@ -115,13 +116,11 @@ $link-thumbnail-placeholder-color: $gray-200;
// Typography
$font-family-sans-serif: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
$font-weight-normal: 400;
$font-size-base: 1rem;
$font-size-xs: $font-size-base * .75;
$font-weight-normal: 400;
$text-muted: $body-color-muted;
$text-muted: $body-secondary-color;
// Tables
@ -130,6 +129,7 @@ $table-dark-color: $white;
// Buttons + Forms
$input-color: $body-color;
$input-border-color: $gray-300;
$input-btn-focus-width: .15rem;
$input-btn-padding-y-xs: .15rem;
@ -139,6 +139,8 @@ $input-btn-line-height-xs: $line-height-sm;
$input-placeholder-color: $text-muted;
$input-btn-border-width: $border-width;
// Buttons
$btn-padding-y-xs: $input-btn-padding-y-xs;

View File

@ -108,7 +108,7 @@ a.badge {
}
.text-pale {
color: $body-color-pale;
color: $body-secondary-color;
}
.btn-xs {
@ -178,6 +178,10 @@ code {
display: none !important;
}
.btn-cloud .btn {
margin: 2px;
}
.link-thumbnail {
box-shadow: inset 0 0 1px $secondary;
display: flex;

View File

@ -1,5 +1,5 @@
/*!
* Bootstrap v5.2.0 (https://getbootstrap.com/)
* Bootstrap v5.3.0 (https://getbootstrap.com/)
* Copyright 2011-2022 The Bootstrap Authors
* Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
@ -9,6 +9,7 @@
// Configuration
//@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/variables-dark";
@import "~bootstrap/scss/maps";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/utilities";

View File

@ -19,10 +19,11 @@ $select-color-optgroup: $white;
$select-color-optgroup-text: $dropdown-header-color;
$select-color-optgroup-border: $dropdown-divider-bg;
$select-color-dropdown: $white;
$select-color-dropdown-border-top: mix($input-border-color, $input-bg, 0.8);
$select-color-dropdown-border-top: color-mix($input-border-color, $input-bg, 80%);
$select-color-dropdown-item-active: $dropdown-link-hover-bg;
$select-color-dropdown-item-active-text: $dropdown-link-hover-color;
$select-color-dropdown-item-create-active-text: $dropdown-link-hover-color;
$select-color-dropdown-item-create-text: rgba(red($select-color-text), green($select-color-text), blue($select-color-text), 0.5);
$select-opacity-disabled: 0.5;
$select-shadow-input: none;
$select-shadow-input-focus: inset 0 1px 2px rgba($black, 0.15);

View File

@ -1,7 +1,7 @@
# DOCKERFILE DEVELOPMENT
# Installs MySQL Client for database exports, xDebug with PCov and Composer
FROM php:8.0-fpm
FROM php:8.0.29-fpm
WORKDIR /app
RUN apt-get update && apt-get install -y \

View File

@ -1,7 +1,8 @@
FROM php:8.2-fpm-alpine
# Install package and PHP dependencies
RUN apk add --no-cache mariadb-client postgresql postgresql-dev zip libzip-dev; \
RUN apk add --no-cache mariadb-client postgresql postgresql-dev sqlite zip libzip-dev; \
docker-php-ext-configure zip; \
docker-php-ext-install bcmath pdo_mysql pdo_pgsql zip; \
mkdir /ssl-certs
mkdir /ssl-certs; \
docker-php-source delete

View File

@ -21,6 +21,15 @@ COPY ./.env.example /app/.env
# Install dependencies using Composer
RUN composer install -n --prefer-dist --no-dev
RUN mv vendor/spatie/laravel-backup/resources/lang/de vendor/spatie/laravel-backup/resources/lang/de_DE; \
mv vendor/spatie/laravel-backup/resources/lang/en vendor/spatie/laravel-backup/resources/lang/en_US; \
mv vendor/spatie/laravel-backup/resources/lang/es vendor/spatie/laravel-backup/resources/lang/es_ES; \
mv vendor/spatie/laravel-backup/resources/lang/fr vendor/spatie/laravel-backup/resources/lang/fr_FR; \
mv vendor/spatie/laravel-backup/resources/lang/it vendor/spatie/laravel-backup/resources/lang/it_IT; \
mv vendor/spatie/laravel-backup/resources/lang/no vendor/spatie/laravel-backup/resources/lang/no_NO; \
mv vendor/spatie/laravel-backup/resources/lang/pl vendor/spatie/laravel-backup/resources/lang/pl_PL; \
mv vendor/spatie/laravel-backup/resources/lang/zh-CN vendor/spatie/laravel-backup/resources/lang/zh_CN
# ================================
# Compile all assets
FROM node:18 AS npm_builder
@ -61,14 +70,6 @@ COPY --from=builder /app/bootstrap/cache /app/bootstrap/cache
# Publish package resources
RUN php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"
RUN mv vendor/spatie/laravel-backup/resources/lang/de vendor/spatie/laravel-backup/resources/lang/de_DE; \
mv vendor/spatie/laravel-backup/resources/lang/en vendor/spatie/laravel-backup/resources/lang/en_US; \
mv vendor/spatie/laravel-backup/resources/lang/es vendor/spatie/laravel-backup/resources/lang/es_ES; \
mv vendor/spatie/laravel-backup/resources/lang/fr vendor/spatie/laravel-backup/resources/lang/fr_FR; \
mv vendor/spatie/laravel-backup/resources/lang/it vendor/spatie/laravel-backup/resources/lang/it_IT; \
mv vendor/spatie/laravel-backup/resources/lang/no vendor/spatie/laravel-backup/resources/lang/no_NO; \
mv vendor/spatie/laravel-backup/resources/lang/pl vendor/spatie/laravel-backup/resources/lang/pl_PL; \
mv vendor/spatie/laravel-backup/resources/lang/zh-CN vendor/spatie/laravel-backup/resources/lang/zh_CN
# Copy files from the theme build
COPY --from=npm_builder /srv/public/assets/dist/js /app/public/assets/dist/js

View File

@ -46,6 +46,7 @@ server {
try_files $uri /index.php;
include fastcgi.conf;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_keep_conn on;
fastcgi_intercept_errors on;
fastcgi_index index.php;

View File

@ -138,6 +138,10 @@ use App\Enums\ModelAttribute;
@lang('search.order_by.' . $order_by)
</option>
@endforeach
<option value="random"
@if($query_settings['order_by'] == 'random') selected @endif>
@lang('search.order_by.random')
</option>
</select>
</div>
@ -149,9 +153,11 @@ use App\Enums\ModelAttribute;
<div class="card-table mt-4">
@if($results->isEmpty())
<div class="alert alert-info m-3">
@lang('search.no_results')
</div>
@if($query_settings['performed_search'])
<div class="alert alert-info m-3">
@lang('search.no_results')
</div>
@endif
@else
@include('app.search.partials.table', ['results' => $results])
@endif

View File

@ -124,7 +124,7 @@
@lang('list.recent_lists')
</div>
<div class="card-body">
<div class="card-body btn-cloud">
@forelse($recent_lists as $list)
<a href="{{ route('lists.show', ['list' => $list]) }}" class="btn btn-light btn-sm m-1">
<x-models.name-with-user :model="$list"/>
@ -145,7 +145,7 @@
@lang('tag.recent_tags')
</div>
<div class="card-body">
<div class="card-body btn-cloud">
@forelse($recent_tags as $tag)
<a href="{{ route('tags.show', ['tag' => $tag]) }}" class="btn btn-light btn-sm m-1">
<x-models.name-with-user :model="$tag"/>

View File

@ -28,4 +28,8 @@
href="{{ route($baseRoute, ['orderBy' => 'title', 'orderDir' => 'desc']) }}">
@lang('search.order_by.title:desc')
</a>
<a class="dropdown-item small"
href="{{ route($baseRoute, ['orderBy' => 'random']) }}">
@lang('search.order_by.random')
</a>
</div>

View File

@ -1,4 +1,5 @@
*
!public/
!backup-temp/
!backups/
!.gitignore

2
storage/app/backups/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -27,7 +27,7 @@
<DD>Free tool for web application load testing that allows for the simulation of concurrent connections to your web
application&amp;#39;s APIs
<DT>
<A HREF="https://astralapp.com/" ADD_DATE="1549404064" PRIVATE="0" TAGS="favourites,Github,organization">Astral —
<A HREF="https://astralapp.com/" ADD_DATE="1549404064" TAGS="favourites,Github,organization">Astral —
Organize Your GitHub Stars With Ease</A>
<DD>Astral is the best way to manage your starred repositories on GitHub using tags, notes and a powerful search
feature.

View File

@ -49,6 +49,22 @@ class LinkControllerTest extends TestCase
->assertSee('https://public-link.com')
->assertSee('https://internal-link.com')
->assertDontSee('https://private-link.com');
$this->flushSession();
$this->get('links?orderBy=created_at&orderDir=asc')
->assertOk()
->assertSeeInOrder([
'https://linkace.example.com/test',
'https://the-new-linkace.com',
]);
$this->flushSession();
$this->get('links?orderBy=created_at&orderDir=wrong-asc')
->assertOk()
->assertSeeInOrder([
'https://the-new-linkace.com',
'https://linkace.example.com/test',
]);
}
public function testCreateView(): void

View File

@ -33,6 +33,22 @@ class ListControllerTest extends TestCase
->assertSee('Public List')
->assertSee('Internal List')
->assertDontSee('Private List');
$this->flushSession();
$this->get('lists?orderBy=created_at&orderDir=desc')
->assertOk()
->assertSeeInOrder([
'Super New List',
'A Test List',
]);
$this->flushSession();
$this->get('lists?orderBy=created_at&orderDir=wrong-desc')
->assertOk()
->assertSeeInOrder([
'A Test List',
'Super New List',
]);
}
public function testIndexViewWithValidFilterResult(): void

View File

@ -29,6 +29,22 @@ class TagControllerTest extends TestCase
$this->createTestTags();
$this->get('tags')->assertOk()->assertSee('Public Tag')->assertSee('Internal Tag')->assertDontSee('Private Tag');
$this->flushSession();
$this->get('tags?orderBy=created_at&orderDir=desc')
->assertOk()
->assertSeeInOrder([
'new-tag',
'a-tag',
]);
$this->flushSession();
$this->get('tags?orderBy=created_at&orderDir=wrong-desc')
->assertOk()
->assertSeeInOrder([
'a-tag',
'new-tag',
]);
}
public function testIndexViewWithValidFilterResult(): void