Merge branch 'dev' into ai-tagging

This commit is contained in:
Daniel
2024-12-09 22:33:03 -05:00
committed by GitHub
30 changed files with 1757 additions and 561 deletions

View File

@@ -37,6 +37,8 @@ IMPORT_LIMIT=
PLAYWRIGHT_LAUNCH_OPTIONS_EXECUTABLE_PATH=
MAX_WORKERS=
DISABLE_PRESERVATION=
NEXT_PUBLIC_RSS_POLLING_INTERVAL_MINUTES=
RSS_SUBSCRIPTION_LIMIT_PER_USER=
# AI Settings
NEXT_PUBLIC_OLLAMA_ENDPOINT_URL=

View File

@@ -6,7 +6,7 @@
<a href="https://discord.com/invite/CtuYV47nuJ"><img src="https://img.shields.io/discord/1117993124669702164?logo=discord&style=flat" alt="Discord"></a>
<a href="https://twitter.com/LinkwardenHQ"><img src="https://img.shields.io/twitter/follow/linkwarden" alt="Twitter"></a> <a href="https://news.ycombinator.com/item?id=36942308"><img src="https://img.shields.io/badge/Hacker%20News-280-%23FF6600"></img></a>
<a href="https://github.com/linkwarden/linkwarden/actions/workflows/release-container.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/linkwarden/linkwarden/release-container.yml"></a>
<a href="https://github.com/linkwarden/linkwarden/releases"><img alt="GitHub release" src="https://img.shields.io/github/v/release/linkwarden/linkwarden"></a>
<a href="https://opencollective.com/linkwarden"><img src="https://img.shields.io/opencollective/all/linkwarden" alt="Open Collective"></a>
</div>

View File

@@ -246,11 +246,10 @@ const renderItem = (
return (
<div ref={provided.innerRef} {...provided.draggableProps} className="mb-1">
<div
className={`${
currentPath === `/collections/${collection.id}`
className={`${currentPath === `/collections/${collection.id}`
? "bg-primary/20 is-active"
: "hover:bg-neutral/20"
} duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`}
} duration-100 flex gap-1 items-center pr-2 pl-1 rounded-md`}
>
{Dropdown(item as ExtendedTreeItem, onExpand, onCollapse)}
@@ -260,7 +259,7 @@ const renderItem = (
{...provided.dragHandleProps}
>
<div
className={`py-1 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
className={`py-1 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
{collection.icon ? (
<Icon

View File

@@ -5,6 +5,7 @@ import { Options } from "./types";
import CreatableSelect from "react-select/creatable";
import Select from "react-select";
import { useCollections } from "@/hooks/store/collections";
import clsx from "clsx";
type Props = {
onChange: any;
@@ -18,6 +19,7 @@ type Props = {
creatable?: boolean;
autoFocus?: boolean;
onBlur?: any;
className?: string;
};
export default function CollectionSelection({
@@ -27,6 +29,7 @@ export default function CollectionSelection({
creatable = true,
autoFocus,
onBlur,
className,
}: Props) {
const { data: collections = [] } = useCollections();
@@ -103,7 +106,7 @@ export default function CollectionSelection({
return (
<CreatableSelect
isClearable={false}
className="react-select-container"
className={clsx("react-select-container", className)}
classNamePrefix="react-select"
onChange={onChange}
options={options}
@@ -121,7 +124,7 @@ export default function CollectionSelection({
return (
<Select
isClearable={false}
className="react-select-container"
className={clsx("react-select-container", className)}
classNamePrefix="react-select"
onChange={onChange}
options={options}

View File

@@ -0,0 +1,60 @@
import React, { useEffect, useState } from "react";
import Modal from "../Modal";
import Button from "../ui/Button";
import { useTranslation } from "next-i18next";
import toast from "react-hot-toast";
import { RssSubscription } from "@prisma/client";
import { useDeleteRssSubscription } from "@/hooks/store/rss";
type Props = {
onClose: Function;
rssSubscription: RssSubscription;
};
export default function DeleteRssSubscriptionModal({
onClose,
rssSubscription,
}: Props) {
const { t } = useTranslation();
const [subscription, setSubscription] =
useState<RssSubscription>(rssSubscription);
const deleteRssSubscription = useDeleteRssSubscription();
useEffect(() => {
setSubscription(rssSubscription);
}, []);
const submit = async () => {
const load = toast.loading(t("deleting"));
await deleteRssSubscription.mutateAsync(subscription.id, {
onSettled: (_, error) => {
toast.dismiss(load);
if (error) {
toast.error(error.message);
} else {
onClose();
toast.success(t("rss_subscription_deleted"));
}
},
});
};
return (
<Modal toggleModal={onClose}>
<p className="text-xl font-thin text-red-500">{t("delete_link")}</p>
<div className="divider mb-3 mt-1"></div>
<div className="flex flex-col gap-3">
<p>{t("rss_deletion_confirmation")}</p>
<Button className="ml-auto" intent="destructive" onClick={submit}>
<i className="bi-trash text-xl" />
{t("delete")}
</Button>
</div>
</Modal>
);
}

View File

@@ -0,0 +1,104 @@
import React, { useState } from "react";
import Modal from "../Modal";
import { useTranslation } from "next-i18next";
import { useAddRssSubscription } from "@/hooks/store/rss";
import toast from "react-hot-toast";
import TextInput from "../TextInput";
import CollectionSelection from "../InputSelect/CollectionSelection";
type Props = {
onClose: Function;
};
export default function NewRssSubscriptionModal({ onClose }: Props) {
const { t } = useTranslation();
const addRssSubscription = useAddRssSubscription();
const [submitLoader, setSubmitLoader] = useState(false);
const [form, setForm] = useState({
name: "",
url: "",
collectionId: 0,
collectionName: "",
});
const submit = async () => {
if (submitLoader) return;
if (!form.name || !form.url || !form.collectionId) {
return toast.error(t("fill_all_fields"));
}
setSubmitLoader(true);
const load = toast.loading(t("creating"));
await addRssSubscription.mutateAsync(form, {
onSettled: (_, error) => {
setSubmitLoader(false);
toast.dismiss(load);
if (error) {
toast.error(error.message);
} else {
onClose();
toast.success(t("created"));
}
},
});
};
return (
<Modal toggleModal={onClose}>
<>
<p className="text-xl font-thin">{t("create_rss_subscription")}</p>
<div className="divider mb-3 mt-1"></div>
<div className="flex sm:flex-row flex-col gap-3 items-center">
<div className="w-full">
<label>{t("name")}</label>
<TextInput
type="text"
placeholder="Sample RSS"
className="bg-base-200 mt-2"
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
/>
</div>
<div className="w-full">
<label>{t("collection")}</label>
<CollectionSelection
className="mt-2"
onChange={(e: any) => {
if (e?.__isNew__) e.value = undefined;
setForm({
...form,
collectionId: e?.value,
collectionName: e?.label,
});
}}
/>
</div>
</div>
<div className="w-full mt-3">
<label>{t("link")}</label>
<TextInput
type="text"
placeholder="https://example.com/rss"
className="bg-base-200 mt-2"
value={form.url}
onChange={(e) => setForm({ ...form, url: e.target.value })}
/>
</div>
<div className="flex justify-end items-center mt-5">
<button
className="btn btn-accent dark:border-violet-400 text-white"
onClick={submit}
>
{t("create_token")}
</button>
</div>
</>
</Modal>
);
}

View File

@@ -50,6 +50,19 @@ export default function SettingsSidebar({ className }: { className?: string }) {
</div>
</Link>
<Link href="/settings/rss-subscriptions">
<div
className={`${
active === "/settings/rss-subscriptions"
? "bg-primary/20"
: "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-rss text-primary text-2xl"></i>
<p className="truncate w-full pr-7">RSS Subscriptions</p>
</div>
</Link>
<Link href="/settings/access-tokens">
<div
className={`${
@@ -85,7 +98,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
: "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-credit-card text-primary text-xl"></i>
<i className="bi-credit-card text-primary text-2xl"></i>
<p className="truncate w-full pr-7">{t("billing")}</p>
</div>
</Link>
@@ -104,7 +117,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div
className={`hover:bg-neutral/20 duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-question-circle text-primary text-xl"></i>
<i className="bi-question-circle text-primary text-2xl"></i>
<p className="truncate w-full pr-7">{t("help")}</p>
</div>
</Link>
@@ -112,7 +125,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div
className={`hover:bg-neutral/20 duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-github text-primary text-xl"></i>
<i className="bi-github text-primary text-2xl"></i>
<p className="truncate w-full pr-7">{t("github")}</p>
</div>
</Link>
@@ -120,7 +133,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div
className={`hover:bg-neutral/20 duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-twitter-x text-primary text-xl"></i>
<i className="bi-twitter-x text-primary text-2xl"></i>
<p className="truncate w-full pr-7">{t("twitter")}</p>
</div>
</Link>
@@ -128,7 +141,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div
className={`hover:bg-neutral/20 duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<i className="bi-mastodon text-primary text-xl"></i>
<i className="bi-mastodon text-primary text-2xl"></i>
<p className="truncate w-full pr-7">{t("mastodon")}</p>
</div>
</Link>

69
hooks/store/rss.tsx Normal file
View File

@@ -0,0 +1,69 @@
import { RssSubscription } from "@prisma/client";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useSession } from "next-auth/react";
interface RssSubscriptionWithCollectionName extends RssSubscription {
collection: {
name: string;
};
}
const useRssSubscriptions = () => {
const { status } = useSession();
return useQuery({
queryKey: ["rss-subscriptions"],
queryFn: async () => {
const response = await fetch("/api/v1/user/rss");
if (!response.ok) throw new Error("Failed to fetch rss subscriptions.");
const data = await response.json();
return data.response as RssSubscriptionWithCollectionName[];
},
enabled: status === "authenticated",
});
};
const useAddRssSubscription = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (body: Partial<RssSubscription>) => {
const response = await fetch("/api/v1/user/rss", {
body: JSON.stringify(body),
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (!response.ok) throw new Error(data.response);
return data.response;
},
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ["rss-subscriptions"] });
},
});
};
const useDeleteRssSubscription = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (rssSubscriptionId: number) => {
const response = await fetch(`/api/v1/user/rss/${rssSubscriptionId}`, {
method: "DELETE",
});
const data = await response.json();
if (!response.ok) throw new Error(data.response);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["rss-subscriptions"] });
},
});
};
export { useRssSubscriptions, useAddRssSubscription, useDeleteRssSubscription };

View File

@@ -1,7 +1,7 @@
import { prisma } from "@/lib/api/db";
import fetchTitleAndHeaders from "@/lib/shared/fetchTitleAndHeaders";
import createFolder from "@/lib/api/storage/createFolder";
import setLinkCollection from "../../setLinkCollection";
import setCollection from "../../setCollection";
import {
PostLinkSchema,
PostLinkSchemaType,
@@ -25,7 +25,11 @@ export default async function postLink(
const link = dataValidation.data;
const linkCollection = await setLinkCollection(link, userId);
const linkCollection = await setCollection({
userId,
collectionId: link.collection?.id,
collectionName: link.collection?.name,
});
if (!linkCollection)
return { response: "Collection is not accessible.", status: 400 };

View File

@@ -6,6 +6,7 @@ export default async function exportData(userId: number) {
include: {
collections: {
include: {
rssSubscriptions: true,
links: {
include: {
tags: true,

View File

@@ -199,7 +199,7 @@ const createLink = async (
tags?: string[],
importDate?: Date
) => {
url = url.trim().slice(0, 254);
url = url.trim().slice(0, 2047);
try {
new URL(url);
} catch (e) {

View File

@@ -63,7 +63,7 @@ export default async function importFromLinkwarden(
await prisma.link.create({
data: {
url: link.url?.trim().slice(0, 254),
url: link.url?.trim().slice(0, 2047),
name: link.name?.trim().slice(0, 254),
description: link.description?.trim().slice(0, 254),
importDate: new Date(link.importDate || link.createdAt),

View File

@@ -79,7 +79,7 @@ export default async function importFromWallabag(
pinnedBy: link.is_starred
? { connect: { id: userId } }
: undefined,
url: link.url?.trim().slice(0, 254),
url: link.url?.trim().slice(0, 2047),
name: link.title?.trim().slice(0, 254) || "",
textContent: link.content?.trim() || "",
importDate: link.created_at || null,

View File

@@ -5,7 +5,10 @@ const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: process.env.DEBUG === "true" ? ["query", "info", "warn", "error"] : ["warn", "error"]
log:
process.env.DEBUG === "true"
? ["query", "info", "warn", "error"]
: ["warn", "error"],
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

View File

@@ -8,7 +8,7 @@ const handleMonolith = async (link: Link, content: string) => {
try {
let html = execSync(
`monolith - -I -b ${link.url} ${
`monolith - -I -b "${link.url}" ${
process.env.MONOLITH_CUSTOM_OPTIONS || "-j -F -s"
} -o -`,
{

View File

@@ -1,18 +1,27 @@
import { prisma } from "./db";
import getPermission from "./getPermission";
import { UsersAndCollections } from "@prisma/client";
import { PostLinkSchemaType } from "../shared/schemaValidation";
const setLinkCollection = async (link: PostLinkSchemaType, userId: number) => {
if (link.collection?.id && typeof link.collection?.id === "number") {
type SetCollectionInput = {
userId: number;
collectionId?: number;
collectionName?: string;
};
const setCollection = async ({
userId,
collectionId,
collectionName,
}: SetCollectionInput) => {
if (collectionId) {
// Check if the collection exists
const existingCollection = await prisma.collection.findUnique({
where: {
id: link.collection.id,
},
where: { id: collectionId },
});
if (!existingCollection) return null;
// Check if the user has access to the collection
const collectionIsAccessible = await getPermission({
userId,
collectionId: existingCollection.id,
@@ -22,12 +31,16 @@ const setLinkCollection = async (link: PostLinkSchemaType, userId: number) => {
(e: UsersAndCollections) => e.userId === userId && e.canCreate
);
if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess))
if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) {
return null;
}
return existingCollection;
} else if (link.collection?.name) {
if (link.collection.name === "Unorganized") {
}
if (collectionName) {
// If the collection name is "Unorganized", find or create it
if (collectionName === "Unorganized") {
const firstTopLevelUnorganizedCollection =
await prisma.collection.findFirst({
where: {
@@ -41,18 +54,18 @@ const setLinkCollection = async (link: PostLinkSchemaType, userId: number) => {
return firstTopLevelUnorganizedCollection;
}
// Create a new collection with the given name
const newCollection = await prisma.collection.create({
data: {
name: link.collection.name.trim(),
name: collectionName.trim(),
ownerId: userId,
createdById: userId,
},
});
// Update the user's collection order
await prisma.user.update({
where: {
id: userId,
},
where: { id: userId },
data: {
collectionOrder: {
push: newCollection.id,
@@ -61,28 +74,29 @@ const setLinkCollection = async (link: PostLinkSchemaType, userId: number) => {
});
return newCollection;
} else {
const firstTopLevelUnorganizedCollection =
await prisma.collection.findFirst({
where: {
name: "Unorganized",
ownerId: userId,
parentId: null,
},
});
if (firstTopLevelUnorganizedCollection)
return firstTopLevelUnorganizedCollection;
else
return await prisma.collection.create({
data: {
name: "Unorganized",
ownerId: userId,
parentId: null,
createdById: userId,
},
});
}
// Default behavior for "Unorganized" collection if neither collectionId nor collectionName is provided
const firstTopLevelUnorganizedCollection = await prisma.collection.findFirst({
where: {
name: "Unorganized",
ownerId: userId,
parentId: null,
},
});
if (firstTopLevelUnorganizedCollection) {
return firstTopLevelUnorganizedCollection;
}
return await prisma.collection.create({
data: {
name: "Unorganized",
ownerId: userId,
parentId: null,
createdById: userId,
},
});
};
export default setLinkCollection;
export default setCollection;

View File

@@ -211,3 +211,10 @@ export const UpdateTagSchema = z.object({
});
export type UpdateTagSchemaType = z.infer<typeof UpdateTagSchema>;
export const PostRssSubscriptionSchema = z.object({
name: z.string().max(50),
url: z.string().url().max(2048),
collectionId: z.number().optional(),
collectionName: z.string().max(50).optional(),
});

View File

@@ -15,6 +15,7 @@ module.exports = {
"de",
"nl",
"tr",
"pl",
],
},
reloadOnPrerender: process.env.NODE_ENV === "development",

View File

@@ -1,6 +1,6 @@
{
"name": "linkwarden",
"version": "v2.8.3",
"version": "v2.8.4",
"main": "index.js",
"repository": "https://github.com/linkwarden/linkwarden.git",
"author": "Daniel31X13 <daniel31x13@gmail.com>",
@@ -77,6 +77,7 @@
"react-select": "^5.7.4",
"react-spinners": "^0.14.1",
"react-window": "^1.8.10",
"rss-parser": "^3.13.0",
"socks-proxy-agent": "^8.0.2",
"stripe": "^12.13.0",
"tailwind-merge": "^2.3.0",

View File

@@ -0,0 +1,29 @@
import { prisma } from "@/lib/api/db";
import verifyUser from "@/lib/api/verifyUser";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const user = await verifyUser({ req, res });
if (!user) return;
const rssId = Number(req.query.id);
if (req.method === "DELETE") {
const rssSubscription = await prisma.rssSubscription.findUnique({
where: { id: rssId },
});
if (!rssSubscription)
return res.status(404).json({ response: "RSS subscription not found." });
if (rssSubscription.ownerId !== user.id)
return res.status(403).json({ response: "Permission denied." });
await prisma.rssSubscription.delete({ where: { id: rssId } });
return res.status(200).json({ response: "RSS subscription deleted." });
}
}

View File

@@ -0,0 +1,98 @@
import { prisma } from "@/lib/api/db";
import setCollection from "@/lib/api/setCollection";
import verifyUser from "@/lib/api/verifyUser";
import { PostRssSubscriptionSchema } from "@/lib/shared/schemaValidation";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const user = await verifyUser({ req, res });
if (!user) return;
if (req.method === "GET") {
const response = await prisma.rssSubscription.findMany({
include: {
collection: {
select: {
name: true,
},
},
},
});
return res.status(200).json({ response });
}
if (req.method === "POST") {
const dataValidation = PostRssSubscriptionSchema.safeParse(req.body);
if (!dataValidation.success) {
return res.status(400).json({
response: `Error: ${
dataValidation.error.issues[0].message
} [${dataValidation.error.issues[0].path.join(", ")}]`,
});
}
const rssSubscriptionCount = await prisma.rssSubscription.count({
where: {
ownerId: user.id,
},
});
const RSS_SUBSCRIPTION_LIMIT_PER_USER =
Number(process.env.RSS_SUBSCRIPTION_LIMIT_PER_USER) || 20;
if (rssSubscriptionCount >= 10) {
return res.status(403).json({
response: `You have reached the limit of ${RSS_SUBSCRIPTION_LIMIT_PER_USER} RSS subscriptions.`,
});
}
const { name, url, collectionId, collectionName } = dataValidation.data;
const linkCollection = await setCollection({
userId: user.id,
collectionId: collectionId,
collectionName: collectionName,
});
if (!linkCollection) {
return res.status(403).json({
response: "You do not have permission to add a link to this collection",
});
}
const existingRssSubscription = await prisma.rssSubscription.findFirst({
where: {
name: name,
ownerId: user.id,
},
});
if (existingRssSubscription) {
return {
response: "RSS Subscription with that name already exists.",
status: 400,
};
}
const response = await prisma.rssSubscription.create({
data: {
name,
url,
ownerId: user.id,
lastBuildDate: new Date(),
collection: {
connect: {
id: linkCollection.id,
},
},
},
});
return res.status(200).json({ response });
}
}

View File

@@ -0,0 +1,95 @@
import SettingsLayout from "@/layouts/SettingsLayout";
import { useTranslation } from "next-i18next";
import getServerSideProps from "@/lib/client/getServerSideProps";
import { useRssSubscriptions } from "@/hooks/store/rss";
import DeleteRssSubscriptionModal from "@/components/ModalContent/DeleteRssSubscriptionModal";
import { useState } from "react";
import { RssSubscription } from "@prisma/client";
import NewRssSubscriptionModal from "@/components/ModalContent/NewRssSubscriptionModal";
import Link from "next/link";
export default function RssSubscriptions() {
const { t } = useTranslation();
const { data: rssSubscriptions = [] } = useRssSubscriptions();
const [deleteSubscriptionModal, setDeleteSubscriptionModal] = useState(false);
const [newSubscriptionModal, setNewSubscriptionModal] = useState(false);
const [selectedSubscription, setSelectedSubscription] =
useState<RssSubscription | null>(null);
const openDeleteModal = (subscription: RssSubscription) => {
setSelectedSubscription(subscription);
setDeleteSubscriptionModal(true);
};
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">
{t("rss_subscriptions")}
</p>
<div className="divider my-3"></div>
<div className="flex flex-col gap-3">
<p>
{t("rss_subscriptions_desc", {
number: process.env.NEXT_PUBLIC_RSS_POLLING_INTERVAL_MINUTES || 60,
})}
</p>
<button
className={`btn ml-auto btn-accent dark:border-violet-400 text-white tracking-wider w-fit flex items-center gap-2`}
onClick={() => {
setNewSubscriptionModal(true);
}}
>
{t("new_rss_subscription")}
</button>
{rssSubscriptions.length > 0 && (
<table className="table mt-2 overflow-x-auto">
<thead>
<tr>
<th>{t("name")}</th>
<th>{t("link")}</th>
<th>{t("collection")}</th>
<th></th>
</tr>
</thead>
<tbody>
{rssSubscriptions.map((rssSubscription, i) => (
<tr key={i}>
<td>{rssSubscription.name}</td>
<td>{rssSubscription.url}</td>
<td>{rssSubscription.collection.name}</td>
<td>
<button
className="btn btn-sm btn-ghost btn-square hover:bg-red-500"
onClick={() => openDeleteModal(rssSubscription)}
>
<i className="bi-x text-lg"></i>
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
{newSubscriptionModal && (
<NewRssSubscriptionModal
onClose={() => setNewSubscriptionModal(false)}
/>
)}
{deleteSubscriptionModal && selectedSubscription && (
<DeleteRssSubscriptionModal
rssSubscription={selectedSubscription}
onClose={() => {
setDeleteSubscriptionModal(false);
setSelectedSubscription(null);
}}
/>
)}
</SettingsLayout>
);
}
export { getServerSideProps };

View File

@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "RssSubscription" (
"id" SERIAL NOT NULL,
"url" TEXT NOT NULL,
"name" TEXT NOT NULL,
"lastBuildDate" TIMESTAMP(3),
"collectionId" INTEGER NOT NULL,
"ownerId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "RssSubscription_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "RssSubscription" ADD CONSTRAINT "RssSubscription_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "Collection"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -104,25 +104,26 @@ model PasswordResetToken {
}
model Collection {
id Int @id @default(autoincrement())
name String
description String @default("")
icon String?
iconWeight String?
color String @default("#0ea5e9")
parentId Int?
parent Collection? @relation("SubCollections", fields: [parentId], references: [id])
subCollections Collection[] @relation("SubCollections")
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
members UsersAndCollections[]
createdBy User? @relation("CreatedCollections", fields: [createdById], references: [id])
createdById Int?
links Link[]
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
id Int @id @default(autoincrement())
name String
description String @default("")
icon String?
iconWeight String?
color String @default("#0ea5e9")
parentId Int?
parent Collection? @relation("SubCollections", fields: [parentId], references: [id])
subCollections Collection[] @relation("SubCollections")
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
members UsersAndCollections[]
createdBy User? @relation("CreatedCollections", fields: [createdById], references: [id])
createdById Int?
links Link[]
rssSubscriptions RssSubscription[]
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@index([ownerId])
}
@@ -198,7 +199,7 @@ model Subscription {
model AccessToken {
id Int @id @default(autoincrement())
name String
name String
user User @relation(fields: [userId], references: [id])
userId Int
token String @unique
@@ -209,3 +210,15 @@ model AccessToken {
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
}
model RssSubscription {
id Int @id @default(autoincrement())
url String
name String
lastBuildDate DateTime?
collection Collection @relation(fields: [collectionId], references: [id])
collectionId Int
ownerId Int
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
}

View File

@@ -394,7 +394,7 @@
"upload_preview_image": "Upload Preview Image",
"columns": "Columns",
"default": "Default",
"invalid_url_guide":"Please enter a valid Address for the Link. (It should start with http/https)",
"invalid_url_guide": "Please enter a valid Address for the Link. (It should start with http/https)",
"email_invalid": "Please enter a valid email address.",
"username_invalid_guide": "Username has to be at least 3 characters, no spaces and special characters are allowed.",
"team_management": "Team Management",
@@ -439,4 +439,10 @@
"disabled": "Disabled",
"ai_tagging_disabled_desc": "AI tagging is disabled.",
"tag_selection_placeholder": "Choose or add custom tags…"
"rss_subscriptions": "RSS Subscriptions",
"rss_subscriptions_desc": "RSS Subscriptions are a way to keep up with your favorite websites and blogs. Linkwarden will automatically fetch the latest articles every {{number}} minutes from the feeds you provide.",
"rss_deletion_confirmation": "Are you sure you want to delete this RSS Subscription?",
"new_rss_subscription": "New RSS Subscription",
"rss_subscription_deleted": "RSS Subscription deleted!",
"create_rss_subscription": "Create RSS Subscription"
}

View File

@@ -1,375 +1,434 @@
{
"user_administration": "Administración de usuarios",
"search_users": "Buscar usuarios",
"no_users_found": "No se ha encontrado el usuario.",
"no_user_found_in_search": "No se ha encontrado el usuario con esos parámetros de búsqueda.",
"username": "Nombre de Usuario",
"email": "Email",
"subscribed": "Suscrito",
"created_at": "Creado en",
"not_available": "N/D",
"check_your_email": "Por favor, comprueba tu correo electrónico",
"authenticating": "Autenticando...",
"verification_email_sent": "Email de verificación enviado.",
"verification_email_sent_desc": "Se ha enviado un enlace de inicio de sesión a tu correo electrónico. Si no ves el correo, revisa tu carpeta de spam.",
"resend_email": "Reenviar correo electrónico",
"invalid_credentials": "Credenciales inválidas.",
"fill_all_fields": "Por favor, rellena todos los campos.",
"enter_credentials": "Ingresa tus credenciales.",
"username_or_email": "Nombre de usuario o correo electrónico",
"password": "Contraseña",
"confirm_password": "Confirmar contraseña",
"forgot_password": "¿Has olvidado tu contraseña?",
"login": "Iniciar sesión",
"or_continue_with": "O continue con",
"new_here": "¿Nuevo por aquí?",
"sign_up": "Registrarse",
"sign_in_to_your_account": "Inicia sesión en tu cuenta.",
"dashboard_desc": "Un breve resumen de tus datos",
"link": "Enlace",
"links": "Enlaces",
"collection": "Colección",
"collections": "Colecciones",
"tag": "Etiqueta",
"tags": "Etiquetas",
"recent": "Reciente",
"recent_links_desc": "Enlaces añadidos recientemente",
"view_all": "Ver todo",
"view_added_links_here": "¡Mira todos tus enlaces añadidos recientemente aquí!",
"view_added_links_here_desc": "Esta sección mostrará tus enlaces añadidos más recientes en todas las colecciones a las que tengas acceso.",
"add_link": "Añade nuevos enlaces",
"import_links": "Importar enlaces",
"from_linkwarden": "Desde Linkwarden",
"from_html": "Desde un archivo HTML de marcadores",
"from_wallabag": "Desde Wallabag (archivo JSON)",
"pinned": "Anclado",
"pinned_links_desc": "Tus enlaces anclados",
"pin_favorite_links_here": "¡Ancla tus enlaces favoritos aquí!",
"pin_favorite_links_here_desc": "Puedes anclar tus enlaces favoritos haciendo clic en los tres puntos en cada enlace y seleccionando Anclar en el tablero.",
"sending_password_link": "Enviando enlace de recuperación de contraseña...",
"password_email_prompt": "Ingresa tu correo electrónico para que podamos enviarte un enlace para crear una nueva contraseña.",
"send_reset_link": "Enviar enlace de restablecimiento",
"reset_email_sent_desc": "Revisa tu correo electrónico para encontrar un enlace para restablecer tu contraseña. Si no aparece en unos minutos, revisa tu carpeta de spam.",
"back_to_login": "Volver al inicio de sesión",
"email_sent": "¡Email enviado!",
"passwords_mismatch": "Las contraseñas no coinciden.",
"password_too_short": "La contraseña deben tener al menos 8 caracteres.",
"creating_account": "Creando cuenta...",
"account_created": "¡Cuenta creada!",
"trial_offer_desc": "¡Desbloquea {{count}} días de servicio Premium gratis!",
"register_desc": "Crear una nueva cuenta",
"registration_disabled_desc": "El registro está deshabilitado para esta instancia. Por favor, contacta al administrador en caso de cualquier problema.",
"enter_details": "Ingresa tus datos",
"display_name": "Mostrar nombre",
"sign_up_agreement": "Al registrarte, aceptas nuestros <0>Términos de Servicio</0> y nuestra <1>Política de Privacidad</1>.",
"need_help": "¿Necesitas ayuda?",
"get_in_touch": "Contáctanos",
"already_registered": "¿Ya tienes una cuenta?",
"deleting_selections": "Eliminando seleccionados...",
"links_deleted": "{{count}} enlaces eliminados.",
"link_deleted": "1 enlace eliminado.",
"links_selected": "{{count}} enlaces seleccionados",
"link_selected": "1 enlace seleccionado",
"nothing_selected": "Nada seleccionado",
"edit": "Editar",
"delete": "Eliminar",
"nothing_found": "No se encontró nada.",
"redirecting_to_stripe": "Redirigiendo a Stripe...",
"subscribe_title": "¡Suscríbete a Linkwarden!",
"subscribe_desc": "Serás redirigido a Stripe. No dudes en ponerte en contacto con nosotros en <0>support@linkwarden.app</0> en caso de cualquier problema.",
"monthly": "Mensual",
"yearly": "Anual",
"discount_percent": "{{percent}}% de descuento",
"billed_monthly": "Facturado mensualmente",
"billed_yearly": "Facturado anualmente",
"total": "Total",
"total_annual_desc": "Prueba gratuita de {{count}} días, luego ${{annualPrice}} al año",
"total_monthly_desc": "Prueba gratuita de {{count}} días, luego ${{annualPrice}} al mes",
"plus_tax": "+ IVA si corresponde",
"complete_subscription": "Completar suscripción",
"sign_out": "Cerrar sesión",
"access_tokens": "Tokens de acceso",
"access_tokens_description": "Los tokens de acceso se pueden usar para acceder a Linkwarden desde otras aplicaciones y servicios sin revelar tu nombre de usuario y contraseña.",
"new_token": "Nuevo token de acceso",
"name": "Nombre",
"created_success": "¡Creado!",
"created": "Creado",
"expires": "Expira",
"accountSettings": "Ajustes de la cuenta",
"language": "Idioma",
"profile_photo": "Foto de perfil",
"upload_new_photo": "Sube una foto nueva...",
"remove_photo": "Eliminar foto",
"make_profile_private": "Hacer perfil privado",
"profile_privacy_info": "Esto limitará quién puede encontrarte y agregarte a nuevas colecciones.",
"whitelisted_users": "Usuarios autorizados",
"whitelisted_users_info": "Por favor, proporciona el nombre de usuario de los usuarios a los que deseas conceder visibilidad de tu perfil, separados por comas.",
"whitelisted_users_placeholder": "Tu perfil está oculto para todos en este momento...",
"save_changes": "Guardar cambios",
"import_export": "Importar y exportar",
"import_data": "Importa tus datos desde otras plataformas.",
"download_data": "Descarga tus datos al instante.",
"export_data": "Exportar datos",
"delete_account": "Eliminar cuenta",
"delete_account_warning": "Esto eliminará permanentemente TODOS los enlaces, colecciones, etiquetas y datos archivados en tu cuenta.",
"cancel_subscription_notice": "También cancelará tu suscripción.",
"account_deletion_page": "Página de eliminación de cuenta",
"applying_settings": "Aplicando configuraciones...",
"settings_applied":"¡Configuraciones aplicadas!",
"email_change_request": "Solicitud de cambio de correo electrónico enviado. Por favor, verifica el nuevo correo electrónico.",
"image_upload_size_error": "Por favor, selecciona un archivo PNG o JPEG que sea menor de 1 MB.",
"image_upload_format_error": "Formato de archivo no válido.",
"importing_bookmarks": "Importando marcadores...",
"import_success": "¡Marcadores importados! Recargando la página...",
"more_coming_soon": "¡Más próximamente!",
"billing_settings": "Ajustes de facturación",
"manage_subscription_intro": "Para gestionar/cancelar tu suscripción, visita el",
"billing_portal": "Portal de facturación",
"help_contact_intro": "Si necesitas ayuda o has encontrado algún problema, no dudes en contactarnos en:",
"fill_required_fields": "Por favor, rellena los campos obligatorios.",
"deleting_message": "Eliminando todo, por favor espera...",
"delete_warning": "Esto eliminará permanentemente todos los enlaces, colecciones, etiquetas y datos archivados en tu cuenta. También cerrará la sesión. ¡Esta acción es irreversible!",
"optional": "Opcional",
"feedback_help": "(¡pero realmente nos ayuda a mejorar!)",
"reason_for_cancellation": "Motivos de la cancelación",
"please_specify": "Por favor, especifica",
"customer_service": "Servicio al usuario",
"low_quality": "Mala calidad",
"missing_features": "Le faltan utilidades",
"switched_service": "Cambio a otra plataforma",
"too_complex": "Muy complejo",
"too_expensive": "Muy caro",
"unused": "No lo uso",
"other": "Otro",
"more_information": "Más información (cuantos más detalles, más útil será)",
"feedback_placeholder": "ej. necesitaba una función que...",
"delete_your_account": "Elliminar tu cuenta",
"change_password": "Cambiar contraseña",
"password_length_error": "La contraseña debe tener al menos 8 caracteres.",
"applying_changes": "Aplicando...",
"password_change_instructions": "Para cambiar tu contraseña, por favor completa lo siguiente. Tu contraseña debe tener al menos 8 caracteres.",
"old_password": "Contraseña actual",
"new_password": "Nueva contraseña",
"preference": "Preferencias",
"select_theme": "Seleccionar tema",
"dark": "Oscuro",
"light": "Claro",
"archive_settings": "Ajustes de archivado",
"formats_to_archive": "Formatos para archivar/conservar páginas web:",
"screenshot": "Captura de pantalla",
"pdf": "PDF",
"archive_org_snapshot": "Captura en archive.org",
"link_settings": "Ajustes de enlaces",
"prevent_duplicate_links": "Prevenir enlaces duplicados",
"clicking_on_links_should": "Hacer click en enlaces debería:",
"open_original_content": "Abrir el contenido original",
"open_pdf_if_available": "Abrir PDF, si está disponible",
"open_readable_if_available": "Abrir versión lectura, si está disponible",
"open_screenshot_if_available": "Abrir la captura de pantalla, si está disponible",
"open_webpage_if_available": "Abrir copia local de la web, si está disponible",
"tag_renamed": "¡Etiqueta renombrada!",
"tag_deleted": "¡Etiqueta eliminada!",
"rename_tag": "Renombrar etiqueta",
"delete_tag": "Eliminar etiqueta",
"list_created_with_linkwarden": "Lista generada con Linkwarden",
"by_author": "Por {{author}}.",
"by_author_and_other": "Por {{author}} y {{count}} más.",
"by_author_and_others": "Por {{author}} y {{count}} más.",
"search_count_link": "Buscar {{count}} enlace",
"search_count_links": "Buscar {{count}} enlaces",
"collection_is_empty": "Esta colección está vacía...",
"all_links": "Todos los enlaces",
"all_links_desc": "Enlaces de todas las colecciones",
"you_have_not_added_any_links": "Aún no has creado ningún enlace",
"collections_you_own": "Colecciones que te pertenecen",
"new_collection": "Nueva colección",
"other_collections": "Otras colecciones",
"other_collections_desc": "Colecciones compartidas de las que eres miembro",
"showing_count_results": "Mostrando {{count}} resultados",
"showing_count_result": "Mostrando {{count}} resultado",
"edit_collection_info": "Editar información de la colección",
"share_and_collaborate": "Compartir y colaborar",
"view_team": "Ver grupo",
"team": "Grupo",
"create_subcollection": "Crear subcolección",
"delete_collection": "Eliminar colección",
"leave_collection": "Salir de la colección",
"email_verified_signing_out": "Correo electrónico verificado. Cerrando sesión...",
"invalid_token": "Token inválido.",
"sending_password_recovery_link": "Enviando enlace de recuperación de contraseña",
"please_fill_all_fields": "Por favor, rellena todos los campos.",
"password_updated": "¡Contraseña actualizada!",
"reset_password": "Restablecer contraseña",
"enter_email_for_new_password": "Ingresa tu correo electrónico para que podamos enviarte un enlace para crear una nueva contraseña.",
"update_password": "Actualizar contraseña",
"password_successfully_updated": "Tu contraseña ha sido actualizada correctamente.",
"user_already_member": "El usuario ya existe.",
"you_are_already_collection_owner": "Ya eres el dueño de esta colección.",
"date_newest_first": "Fecha (Nuevos primero)",
"date_oldest_first": "Fecha (Antiguos primero)",
"name_az": "Nombre (A-Z)",
"name_za": "Nombre (Z-A)",
"description_az": "Descripción (A-Z)",
"description_za": "Descripción (Z-A)",
"all_rights_reserved": "© {{date}} <0>Linkwarden</0>. Todos los derechos reservados.",
"you_have_no_collections": "No tienes ninguna colección...",
"you_have_no_tags": "No tienes ninguna etiqueta...",
"cant_change_collection_you_dont_own": "No puedes hacer cambios en una colección que no te pertenece.",
"account": "Cuenta",
"billing": "Facturación",
"linkwarden_version": "Linkwarden {{version}}",
"help": "Ayuda",
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"link_preservation_in_queue": "La conservación del enlace está actualmente en cola.",
"check_back_later": "Por favor, vuelve más tarde para ver el resultado.",
"there_are_more_formats": "Hay más formatos de conservación en cola.",
"settings": "Ajustes",
"switch_to": "Cambiar a {{theme}}",
"logout": "Cerrar sesión",
"start_journey": "¡Empieza tu experiencia creando un nuevo enlace!",
"create_new_link": "Crear un nuevo enlace",
"new_link": "Nuevo enlace",
"create_new": "Crear nuevo...",
"pwa_install_prompt": "Instala Linkwarden en la pantalla de inicio para un acceso más rápido y una experiencia mejorada. <0>Aprende más</0>",
"full_content": "Contenido completo",
"slower": "Más despacio",
"new_version_announcement": "¡Descubre las novedades en <0>Linkwarden {{version}}!</0>",
"creating": "Creando...",
"upload_file": "Subir archivo",
"file": "Archivo",
"file_types": "PDF, PNG, JPG (Hasta {{size}} MB)",
"description": "Descripción",
"auto_generated": "Será generado automáticamente si no se proporciona nada.",
"example_link": "ej. Enlace de ejemplo",
"hide": "Esconder",
"more": "Más",
"options": "Opciones",
"description_placeholder": "Notas, ideas, etc.",
"deleting": "Eliminando...",
"token_revoked": "Token anulado.",
"revoke_token": "Anular token",
"revoke_confirmation": "¿Estás seguro de que deseas anular este token de acceso? Cualquier aplicación o servicio que use este token ya no podrá acceder a Linkwarden con él.",
"revoke": "Anular",
"sending_request": "Enviando solicitud...",
"link_being_archived": "El enlace está siendo archivado...",
"preserved_formats": "Formatos conservados",
"available_formats": "Los siguientes formatos están disponibles para este enlace",
"readable": "Versión lectura",
"preservation_in_queue": "La conservación del enlace está en la cola.",
"view_latest_snapshot": "Ver la última captura en archive.org",
"refresh_preserved_formats": "Actualizar formatos conservados",
"this_deletes_current_preservations": "Esto elimina las conservaciones actuales.",
"create_new_user": "Crear nuevo usuario",
"placeholder_johnny": "Johnny",
"placeholder_email": "johnny@ejemplo.com",
"placeholder_john": "john",
"user_created": "¡Usuario creado!",
"fill_all_fields_error": "Por favor, rellena todos los campos.",
"password_change_note": "<0>Nota:</0> Por favor, asegúrate de informar al usuario que necesita cambiar su contraseña.",
"create_user": "Crear usuario",
"creating_token": "Creando token...",
"token_created": "¡Token creado!",
"access_token_created": "Token de acceso creado",
"token_creation_notice": "Tu nuevo token ha sido creado. Por favor, cópialo y guárdalo en un lugar seguro. No lo mostraremos de nuevo.",
"copied_to_clipboard": "¡Copiado al portapapeles!",
"copy_to_clipboard": "Copiar al portapapeles",
"create_access_token": "Crear un token de acceso",
"expires_in": "Caduca en",
"token_name_placeholder": "ej. Para el atajo de iOS",
"create_token": "Crear token de acceso",
"7_days": "7 días",
"30_days": "30 días",
"60_days": "60 días",
"90_days": "90 días",
"no_expiration": "Sin caducidad",
"creating_link": "Creando enlace...",
"link_created": "¡Enlace creado!",
"link_name_placeholder": "Se generará automáticamente si se deja en blanco.",
"link_url_placeholder": "ej. http://ejemplo.com/",
"link_description_placeholder": "Notas, ideas, etc.",
"more_options": "Más opciones",
"hide_options": "Ocultar opciones",
"create_link": "Crear enlace",
"new_sub_collection": "Nueva subcolección",
"for_collection": "Para {{name}}",
"create_new_collection": "Crear una nueva colección",
"color": "Color",
"reset": "Reestablecer",
"updating_collection": "Actualizando colección...",
"collection_name_placeholder": "ej. Colección de ejemplo",
"collection_description_placeholder": "El propósito de esta colección...",
"create_collection_button": "Crear colección",
"password_change_warning": "Por favor, confirma tu contraseña antes de cambiar tu email.",
"stripe_update_note": " Actualizar este campo cambiará también tu email de facturación en Stripe.",
"sso_will_be_removed_warning": "Si cambias tu correo electrónico, cualquier conexión SSO existente de {{service}} será eliminada.",
"old_email": "Correo electrónico actual",
"new_email": "Correo electrónico nuevo",
"confirm": "Confirmar",
"edit_link": "Editar enlace",
"updating": "Actualizando...",
"updated": "¡Actualizado!",
"placeholder_example_link": "Ej. Enlace de ejemplo",
"make_collection_public": "Hacer pública la colección",
"make_collection_public_checkbox": "Hacer pública esta colección",
"make_collection_public_desc": "Esto permitirá que cualquier persona vea esta colección y sus usuarios.",
"sharable_link_guide": "Enlace para compartir (haz clic para copiar)",
"copied": "¡Copiado!",
"members": "Participantes",
"members_username_placeholder": "Nombre de usuario (sin la '@')",
"owner": "Propietario",
"admin": "Administrador",
"contributor": "Colaborador",
"viewer": "Lector",
"viewer_desc": "Acceso de solo lectura",
"contributor_desc": "Puede ver y crear enlaces",
"admin_desc": "Acceso completo a todos los enlaces",
"remove_member": "Eliminar participante",
"placeholder_example_collection": "Ej. colección de ejemplo",
"placeholder_collection_purpose": "El propósito de esta colección...",
"deleting_user": "Eliminando...",
"user_deleted": "Usuario eliminado.",
"delete_user": "Eliminar usuario",
"confirm_user_deletion": "¿Estás seguro de que quieres eliminar este usuario?",
"irreversible_action_warning": "¡Esta acción es irreversible!",
"delete_confirmation": "Eliminar, sé lo que estoy haciendo",
"delete_link": "Eliminar enlace",
"deleted": "Eliminado.",
"link_deletion_confirmation_message": "¿Estás seguro de que quieres eliminar este enlace?",
"warning": "Advertencia",
"irreversible_warning": "¡Esta acción es irreversible!",
"shift_key_tip": "Mantén presionada la tecla 'Mayúscula' mientras haces clic en 'Eliminar' para omitir esta confirmación en el futuro.",
"deleting_collection": "Eliminando...",
"collection_deleted": "Colección eliminada.",
"confirm_deletion_prompt": "Para confirmar, escribe \"{{name}}\" en el campo a continuación:",
"type_name_placeholder": "Escribe \"{{name}}\" aquí.",
"deletion_warning": "Eliminar esta colección borrará permanentemente todo su contenido, y se volverá inaccesible para todos, incluidos los miembros con acceso previamente.",
"leave_prompt": "Haz clic en el botón de abajo para abandonar la colección actual.",
"leave": "Abandonar",
"edit_links": "Editar {{count}} enlaces",
"move_to_collection": "Mover a colección",
"add_tags": "Añadir etiquetas",
"remove_previous_tags": "Eliminar etiquetas anteriores",
"delete_links": "Eliminar {{count}} enlaces",
"links_deletion_confirmation_message": "¿Estás seguro de que deseas eliminar {{count}} enlaces? ",
"warning_irreversible": "Advertencia: ¡Esta acción es irreversible!",
"shift_key_instruction": "Mantén presionada la tecla 'Mayúscula' mientras haces clic en 'Eliminar' para omitir esta confirmación en el futuro.",
"link_selection_error": "No tienes permiso para editar o eliminar este item.",
"no_description": "No se ha proporcionado una descripción.",
"applying": "Aplicando...",
"unpin": "Desanclar",
"pin_to_dashboard": "Anclar al tablero",
"show_link_details": "Mostrar detalles del enlace",
"hide_link_details": "Ocultar detalles del enlace",
"link_pinned": "¡Enlace anclado!",
"link_unpinned": "¡Enlace desanclado!",
"webpage": "Página web",
"server_administration": "Administración del servidor",
"all_collections": "Todas las colecciones",
"dashboard": "Tablero",
"demo_title": "Solo para demostración",
"demo_desc": "Esta es solo una instancia de demostración de Linkwarden y las cargas están deshabilitadas.",
"demo_desc_2": "Si deseas probar la versión completa, puedes registrarte para una prueba gratuita en:",
"demo_button": "Iniciar sesión como usuario demo"
}
"user_administration": "Administración de usuarios",
"search_users": "Buscar usuarios",
"no_users_found": "No se ha encontrado el usuario.",
"no_user_found_in_search": "No se ha encontrado el usuario con esos parámetros de búsqueda.",
"username": "Nombre de Usuario",
"email": "Email",
"subscribed": "Suscrito",
"created_at": "Creado en",
"not_available": "N/D",
"check_your_email": "Por favor, comprueba tu correo electrónico",
"authenticating": "Autenticando...",
"verification_email_sent": "Email de verificación enviado.",
"verification_email_sent_desc": "Se ha enviado un enlace de inicio de sesión a tu correo electrónico. Si no ves el correo, revisa tu carpeta de spam.",
"resend_email": "Reenviar correo electrónico",
"invalid_credentials": "Credenciales inválidas.",
"fill_all_fields": "Por favor, rellena todos los campos.",
"enter_credentials": "Ingresa tus credenciales.",
"username_or_email": "Nombre de usuario o correo electrónico",
"password": "Contraseña",
"confirm_password": "Confirmar contraseña",
"forgot_password": "¿Has olvidado tu contraseña?",
"login": "Iniciar sesión",
"or_continue_with": "O continue con",
"new_here": "¿Nuevo por aquí?",
"sign_up": "Registrarse",
"sign_in_to_your_account": "Inicia sesión en tu cuenta.",
"dashboard_desc": "Un breve resumen de tus datos",
"link": "Enlace",
"links": "Enlaces",
"collection": "Colección",
"collections": "Colecciones",
"tag": "Etiqueta",
"tags": "Etiquetas",
"recent": "Reciente",
"recent_links_desc": "Enlaces añadidos recientemente",
"view_all": "Ver todo",
"view_added_links_here": "¡Mira todos tus enlaces añadidos recientemente aquí!",
"view_added_links_here_desc": "Esta sección mostrará tus enlaces añadidos más recientes en todas las colecciones a las que tengas acceso.",
"add_link": "Añade nuevos enlaces",
"import_links": "Importar enlaces",
"from_linkwarden": "Desde Linkwarden",
"from_html": "Desde un archivo HTML de marcadores",
"from_wallabag": "Desde Wallabag (archivo JSON)",
"pinned": "Anclado",
"pinned_links_desc": "Tus enlaces anclados",
"pin_favorite_links_here": "¡Ancla tus enlaces favoritos aquí!",
"pin_favorite_links_here_desc": "Puedes anclar tus enlaces favoritos haciendo clic en los tres puntos en cada enlace y seleccionando Anclar en el tablero.",
"sending_password_link": "Enviando enlace de recuperación de contraseña...",
"password_email_prompt": "Ingresa tu correo electrónico para que podamos enviarte un enlace para crear una nueva contraseña.",
"send_reset_link": "Enviar enlace de restablecimiento",
"reset_email_sent_desc": "Revisa tu correo electrónico para encontrar un enlace para restablecer tu contraseña. Si no aparece en unos minutos, revisa tu carpeta de spam.",
"back_to_login": "Volver al inicio de sesión",
"email_sent": "¡Email enviado!",
"passwords_mismatch": "Las contraseñas no coinciden.",
"password_too_short": "La contraseña deben tener al menos 8 caracteres.",
"creating_account": "Creando cuenta...",
"account_created": "¡Cuenta creada!",
"trial_offer_desc": "¡Desbloquea {{count}} días de servicio Premium gratis!",
"register_desc": "Crear una nueva cuenta",
"registration_disabled_desc": "El registro está deshabilitado para esta instancia. Por favor, contacta al administrador en caso de cualquier problema.",
"enter_details": "Ingresa tus datos",
"display_name": "Mostrar nombre",
"sign_up_agreement": "Al registrarte, aceptas nuestros <0>Términos de Servicio</0> y nuestra <1>Política de Privacidad</1>.",
"need_help": "¿Necesitas ayuda?",
"get_in_touch": "Contáctanos",
"already_registered": "¿Ya tienes una cuenta?",
"deleting_selections": "Eliminando seleccionados...",
"links_deleted": "{{count}} enlaces eliminados.",
"link_deleted": "1 enlace eliminado.",
"links_selected": "{{count}} enlaces seleccionados",
"link_selected": "1 enlace seleccionado",
"nothing_selected": "Nada seleccionado",
"edit": "Editar",
"delete": "Eliminar",
"nothing_found": "No se encontró nada.",
"redirecting_to_stripe": "Redirigiendo a Stripe...",
"subscribe_title": "¡Suscríbete a Linkwarden!",
"subscribe_desc": "Serás redirigido a Stripe. No dudes en ponerte en contacto con nosotros en <0>support@linkwarden.app</0> en caso de cualquier problema.",
"monthly": "Mensual",
"yearly": "Anual",
"discount_percent": "{{percent}}% de descuento",
"billed_monthly": "Facturado mensualmente",
"billed_yearly": "Facturado anualmente",
"total": "Total",
"total_annual_desc": "Prueba gratuita de {{count}} días, luego ${{annualPrice}} al año",
"total_monthly_desc": "Prueba gratuita de {{count}} días, luego ${{annualPrice}} al mes",
"plus_tax": "+ IVA si corresponde",
"complete_subscription": "Completar suscripción",
"sign_out": "Cerrar sesión",
"access_tokens": "Tokens de acceso",
"access_tokens_description": "Los tokens de acceso se pueden usar para acceder a Linkwarden desde otras aplicaciones y servicios sin revelar tu nombre de usuario y contraseña.",
"new_token": "Nuevo token de acceso",
"name": "Nombre",
"created_success": "¡Creado!",
"created": "Creado",
"expires": "Expira",
"accountSettings": "Ajustes de la cuenta",
"language": "Idioma",
"profile_photo": "Foto de perfil",
"upload_new_photo": "Sube una foto nueva...",
"remove_photo": "Eliminar foto",
"make_profile_private": "Hacer perfil privado",
"profile_privacy_info": "Esto limitará quién puede encontrarte y agregarte a nuevas colecciones.",
"whitelisted_users": "Usuarios autorizados",
"whitelisted_users_info": "Por favor, proporciona el nombre de usuario de los usuarios a los que deseas conceder visibilidad de tu perfil, separados por comas.",
"whitelisted_users_placeholder": "Tu perfil está oculto para todos en este momento...",
"save_changes": "Guardar cambios",
"import_export": "Importar y exportar",
"import_data": "Importa tus datos desde otras plataformas.",
"download_data": "Descarga tus datos al instante.",
"export_data": "Exportar datos",
"delete_account": "Eliminar cuenta",
"delete_account_warning": "Esto eliminará permanentemente TODOS los enlaces, colecciones, etiquetas y datos archivados en tu cuenta.",
"cancel_subscription_notice": "También cancelará tu suscripción.",
"account_deletion_page": "Página de eliminación de cuenta",
"applying_settings": "Aplicando configuraciones...",
"settings_applied":"¡Configuraciones aplicadas!",
"email_change_request": "Solicitud de cambio de correo electrónico enviado. Por favor, verifica el nuevo correo electrónico.",
"image_upload_no_file_error": "No se ha seleccionado ningún archivo. Por favor, elija una imagen para cargar.",
"image_upload_size_error": "Por favor, selecciona un archivo PNG o JPEG que sea menor de 1 MB.",
"image_upload_format_error": "Formato de archivo no válido.",
"importing_bookmarks": "Importando marcadores...",
"import_success": "¡Marcadores importados! Recargando la página...",
"more_coming_soon": "¡Más próximamente!",
"billing_settings": "Ajustes de facturación",
"manage_subscription_intro": "Para gestionar/cancelar tu suscripción, visita el",
"billing_portal": "Portal de facturación",
"help_contact_intro": "Si necesitas ayuda o has encontrado algún problema, no dudes en contactarnos en:",
"fill_required_fields": "Por favor, rellena los campos obligatorios.",
"deleting_message": "Eliminando todo, por favor espera...",
"delete_warning": "Esto eliminará permanentemente todos los enlaces, colecciones, etiquetas y datos archivados en tu cuenta. También cerrará la sesión. ¡Esta acción es irreversible!",
"optional": "Opcional",
"feedback_help": "(¡pero realmente nos ayuda a mejorar!)",
"reason_for_cancellation": "Motivos de la cancelación",
"please_specify": "Por favor, especifica",
"customer_service": "Servicio al usuario",
"low_quality": "Mala calidad",
"missing_features": "Le faltan utilidades",
"switched_service": "Cambio a otra plataforma",
"too_complex": "Muy complejo",
"too_expensive": "Muy caro",
"unused": "No lo uso",
"other": "Otro",
"more_information": "Más información (cuantos más detalles, más útil será)",
"feedback_placeholder": "ej. necesitaba una función que...",
"delete_your_account": "Elliminar tu cuenta",
"change_password": "Cambiar contraseña",
"password_length_error": "La contraseña debe tener al menos 8 caracteres.",
"applying_changes": "Aplicando...",
"password_change_instructions": "Para cambiar tu contraseña, por favor completa lo siguiente. Tu contraseña debe tener al menos 8 caracteres.",
"old_password": "Contraseña actual",
"new_password": "Nueva contraseña",
"preference": "Preferencias",
"select_theme": "Seleccionar tema",
"dark": "Oscuro",
"light": "Claro",
"archive_settings": "Ajustes de archivado",
"formats_to_archive": "Formatos para archivar/conservar páginas web:",
"screenshot": "Captura de pantalla",
"pdf": "PDF",
"archive_org_snapshot": "Captura en archive.org",
"link_settings": "Ajustes de enlaces",
"prevent_duplicate_links": "Prevenir enlaces duplicados",
"clicking_on_links_should": "Hacer click en enlaces debería:",
"open_original_content": "Abrir el contenido original",
"open_pdf_if_available": "Abrir PDF, si está disponible",
"open_readable_if_available": "Abrir versión lectura, si está disponible",
"open_screenshot_if_available": "Abrir la captura de pantalla, si está disponible",
"open_webpage_if_available": "Abrir copia local de la web, si está disponible",
"tag_renamed": "¡Etiqueta renombrada!",
"tag_deleted": "¡Etiqueta eliminada!",
"rename_tag": "Renombrar etiqueta",
"delete_tag": "Eliminar etiqueta",
"list_created_with_linkwarden": "Lista generada con Linkwarden",
"by_author": "Por {{author}}.",
"by_author_and_other": "Por {{author}} y {{count}} más.",
"by_author_and_others": "Por {{author}} y {{count}} más.",
"search_count_link": "Buscar {{count}} enlace",
"search_count_links": "Buscar {{count}} enlaces",
"collection_is_empty": "Esta colección está vacía...",
"all_links": "Todos los enlaces",
"all_links_desc": "Enlaces de todas las colecciones",
"you_have_not_added_any_links": "Aún no has creado ningún enlace",
"collections_you_own": "Colecciones que te pertenecen",
"new_collection": "Nueva colección",
"other_collections": "Otras colecciones",
"other_collections_desc": "Colecciones compartidas de las que eres miembro",
"showing_count_results": "Mostrando {{count}} resultados",
"showing_count_result": "Mostrando {{count}} resultado",
"edit_collection_info": "Editar información de la colección",
"share_and_collaborate": "Compartir y colaborar",
"view_team": "Ver grupo",
"team": "Grupo",
"create_subcollection": "Crear subcolección",
"delete_collection": "Eliminar colección",
"leave_collection": "Salir de la colección",
"email_verified_signing_out": "Correo electrónico verificado. Cerrando sesión...",
"invalid_token": "Token inválido.",
"sending_password_recovery_link": "Enviando enlace de recuperación de contraseña",
"please_fill_all_fields": "Por favor, rellena todos los campos.",
"password_updated": "¡Contraseña actualizada!",
"reset_password": "Restablecer contraseña",
"enter_email_for_new_password": "Ingresa tu correo electrónico para que podamos enviarte un enlace para crear una nueva contraseña.",
"update_password": "Actualizar contraseña",
"password_successfully_updated": "Tu contraseña ha sido actualizada correctamente.",
"user_already_member": "El usuario ya existe.",
"you_are_already_collection_owner": "Ya eres el dueño de esta colección.",
"date_newest_first": "Fecha (Nuevos primero)",
"date_oldest_first": "Fecha (Antiguos primero)",
"name_az": "Nombre (A-Z)",
"name_za": "Nombre (Z-A)",
"description_az": "Descripción (A-Z)",
"description_za": "Descripción (Z-A)",
"all_rights_reserved": "© {{date}} <0>Linkwarden</0>. Todos los derechos reservados.",
"you_have_no_collections": "No tienes ninguna colección...",
"you_have_no_tags": "No tienes ninguna etiqueta...",
"cant_change_collection_you_dont_own": "No puedes hacer cambios en una colección que no te pertenece.",
"account": "Cuenta",
"billing": "Facturación",
"linkwarden_version": "Linkwarden {{version}}",
"help": "Ayuda",
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"link_preservation_in_queue": "La conservación del enlace está actualmente en cola.",
"check_back_later": "Por favor, vuelve más tarde para ver el resultado.",
"there_are_more_formats": "Hay más formatos de conservación en cola.",
"settings": "Ajustes",
"switch_to": "Cambiar a {{theme}}",
"logout": "Cerrar sesión",
"start_journey": "¡Empieza tu experiencia creando un nuevo enlace!",
"create_new_link": "Crear un nuevo enlace",
"new_link": "Nuevo enlace",
"create_new": "Crear nuevo...",
"pwa_install_prompt": "Instala Linkwarden en la pantalla de inicio para un acceso más rápido y una experiencia mejorada. <0>Aprende más</0>",
"full_content": "Contenido completo",
"slower": "Más despacio",
"new_version_announcement": "¡Descubre las novedades en <0>Linkwarden {{version}}!</0>",
"creating": "Creando...",
"upload_file": "Subir archivo",
"file": "Archivo",
"file_types": "PDF, PNG, JPG (Hasta {{size}} MB)",
"description": "Descripción",
"auto_generated": "Será generado automáticamente si no se proporciona nada.",
"example_link": "ej. Enlace de ejemplo",
"hide": "Esconder",
"more": "s",
"options": "Opciones",
"description_placeholder": "Notas, ideas, etc.",
"deleting": "Eliminando...",
"token_revoked": "Token anulado.",
"revoke_token": "Anular token",
"revoke_confirmation": "¿Estás seguro de que deseas anular este token de acceso? Cualquier aplicación o servicio que use este token ya no podrá acceder a Linkwarden con él.",
"revoke": "Anular",
"sending_request": "Enviando solicitud...",
"link_being_archived": "El enlace está siendo archivado...",
"preserved_formats": "Formatos conservados",
"available_formats": "Los siguientes formatos están disponibles para este enlace",
"readable": "Versión lectura",
"preservation_in_queue": "La conservación del enlace está en la cola.",
"view_latest_snapshot": "Ver la última captura en archive.org",
"refresh_preserved_formats": "Actualizar formatos conservados",
"this_deletes_current_preservations": "Esto elimina las conservaciones actuales.",
"create_new_user": "Crear nuevo usuario",
"placeholder_johnny": "Johnny",
"placeholder_email": "johnny@ejemplo.com",
"placeholder_john": "john",
"user_created": "¡Usuario creado!",
"fill_all_fields_error": "Por favor, rellena todos los campos.",
"password_change_note": "<0>Nota:</0> Por favor, asegúrate de informar al usuario que necesita cambiar su contraseña.",
"create_user": "Crear usuario",
"creating_token": "Creando token...",
"token_created": "¡Token creado!",
"access_token_created": "Token de acceso creado",
"token_creation_notice": "Tu nuevo token ha sido creado. Por favor, cópialo y guárdalo en un lugar seguro. No lo mostraremos de nuevo.",
"copied_to_clipboard": "¡Copiado al portapapeles!",
"copy_to_clipboard": "Copiar al portapapeles",
"create_access_token": "Crear un token de acceso",
"expires_in": "Caduca en",
"token_name_placeholder": "ej. Para el atajo de iOS",
"create_token": "Crear token de acceso",
"7_days": "7 días",
"30_days": "30 días",
"60_days": "60 días",
"90_days": "90 días",
"no_expiration": "Sin caducidad",
"creating_link": "Creando enlace...",
"link_created": "¡Enlace creado!",
"link_name_placeholder": "Se generará automáticamente si se deja en blanco.",
"link_url_placeholder": "ej. http://ejemplo.com/",
"link_description_placeholder": "Notas, ideas, etc.",
"more_options": "Más opciones",
"hide_options": "Ocultar opciones",
"create_link": "Crear enlace",
"new_sub_collection": "Nueva subcolección",
"for_collection": "Para {{name}}",
"create_new_collection": "Crear una nueva colección",
"color": "Color",
"reset": "Reestablecer",
"updating_collection": "Actualizando colección...",
"collection_name_placeholder": "ej. Colección de ejemplo",
"collection_description_placeholder": "El propósito de esta colección...",
"create_collection_button": "Crear colección",
"password_change_warning": "Por favor, confirma tu contraseña antes de cambiar tu email.",
"stripe_update_note": " Actualizar este campo cambiará también tu email de facturación en Stripe.",
"sso_will_be_removed_warning": "Si cambias tu correo electrónico, cualquier conexión SSO existente de {{service}} será eliminada.",
"old_email": "Correo electrónico actual",
"new_email": "Correo electrónico nuevo",
"confirm": "Confirmar",
"edit_link": "Editar enlace",
"updating": "Actualizando...",
"updated": "¡Actualizado!",
"placeholder_example_link": "Ej. Enlace de ejemplo",
"make_collection_public": "Hacer pública la colección",
"make_collection_public_checkbox": "Hacer pública esta colección",
"make_collection_public_desc": "Esto permitirá que cualquier persona vea esta colección y sus usuarios.",
"sharable_link_guide": "Enlace para compartir (haz clic para copiar)",
"copied": "¡Copiado!",
"members": "Participantes",
"members_username_placeholder": "Nombre de usuario (sin la '@')",
"owner": "Propietario",
"admin": "Administrador",
"contributor": "Colaborador",
"viewer": "Lector",
"viewer_desc": "Acceso de solo lectura",
"contributor_desc": "Puede ver y crear enlaces",
"admin_desc": "Acceso completo a todos los enlaces",
"remove_member": "Eliminar participante",
"placeholder_example_collection": "Ej. colección de ejemplo",
"placeholder_collection_purpose": "El propósito de esta colección...",
"deleting_user": "Eliminando...",
"user_deleted": "Usuario eliminado.",
"delete_user": "Eliminar usuario",
"confirm_user_deletion": "¿Estás seguro de que quieres eliminar este usuario?",
"irreversible_action_warning": "¡Esta acción es irreversible!",
"delete_confirmation": "Eliminar, sé lo que estoy haciendo",
"delete_link": "Eliminar enlace",
"deleted": "Eliminado.",
"link_deletion_confirmation_message": "¿Estás seguro de que quieres eliminar este enlace?",
"warning": "Advertencia",
"irreversible_warning": "¡Esta acción es irreversible!",
"shift_key_tip": "Mantén presionada la tecla 'Mayúscula' mientras haces clic en 'Eliminar' para omitir esta confirmación en el futuro.",
"deleting_collection": "Eliminando...",
"collection_deleted": "Colección eliminada.",
"confirm_deletion_prompt": "Para confirmar, escribe \"{{name}}\" en el campo a continuación:",
"type_name_placeholder": "Escribe \"{{name}}\" aquí.",
"deletion_warning": "Eliminar esta colección borrará permanentemente todo su contenido, y se volverá inaccesible para todos, incluidos los miembros con acceso previamente.",
"leave_prompt": "Haz clic en el botón de abajo para abandonar la colección actual.",
"leave": "Abandonar",
"edit_links": "Editar {{count}} enlaces",
"move_to_collection": "Mover a colección",
"add_tags": "Añadir etiquetas",
"remove_previous_tags": "Eliminar etiquetas anteriores",
"delete_links": "Eliminar {{count}} enlaces",
"links_deletion_confirmation_message": "¿Estás seguro de que deseas eliminar {{count}} enlaces? ",
"warning_irreversible": "Advertencia: ¡Esta acción es irreversible!",
"shift_key_instruction": "Mantén presionada la tecla 'Mayúscula' mientras haces clic en 'Eliminar' para omitir esta confirmación en el futuro.",
"link_selection_error": "No tienes permiso para editar o eliminar este item.",
"no_description": "No se ha proporcionado una descripción.",
"applying": "Aplicando...",
"unpin": "Desanclar",
"pin_to_dashboard": "Anclar al tablero",
"show_link_details": "Mostrar detalles del enlace",
"hide_link_details": "Ocultar detalles del enlace",
"link_pinned": "¡Enlace anclado!",
"link_unpinned": "¡Enlace desanclado!",
"webpage": "Página web",
"server_administration": "Administración del servidor",
"all_collections": "Todas las colecciones",
"dashboard": "Tablero",
"demo_title": "Solo para demostración",
"demo_desc": "Esta es solo una instancia de demostración de Linkwarden y las cargas están deshabilitadas.",
"demo_desc_2": "Si deseas probar la versión completa, puedes registrarte para una prueba gratuita en:",
"demo_button": "Iniciar sesión como usuario demo",
"regular": "Regular",
"thin": "Fino",
"bold": "Grueso",
"fill": "Rellenar",
"duotone": "Duotono",
"light_icon": "Claro",
"search": "Buscar",
"set_custom_icon": "Establecer icono personalizado",
"view": "Vista",
"show": "Mostrar",
"image": "Imagen",
"icon": "Icono",
"date": "Fecha",
"preview_unavailable": "Vista previa no disponible",
"saved": "Guardado",
"untitled": "Sin título",
"no_tags": "Sin etiquetas.",
"no_description_provided": "No hay descripción proporcionada.",
"change_icon": "Cambiar icono",
"upload_preview_image": "Cargar imagen de vista previa",
"columns": "Columnas",
"default": "Por defecto",
"invalid_url_guide":"Introduzca una dirección válida para el enlace. (Debe empezar por http/https)",
"email_invalid": "Introduzca una dirección de correo electrónico válida.",
"username_invalid_guide": "El nombre de usuario debe tener al menos 3 caracteres, sin espacios ni caracteres especiales.",
"team_management": "Gestión de equipos",
"invite_user": "Invitar usuario",
"invite_users": "Invitar usuarios",
"invite_user_desc": "Para invitar a alguien a su equipo, introduzca su dirección de correo electrónico a continuación:",
"invite_user_note": "Tenga en cuenta que, una vez aceptada la invitación, se adquirirá una plaza adicional y se facturará automáticamente a su cuenta.",
"invite_user_price": "El coste de cada plaza es de ${{price}} al mes o ${{priceAnnual}} al año, dependiendo de su plan de suscripción actual.",
"send_invitation": "Enviar invitación",
"learn_more": "Más información",
"invitation_desc": "{{owner}} le ha invitado a unirse a Linkwarden. \nPara continuar, termine de configurar su cuenta.",
"invitation_accepted": "¡Invitación Aceptada!",
"status": "Estado",
"pending": "Pendiente",
"active": "Activo",
"manage_seats": "Gestionar plazas",
"seats_purchased": "{{count}} plazas adquiridas",
"seat_purchased": "{{count}} asiento comprado",
"date_added": "Fecha de creación",
"resend_invite": "Reenviar invitación",
"resend_invite_success": "¡Invitación reenviada!",
"remove_user": "Eliminar usuario",
"continue_to_dashboard": "Continuar al Tablero",
"confirm_user_removal_desc": "Deberán tener una suscripción para acceder de nuevo a Linkwarden.",
"click_out_to_apply": "Haga clic fuera para aplicar",
"submit": "Enviar",
"thanks_for_feedback": "¡Gracias por sus comentarios!",
"quick_survey": "Encuesta rápida",
"how_did_you_discover_linkwarden": "¿Cómo ha descubierto Linkwarden?",
"rather_not_say": "Mejor no decir",
"search_engine": "Motor de búsqueda (Google, Bing, etc.)",
"reddit": "Reddit",
"lemmy": "Lemmy",
"people_recommendation": "Recomendación (Amigo, Familia, etc.)",
"open_all_links": "Abrir todos los enlaces"
}

View File

@@ -3,29 +3,29 @@
"search_users": "Rechercher des utilisateurs",
"no_users_found": "Aucun utilisateur trouvé.",
"no_user_found_in_search": "Aucun utilisateur trouvé avec la requête de recherche donnée.",
"username": "Nom d'utilisateur",
"username": "Nom dutilisateur",
"email": "E-mail",
"subscribed": "Abonné(e)",
"created_at": "Créé le",
"not_available": "S/O",
"check_your_email": "Veuillez vérifier votre adresse e-mail",
"authenticating": "Authentification...",
"verification_email_sent": "Envoi de l'email de vérification.",
"verification_email_sent_desc": "Un lien de connexion a été envoyé à votre adresse électronique. Si vous ne voyez pas l'e-mail, vérifiez votre dossier de courrier indésirable.",
"resend_email": "Renvoyez l'e-mail",
"invalid_credentials": "Informations d'identification invalides.",
"verification_email_sent": "Envoi de lemail de vérification.",
"verification_email_sent_desc": "Un lien de connexion a été envoyé à votre adresse électronique. Si vous ne voyez pas le-mail, vérifiez votre dossier de courrier indésirable.",
"resend_email": "Renvoyer le-mail",
"invalid_credentials": "Informations didentification invalides.",
"fill_all_fields": "Veuillez remplir tous les champs.",
"enter_credentials": "Entrez vos données d'identification",
"username_or_email": "Nom d'utilisateur ou e-mail",
"enter_credentials": "Entrez vos données didentification",
"username_or_email": "Nom dutilisateur ou e-mail",
"password": "Mot de passe",
"confirm_password": "Confirmer le mot de passe",
"forgot_password": "Mot de passe oublié ?",
"login": "Se connecter",
"or_continue_with": "Ou continuer avec",
"new_here": "Vous êtes nouveau ?",
"sign_up": "S'inscrire",
"new_here": "Vous êtes nouvelle/nouveau ?",
"sign_up": "Sinscrire",
"sign_in_to_your_account": "Connectez-vous à votre compte",
"dashboard_desc": "Un bref aperçu de vos données",
"dashboard_desc": "Aperçu rapide de vos données",
"link": "Lien",
"links": "Liens",
"collection": "Collection",
@@ -40,59 +40,59 @@
"add_link": "Ajouter un nouveau lien",
"import_links": "Importer des liens",
"from_linkwarden": "De Linkwarden",
"from_html": "À partir d'un fichier HTML de signets",
"from_html": "À partir dun fichier HTML de signets",
"from_wallabag": "À partir de Wallabag (fichier JSON)",
"pinned": "Épinglé",
"pinned": "Épinglés",
"pinned_links_desc": "Vos liens épinglés",
"pin_favorite_links_here": "Épinglez vos liens favoris ici !",
"pin_favorite_links_here_desc": "Vous pouvez épingler vos liens préférés en cliquant sur les trois points sur chaque lien et en cliquant sur Épingler au tableau de bord.",
"sending_password_link": "Envoi du lien de récupération du mot de passe...",
"password_email_prompt": "Entrez votre e-mail afin que nous puissions vous envoyer un lien pour créer un nouveau mot de passe.",
"send_reset_link": "Envoyer le lien de réinitialisation",
"reset_email_sent_desc": "Vérifiez votre e-mail pour trouver un lien permettant de réinitialiser votre mot de passe. S'il n'apparaît pas au bout de quelques minutes, vérifiez votre dossier spam.",
"reset_email_sent_desc": "Vérifiez votre e-mail pour trouver un lien permettant de réinitialiser votre mot de passe. Sil napparaît pas au bout de quelques minutes, vérifiez votre dossier spam.",
"back_to_login": "Retour à la page de connexion",
"email_sent": "Email envoyé!",
"passwords_mismatch": "Les mots de passe ne correspondent pas.",
"password_too_short": "Les mots de passe doivent comporter au moins 8 caractères.",
"creating_account": "Création d'un compte...",
"account_created": "Compte créé!",
"creating_account": "Création dun compte...",
"account_created": "Compte créé !",
"trial_offer_desc": "Débloquez {{count}} jours de service Premium sans frais !",
"register_desc": "Créer un nouveau compte",
"registration_disabled_desc": "L'inscription est désactivée pour cette instance, veuillez contacter l'administrateur en cas de problème.",
"registration_disabled_desc": "Linscription est désactivée pour cette instance, veuillez contacter ladministrateur en cas de problème.",
"enter_details": "Entrez vos coordonnées",
"display_name": "Nom affiché",
"sign_up_agreement": "En vous inscrivant, vous acceptez nos <0>Conditions d'utilisation</0> et notre <1>Politique de confidentialité</1>.",
"need_help": "Besoin d'aide?",
"sign_up_agreement": "En vous inscrivant, vous acceptez nos <0>Conditions dutilisation</0> et notre <1>Politique de confidentialité</1>.",
"need_help": "Besoin daide ?",
"get_in_touch": "Prenez contact avec nous",
"already_registered": "Vous avez déjà un compte?",
"already_registered": "Vous avez déjà un compte ?",
"deleting_selections": "Suppression des sélections...",
"links_deleted": "{{count}} Liens supprimés.",
"link_deleted": "1 Lien supprimé.",
"links_selected": "{{count}} Liens sélectionnés",
"link_selected": "1 Lien sélectionné",
"links_deleted": "{{count}} liens supprimés.",
"link_deleted": "1 lien supprimé.",
"links_selected": "{{count}} liens sélectionnés",
"link_selected": "1 lien sélectionné",
"nothing_selected": "Rien de sélectionné",
"edit": "Modifier",
"delete": "Supprimer",
"nothing_found": "Rien n'a été trouvé.",
"nothing_found": "Rien na été trouvé.",
"redirecting_to_stripe": "Redirection vers Stripe...",
"subscribe_title": "Abonnez-vous à Linkwarden!",
"subscribe_desc": "Vous serez redirigé vers Stripe, n'hésitez pas à nous contacter à <0>support@linkwarden.app</0> en cas de problème.",
"subscribe_title": "Abonnez-vous à Linkwarden !",
"subscribe_desc": "Vous serez redirigé vers Stripe, nhésitez pas à nous contacter à <0>support@linkwarden.app</0> en cas de problème.",
"monthly": "Mensuel",
"yearly": "Annuel",
"discount_percent": "{{percent}}% de réduction",
"billed_monthly": "Facturé mensuellement",
"billed_yearly": "Facturé annuellement",
"total": "Total",
"total_annual_desc": "{{count}}-jour d'essai gratuit, puis ${{annualPrice}} annuellement",
"total_monthly_desc": "{{count}}-jour d'essai gratuit, puis ${{monthlyPrice}} par mois",
"total_annual_desc": "{{count}}-jour dessai gratuit, puis ${{annualPrice}} annuellement",
"total_monthly_desc": "{{count}}-jour dessai gratuit, puis ${{monthlyPrice}} par mois",
"plus_tax": "+ TVA si applicable",
"complete_subscription": "Abonnement complet",
"sign_out": "Se déconnecter",
"access_tokens": "Jetons d'accès",
"access_tokens_description": "Les jetons d'accès peuvent être utilisés pour accéder à Linkwarden à partir d'autres applications et services sans divulguer votre nom d'utilisateur et votre mot de passe.",
"new_token": "Nouveau jeton d'accès",
"access_tokens": "Jetons daccès",
"access_tokens_description": "Les jetons daccès peuvent être utilisés pour accéder à Linkwarden à partir dautres applications et services sans divulguer votre nom dutilisateur et votre mot de passe.",
"new_token": "Nouveau jeton daccès",
"name": "Nom",
"created_success": "Créé!",
"created_success": "Créé !",
"created": "Créé",
"expires": "Expire",
"accountSettings": "Paramètres du compte",
@@ -103,11 +103,11 @@
"make_profile_private": "Rendre le profil privé",
"profile_privacy_info": "Cela limitera le nombre de personnes pouvant vous trouver et vous ajouter à de nouvelles collections..",
"whitelisted_users": "Utilisateurs sur liste blanche",
"whitelisted_users_info": "Veuillez fournir le nom d'utilisateur des utilisateurs auxquels vous souhaitez accorder de la visibilité à votre profil. Séparé par une virgule.",
"whitelisted_users_info": "Veuillez fournir le nom dutilisateur des personnes auxquelles vous souhaitez accorder la visibilité de votre profil. Séparés par une virgule.",
"whitelisted_users_placeholder": "Votre profil est actuellement caché à tout le monde...",
"save_changes": "Sauvegarder les modifications",
"import_export": "Importer & Exporter",
"import_data": "Importez vos données depuis d'autres plateformes.",
"import_data": "Importez vos données depuis dautres plateformes.",
"download_data": "Téléchargez vos données instantanément.",
"export_data": "Exporter des données",
"delete_account": "Supprimer le compte",
@@ -117,32 +117,33 @@
"applying_settings": "Application des paramètres...",
"settings_applied": "Paramètres appliqués !",
"email_change_request": "Demande de modification par e-mail envoyée. Veuillez vérifier la nouvelle adresse e-mail.",
"image_upload_no_file_error": "Aucun fichier sélectionné, veuillez choisir une image à téléverser",
"image_upload_size_error": "Veuillez sélectionner un fichier PNG ou JPEG de moins de 1 Mo.",
"image_upload_format_error": "Format de fichier invalide.",
"importing_bookmarks": "Importer des favoris...",
"import_success": "Importation des signets ! Rechargement de la page...",
"more_coming_soon": "Plus à venir bientôt!",
"more_coming_soon": "Plus à venir bientôt !",
"billing_settings": "Paramètres de facturation",
"manage_subscription_intro": "Pour gérer/annuler votre abonnement, visitez le",
"billing_portal": "Portail de facturation",
"help_contact_intro": "Si vous avez encore besoin d'aide ou rencontrez des problèmes, n'hésitez pas à nous contacter à l'adresse suivante :",
"help_contact_intro": "Si vous avez encore besoin daide ou rencontrez des problèmes, nhésitez pas à nous contacter à ladresse suivante :",
"fill_required_fields": "Veuillez remplir les champs requis.",
"deleting_message": "Tout supprimer, veuillez patienter...",
"deleting_message": "Suppresion totale en cours, veuillez patienter...",
"delete_warning": "Cela supprimera définitivement tous les liens, collections, balises et données archivées que vous possédez. Cela vous déconnectera également. Cette action est irréversible !",
"optional": "Facultatif",
"feedback_help": "(mais ça nous aide vraiment à nous améliorer !)",
"reason_for_cancellation": "Raison pour l'annulation",
"reason_for_cancellation": "Raison de lannulation",
"please_specify": "Veuillez préciser",
"customer_service": "Service client",
"low_quality": "Qualité médiocre",
"missing_features": "Fonctionnalités manquantes",
"switched_service": "Service commuté",
"switched_service": "Changement de plateforme",
"too_complex": "Trop compliqué",
"too_expensive": "Trop cher",
"unused": "Inutilisé",
"unused": "Pas lutilité",
"other": "Autre",
"more_information": "Plus d'informations (plus il y a de détails, plus cela serait utile)",
"feedback_placeholder": "Par exemple: J'avais besoin d'une fonctionnalité qui...",
"more_information": "Plus dinformations (nhésitez pas à détailler pour nous aider à mieux comprendre)",
"feedback_placeholder": "Par exemple: Javais besoin dune fonctionnalité qui...",
"delete_your_account": "Supprimer votre compte",
"change_password": "Changer le mot de passe",
"password_length_error": "Les mots de passe doivent comporter au moins 8 caractères.",
@@ -154,33 +155,33 @@
"select_theme": "Sélectionner un thème",
"dark": "Sombre",
"light": "Clair",
"archive_settings": "Paramètres d'archivage",
"archive_settings": "Paramètres darchivage",
"formats_to_archive": "Formats pour archiver/préserver les pages Web:",
"screenshot": "Capture d'écran",
"screenshot": "Capture décran",
"pdf": "PDF",
"archive_org_snapshot": "Instantané d'Archive.org",
"archive_org_snapshot": "Instantané dArchive.org",
"link_settings": "Paramètres du lien",
"prevent_duplicate_links": "Empêcher les liens en double",
"clicking_on_links_should": "Cliquer sur les liens devrait:",
"open_original_content": "Ouvrir le contenu original",
"open_pdf_if_available": "Ouvrir le PDF, si disponible",
"open_readable_if_available": "Ouvrir Readable, si disponible",
"open_screenshot_if_available": "Ouvrez la capture d'écran, si disponible",
"open_screenshot_if_available": "Ouvrez la capture décran, si disponible",
"open_webpage_if_available": "Ouvrir la copie de la page Web, si disponible",
"tag_renamed": "L'étiquette a été renommée !",
"tag_deleted": "Étiquette supprimée!",
"rename_tag": "Renommer l'étiquette",
"delete_tag": "Supprimer l'étiquette",
"tag_renamed": "Létiquette a été renommée !",
"tag_deleted": "Étiquette supprimée !",
"rename_tag": "Renommer létiquette",
"delete_tag": "Supprimer létiquette",
"list_created_with_linkwarden": "Liste créée avec Linkwarden",
"by_author": "Par {{author}}.",
"by_author_and_other": "Par {{author}} et {{count}} autre.",
"by_author_and_others": "Par {{author}} et {{count}} autres.",
"search_count_link": "Recherche {{count}} Lien",
"search_count_links": "Recherche {{count}} Liens",
"search_count_link": "Recherche {{count}} lien",
"search_count_links": "Recherche {{count}} liens",
"collection_is_empty": "Cette collection est vide...",
"all_links": "Tous les liens",
"all_links_desc": "Liens de chaque collection",
"you_have_not_added_any_links": "Vous n'avez pas encore créé de liens",
"you_have_not_added_any_links": "Vous navez pas encore créé de liens",
"collections_you_own": "Collections que vous possédez",
"new_collection": "Nouvelle collection",
"other_collections": "Autres collections",
@@ -189,8 +190,8 @@
"showing_count_result": "Afficher {{count}} résultat",
"edit_collection_info": "Modifier les informations sur la collection",
"share_and_collaborate": "Partager et collaborer",
"view_team": "Voir l'équipe",
"team": "L'équipe",
"view_team": "Voir léquipe",
"team": "Léquipe",
"create_subcollection": "Créer une sous-collection",
"delete_collection": "Supprimer la collection",
"leave_collection": "Quitter la collection",
@@ -198,12 +199,12 @@
"invalid_token": "Jeton non valide.",
"sending_password_recovery_link": "Envoi du lien de récupération du mot de passe...",
"please_fill_all_fields": "Veuillez remplir tous les champs.",
"password_updated": "Mot de passe mis à jour!",
"password_updated": "Mot de passe mis à jour !",
"reset_password": "Réinitialiser le mot de passe",
"enter_email_for_new_password": "Saisissez votre adresse électronique afin que nous puissions vous envoyer un lien pour créer un nouveau mot de passe.",
"update_password": "Mise à jour du mot de passe",
"password_successfully_updated": "Votre mot de passe a été mis à jour avec succès.",
"user_already_member": "L'utilisateur existe déjà.",
"user_already_member": "Lutilisateur existe déjà.",
"you_are_already_collection_owner": "Vous êtes déjà le propriétaire de la collection.",
"date_newest_first": "Date (la plus récente en premier)",
"date_oldest_first": "Date (la plus ancienne en premier)",
@@ -212,9 +213,9 @@
"description_az": "Description (A-Z)",
"description_za": "Description (Z-A)",
"all_rights_reserved": "© {{date}} <0>Linkwarden</0>. Tous droits réservés.",
"you_have_no_collections": "Vous n'avez pas de Collections...",
"you_have_no_collections": "Vous navez pas de collections...",
"you_have_no_tags": "Vous navez pas détiquettes...",
"cant_change_collection_you_dont_own": "Vous ne pouvez pas modifier une collection dont vous n'êtes pas propriétaire.",
"cant_change_collection_you_dont_own": "Vous ne pouvez pas modifier une collection dont vous nêtes pas le propriétaire.",
"account": "Compte",
"billing": "Facturation",
"linkwarden_version": "Linkwarden {{version}}",
@@ -222,26 +223,26 @@
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"link_preservation_in_queue": "La préservation du lien est actuellement dans la file d'attente",
"link_preservation_in_queue": "La préservation du lien est actuellement dans la file dattente",
"check_back_later": "Revenez plus tard pour voir le résultat",
"there_are_more_formats": "Il y a plus de formats conservés dans la file d'attente",
"there_are_more_formats": "Il y a dautres formats conservés dans la file dattente",
"settings": "Paramètres",
"switch_to": "Basculer vers {{theme}}",
"logout": "Déconnexion",
"start_journey": "Commencez votre voyage en créant un nouveau lien!",
"start_journey": "Démarrer lexpérience en créant un nouveau lien !",
"create_new_link": "Créer un nouveau lien",
"new_link": "Nouveau lien",
"create_new": "Créer nouveau...",
"pwa_install_prompt": "Installez Linkwarden sur votre écran d'accueil pour un accès plus rapide et une expérience améliorée. <0>En savoir plus</0>",
"pwa_install_prompt": "Installez Linkwarden sur votre écran daccueil pour un accès plus rapide et une expérience améliorée. <0>En savoir plus</0>",
"full_content": "Contenu complet",
"slower": "Plus lent",
"new_version_announcement": "Voir ce qu'il y a de nouveau dans <0>Linkwarden {{version}}!</0>",
"new_version_announcement": "Voir ce quil y a de nouveau dans <0>Linkwarden {{version}}!</0>",
"creating": "Création...",
"upload_file": "Envoyer fichier",
"file": "Ficher",
"file_types": "PDF, PNG, JPG (Jusqu'à {{size}} MB)",
"file_types": "PDF, PNG, JPG (Jusquà {{size}} MB)",
"description": "Description",
"auto_generated": "Sera généré automatiquement si rien n'est fourni.",
"auto_generated": "Sera généré automatiquement si rien nest fourni.",
"example_link": "Par exemple: Exemple de lien",
"hide": "Cacher",
"more": "Plus",
@@ -250,46 +251,46 @@
"deleting": "Suppression...",
"token_revoked": "Jeton révoqué.",
"revoke_token": "Révoquer le jeton",
"revoke_confirmation": "Êtes-vous sûr de vouloir révoquer ce jeton d'accès ? Les applications ou services utilisant ce jeton ne pourront plus accéder à Linkwarden en l'utilisant.",
"revoke_confirmation": "Êtes-vous sûr de vouloir révoquer ce jeton daccès ? Les applications ou services utilisant ce jeton ne pourront plus lexploiter pour accéder à Linkwarden.",
"revoke": "Révoquer",
"sending_request": "Envoi de la demande...",
"link_being_archived": "Le lien est en cours d'archivage...",
"link_being_archived": "Le lien est en cours darchivage...",
"preserved_formats": "Formats conservés",
"available_formats": "Les formats suivants sont disponibles pour ce lien",
"readable": "Lisible",
"preservation_in_queue": "La préservation du lien est dans la file d'attente",
"view_latest_snapshot": "Voir le dernier instantané sur archive.org",
"refresh_preserved_formats": "Rafraîchir les formats conservés",
"preservation_in_queue": "La préservation du lien est en file dattente",
"view_latest_snapshot": "Voir le dernier instantané sur Archive.org",
"refresh_preserved_formats": "Réactualiser les formats conservés",
"this_deletes_current_preservations": "Cette opération supprime les conservations en cours",
"create_new_user": "Créer un nouvel utilisateur",
"placeholder_johnny": "Johnny",
"placeholder_email": "johnny@exemple.com",
"placeholder_john": "john",
"user_created": "Utilisateur créé!",
"user_created": "Utilisateur créé !",
"fill_all_fields_error": "Veuillez remplir tous les champs.",
"password_change_note": "<0>Note:</0> Veillez à informer l'utilisateur qu'il doit modifier son mot de passe..",
"password_change_note": "<0>Note:</0> Veillez à informer lutilisateur quil doit modifier son mot de passe..",
"create_user": "Créer un utilisateur",
"creating_token": "Création d'un jeton...",
"token_created": "Jeton créé!",
"access_token_created": "Jeton d'accès créé",
"token_creation_notice": "Votre nouveau jeton a été créé. Veuillez le copier et le conserver en lieu sûr. Vous ne pourrez plus le voir.",
"creating_token": "Création dun jeton...",
"token_created": "Jeton créé !",
"access_token_created": "Jeton daccès créé",
"token_creation_notice": "Votre nouveau jeton a été créé. Veuillez le copier et le conserver en lieu sûr. Vous ne pourrez plus le consulter par la suite.",
"copied_to_clipboard": "Copié dans le presse-papiers !",
"copy_to_clipboard": "Copier dans le presse-papiers",
"create_access_token": "Créer un jeton d'accès",
"create_access_token": "Créer un jeton daccès",
"expires_in": "Expire dans",
"token_name_placeholder": "Par exemple: Pour le raccourci iOS",
"create_token": "Créer un jeton d'accès",
"create_token": "Créer un jeton daccès",
"7_days": "7 jous",
"30_days": "30 jours",
"60_days": "60 jours",
"90_days": "90 jous",
"no_expiration": "Pas d'expiration",
"creating_link": "Création d'un lien...",
"link_created": "Lien créé!",
"link_name_placeholder": "Sera généré automatiquement s'il n'est pas renseigné.",
"link_url_placeholder": "Par exemple: http://exemple.com/",
"no_expiration": "Pas dexpiration",
"creating_link": "Création dun lien...",
"link_created": "Lien créé !",
"link_name_placeholder": "Sera généré automatiquement sil nest pas renseigné.",
"link_url_placeholder": "Par exemple : http://exemple.com/",
"link_description_placeholder": "Notes, réflexions, etc.",
"more_options": "Plus d'options",
"more_options": "Plus doptions",
"hide_options": "Cacher les options",
"create_link": "Créer un lien",
"new_sub_collection": "Nouvelle sous-collection",
@@ -297,26 +298,27 @@
"create_new_collection": "Créer une nouvelle collection",
"color": "Couleur",
"reset": "Réinitialiser",
"collection_name_placeholder": "Par exemple: Exemple de collection",
"collection_description_placeholder": "L'objectif de cette collection...",
"updating_collection": "Mise à jour de la collection...",
"collection_name_placeholder": "Par exemple : Exemple de collection",
"collection_description_placeholder": "Lobjectif de cette collection...",
"create_collection_button": "Créer une collection",
"password_change_warning": "Veuillez confirmer votre mot de passe avant de modifier votre adresse électronique.",
"stripe_update_note": " La mise à jour de ce champ modifiera également votre email de facturation sur Stripe.",
"sso_will_be_removed_warning": "Si vous changez d'adresse électronique, toutes les connexions SSO {{service}} existantes seront supprimées.",
"sso_will_be_removed_warning": "Si vous changez dadresse électronique, toutes les connexions SSO {{service}} existantes seront supprimées.",
"old_email": "Ancien e-mail",
"new_email": "Nouvel e-mail",
"confirm": "Confirmer",
"edit_link": "Modifier le lien",
"updating": "Mise à jour...",
"updated": "Mis à jour!",
"placeholder_example_link": "Par exemple: Lien d'exemple",
"placeholder_example_link": "Par exemple: Lien dexemple",
"make_collection_public": "Rendre la collection publique",
"make_collection_public_checkbox": "Faire de cette collection une collection publique",
"make_collection_public_desc": "Cela permettra à n'importe qui de consulter cette collection et ses utilisateurs.",
"make_collection_public_desc": "Cela permettra à nimporte qui de consulter cette collection et ses utilisateurs.",
"sharable_link_guide": "Lien partageable (Cliquez pour copier)",
"copied": "Copié!",
"members": "Membres",
"members_username_placeholder": "Nom d'utilisateur (sans le '@')",
"members_username_placeholder": "Nom dutilisateur (sans le @)",
"owner": "Propriétaire",
"admin": "Administrateur",
"contributor": "Contributeur",
@@ -326,10 +328,10 @@
"admin_desc": "Accès complet à tous les liens",
"remove_member": "Supprimer le membre",
"placeholder_example_collection": "Par exemple: Exemple de collection",
"placeholder_collection_purpose": "L'objectif de cette collection...",
"placeholder_collection_purpose": "Lobjectif de cette collection...",
"deleting_user": "Suppression...",
"user_deleted": "Utilisateur supprimé.",
"delete_user": "Supprimer l'utilisateur",
"delete_user": "Supprimer lutilisateur",
"confirm_user_deletion": "Êtes-vous sûr de vouloir supprimer cet utilisateur ?",
"irreversible_action_warning": "Cette action est irréversible !",
"delete_confirmation": "Supprimer, je sais ce que je fais",
@@ -338,7 +340,7 @@
"link_deletion_confirmation_message": "Êtes-vous sûr de vouloir supprimer ce lien ?",
"warning": "Attention",
"irreversible_warning": "Cette action est irréversible !",
"shift_key_tip": "Maintenez la touche Majuscule enfoncée tout en cliquant sur « Supprimer » pour éviter cette confirmation à l'avenir.",
"shift_key_tip": "Maintenez la touche Majuscule enfoncée tout en cliquant sur « Supprimer » pour éviter cette confirmation à lavenir.",
"deleting_collection": "Suppression...",
"collection_deleted": "Collection supprimée.",
"confirm_deletion_prompt": "Pour confirmer, tapez \"{{name}}\" dans le champ ci-dessous:",
@@ -353,14 +355,13 @@
"delete_links": "Supprimer {{count}} liens",
"links_deletion_confirmation_message": "Êtes-vous sûr de vouloir supprimer {{count}} liens ? ",
"warning_irreversible": "Avertissement : Cette action est irréversible !",
"shift_key_instruction": "Maintenez la touche « Shift » enfoncée tout en cliquant sur « Supprimer » pour éviter cette confirmation à l'avenir.",
"link_selection_error": "Vous n'avez pas l'autorisation de modifier ou de supprimer cet élément.",
"no_description": "Aucune description n'est fournie.",
"shift_key_instruction": "Maintenez la touche « Shift » enfoncée tout en cliquant sur « Supprimer » pour éviter cette confirmation à lavenir.",
"link_selection_error": "Vous navez pas lautorisation de modifier ou de supprimer cet élément.",
"no_description": "Aucune description nest fournie.",
"applying": "Appliquer...",
"unpin": "Épingler",
"pin_to_dashboard": "Épingler au tableau de bord",
"show_link_details": "Afficher les détails du lien",
"hide_link_details": "Cacher les détails du lien",
"link_pinned": "Lien épinglé !",
"link_unpinned": "Lien désépinglé !",
"webpage": "Page web",
@@ -368,7 +369,65 @@
"all_collections": "Toutes les collections",
"dashboard": "Tableau de bord",
"demo_title": "Démonstration uniquement",
"demo_desc": "Il s'agit d'une instance de démonstration de Linkwarden et les téléchargements sont désactivés.",
"demo_desc_2": "Si vous souhaitez tester la version complète, vous pouvez vous inscrire pour un essai gratuit à l'adresse suivante:",
"demo_button": "Se connecter en tant qu'utilisateur de démonstration"
"demo_desc": "Il sagit dune instance de démonstration de Linkwarden, de ce fait les téléversements sont désactivés.",
"demo_desc_2": "Si vous souhaitez tester la version complète, vous pouvez vous inscrire pour un essai gratuit à ladresse suivante :",
"demo_button": "Se connecter en tant quutilisateur de démonstration",
"regular": "Normal",
"thin": "Fin",
"bold": "Gras",
"fill": "Rempli",
"duotone": "Bicolore",
"light_icon": "Léger",
"search": "Rechercher",
"set_custom_icon": "Définir une icône personnalisée",
"view": "Voir",
"show": "Afficher",
"image": "Image",
"icon": "Icône",
"date": "Date",
"preview_unavailable": "Aperçu indisponible",
"saved": "Sauvegardé",
"untitled": "Sans titre",
"no_tags": "Aucun tags",
"no_description_provided": "Aucune description fournie.",
"change_icon": "Changer licône",
"upload_preview_image": "Téléverser la prévisualisation",
"columns": "Colonnes",
"default": "Défaut",
"invalid_url_guide":"Veuillez entrer une adresse valide pour le lien. (Commence généralement par http/https)",
"email_invalid": "Veuillez entrer une adresse email valide.",
"username_invalid_guide": "Le nom dutilisateur doit comporter au minimum 3 caractères, les espaces et les caractères spéciaux ne sont pas autorisés.",
"team_management": "Gestion déquipe",
"invite_user": "Inviter une personne",
"invite_users": "Inviter des personnes",
"invite_user_desc": "Afin dinviter une personne dans votre équipe, veuillez entrer son adresse email juste en-dessous :",
"invite_user_note": "Veuillez noter quune fois linvitation acceptée, cela déclenche lachat dun emplacement supplémentaire et votre compte sera ainsi facturé automatiquement pour cet ajout.",
"invite_user_price": "Le prix dun emplacement à lunité est de ${{price}} par mois ou ${{priceAnnual}} par an, en fonction de votre type dabonnement en cours.",
"send_invitation": "Envoyer linvitation",
"learn_more": "En savoir plus",
"invitation_desc": "{{owner}} vous a invité à rejoindre Linkwarden. \nPour continuer, veuillez terminer la configuration de votre compte.",
"invitation_accepted": "Invitation acceptée !",
"status": "Statut",
"pending": "En attente",
"active": "Activé",
"manage_seats": "Gérer les emplacements",
"seats_purchased": "{{count}} emplacements achetées",
"seat_purchased": "{{count}} emplacement acheté",
"date_added": "Date ajoutée",
"resend_invite": "Renvoyer linvitation",
"resend_invite_success": "Invitation renvoyée !",
"remove_user": "Supprimer lutilisateur",
"continue_to_dashboard": "Continuer vers le tableau de bord",
"confirm_user_removal_desc": "Une nouvelle inscription sera nécessaire si ces personnes souhaitent accéder à nouveau à Linkwarden.",
"click_out_to_apply": "Cliquer en-dehors pour valider",
"submit": "Valider",
"thanks_for_feedback": "Merci pour ce retour !",
"quick_survey": "Sondage rapide",
"how_did_you_discover_linkwarden": "Comment avez-vous connu Linkwarden ?",
"rather_not_say": "Ne se prononce pas",
"search_engine": "Moteur de recherche (Google, Bing, etc.)",
"reddit": "Reddit",
"lemmy": "Lemmy",
"people_recommendation": "Recommandation (ami, famille, etc.)",
"open_all_links": "Ouvrir tous les liens"
}

View File

@@ -0,0 +1,433 @@
{
"user_administration": "Zarządzanie użytkownikami",
"search_users": "Wyszukaj użytkowników",
"no_users_found": "Nie znaleziono użytkowników.",
"no_user_found_in_search": "Nie znaleziono użytkowników z podanym zapytaniem.",
"username": "Nazwa użytkownika",
"email": "Email",
"subscribed": "Subskrybowane",
"created_at": "Utworzono",
"not_available": "N/A",
"check_your_email": "Sprawdź swój e-mail",
"authenticating": "Uwierzytelnianie...",
"verification_email_sent": "Wysłano e-mail weryfikacyjny.",
"verification_email_sent_desc": "Link do logowania został wysłany na Twój adres e-mail. Jeśli nie widzisz wiadomości, sprawdź folder spam.",
"resend_email": "Wyślij ponownie e-mail",
"invalid_credentials": "Nieprawidłowe dane uwierzytelniające.",
"fill_all_fields": "Proszę wypełnić wszystkie pola.",
"enter_credentials": "Wprowadź swoje dane logowania",
"username_or_email": "Nazwa użytkownika lub e-mail",
"password": "Hasło",
"confirm_password": "Potwierdź hasło",
"forgot_password": "Zapomniałeś hasła?",
"login": "Zaloguj się",
"or_continue_with": "Lub kontynuuj z",
"new_here": "Nowy użytkownik?",
"sign_up": "Zarejestruj się",
"sign_in_to_your_account": "Zaloguj się na swoje konto",
"dashboard_desc": "Krótki przegląd Twoich danych",
"link": "Link",
"links": "Linki",
"collection": "Kolekcja",
"collections": "Kolekcje",
"tag": "Tag",
"tags": "Tagi",
"recent": "Ostatnio dodane",
"recent_links_desc": "Ostatnio dodane linki",
"view_all": "Zobacz wszystkie",
"view_added_links_here": "Wyświetl ostatnio dodane linki!",
"view_added_links_here_desc": "Ta sekcja wyświetla Twoje najnowsze dodane linki z każdej kolekcji, do której masz dostęp.",
"add_link": "Dodaj nowy link",
"import_links": "Zaimportuj linki",
"from_linkwarden": "Z Linkwarden",
"from_html": "Z pliku zakładek HTML",
"from_wallabag": "Z Wallabag (plik JSON)",
"pinned": "Przypięte",
"pinned_links_desc": "Twoje przypięte linki",
"pin_favorite_links_here": "Przypnij swoje ulubione linki tutaj!",
"pin_favorite_links_here_desc": "Możesz przypiąć swoje ulubione linki, klikając na trzy kropki przy każdym linku i wybierając Przypnij do pulpitu.",
"sending_password_link": "Wysyłanie linku do resetowania hasła...",
"password_email_prompt": "Wprowadź swój e-mail, abyśmy mogli wysłać Ci link do stworzenia nowego hasła.",
"send_reset_link": "Wyślij link do resetowania",
"reset_email_sent_desc": "Sprawdź swój e-mail w poszukiwaniu linku do zresetowania hasła. Jeśli nie pojawi się w ciągu kilku minut, sprawdź folder spam.",
"back_to_login": "Powrót do logowania",
"email_sent": "E-mail wysłany!",
"passwords_mismatch": "Hasła nie pasują do siebie.",
"password_too_short": "Hasło musi mieć co najmniej 8 znaków.",
"creating_account": "Tworzenie konta...",
"account_created": "Konto utworzone!",
"trial_offer_desc": "Odblokuj {{count}} dni usługi Premium bez kosztów!",
"register_desc": "Utwórz nowe konto",
"registration_disabled_desc": "Rejestracja jest wyłączona w tej instancji, skontaktuj się z administratorem w razie jakichkolwiek problemów.",
"enter_details": "Wprowadź swoje dane",
"display_name": "Wyświetlana nazwa",
"sign_up_agreement": "Rejestrując się, zgadzasz się na nasze <0>Warunki korzystania</0> oraz <1>Politykę prywatności</1>.",
"need_help": "Potrzebujesz pomocy?",
"get_in_touch": "Skontaktuj się z nami",
"already_registered": "Masz już konto?",
"deleting_selections": "Usuwanie wybranych...",
"links_deleted": "Usunięto {{count}} linków.",
"link_deleted": "Usunięto 1 link.",
"links_selected": "Wybrano {{count}} linków.",
"link_selected": "Wybrano 1 link.",
"nothing_selected": "Nic nie wybrano",
"edit": "Edytuj",
"delete": "Usuń",
"nothing_found": "Nie znaleziono.",
"redirecting_to_stripe": "Przekierowywanie do Stripe...",
"subscribe_title": "Subskrybuj Linkwarden!",
"subscribe_desc": "Zostaniesz przekierowany do Stripe, w razie problemów skontaktuj się z nami pod adresem: <0>support@linkwarden.app</0>.",
"monthly": "Miesięcznie",
"yearly": "Rocznie",
"discount_percent": "{{percent}}% zniżki",
"billed_monthly": "Rozliczane miesięcznie",
"billed_yearly": "Rozliczane rocznie",
"total": "Łącznie",
"total_annual_desc": "{{count}}-dniowy darmowy okres próbny, potem {{annualPrice}} USD rocznie",
"total_monthly_desc": "{{count}}-dniowy darmowy okres próbny, potem {{monthlyPrice}} USD miesięcznie",
"plus_tax": "+ VAT, jeśli dotyczy",
"complete_subscription": "Zakończ subskrypcję",
"sign_out": "Wyloguj się",
"access_tokens": "Tokeny dostępu",
"access_tokens_description": "Tokeny dostępu mogą być używane do dostępu do Linkwarden z innych aplikacji i usług bez podawania swojej nazwy użytkownika i hasła.",
"new_token": "Nowy token dostępu",
"name": "Nazwa",
"created_success": "Utworzono!",
"created": "Utworzono",
"expires": "Wygasa",
"accountSettings": "Ustawienia konta",
"language": "Język",
"profile_photo": "Zdjęcie profilowe",
"upload_new_photo": "Prześlij nowe zdjęcie...",
"remove_photo": "Usuń zdjęcie",
"make_profile_private": "Ustaw profil jako prywatny",
"profile_privacy_info": "To ograniczy możliwość znalezienia i dodania Cię do nowych kolekcji.",
"whitelisted_users": "Biała lista użytkowników",
"whitelisted_users_info": "Podaj nazwy użytkowników, którym chcesz umożliwić widoczność swojego profilu. Oddziel je przecinkiem.",
"whitelisted_users_placeholder": "Twój profil jest teraz ukryty przed wszystkimi...",
"save_changes": "Zapisz zmiany",
"import_export": "Importuj i eksportuj",
"import_data": "Zaimportuj swoje dane z innych platform.",
"download_data": "Pobierz swoje dane natychmiast.",
"export_data": "Eksportuj dane",
"delete_account": "Usuń konto",
"delete_account_warning": "Spowoduje to trwałe usunięcie WSZYSTKICH linków, kolekcji, tagów i zarchiwizowanych danych, które posiadasz.",
"cancel_subscription_notice": "Spowoduje to również anulowanie Twojej subskrypcji.",
"account_deletion_page": "Strona usuwania konta",
"applying_settings": "Zastosowywanie ustawień...",
"settings_applied": "Ustawienia zastosowane!",
"email_change_request": "Wysłano prośbę o zmianę adresu e-mail. Proszę zweryfikować nowy adres e-mail.",
"image_upload_no_file_error": "Nie wybrano pliku. Proszę wybrać obraz do przesłania.",
"image_upload_size_error": "Wybierz plik PNG lub JPEG mniejszy niż 1 MB.",
"image_upload_format_error": "Nieprawidłowy format pliku.",
"importing_bookmarks": "Importowanie zakładek...",
"import_success": "Zakładki zaimportowane! Trwa odświeżanie strony...",
"more_coming_soon": "Więcej wkrótce!",
"billing_settings": "Ustawienia rozliczeń",
"manage_subscription_intro": "Aby zarządzać/anulować subskrypcję, odwiedź",
"billing_portal": "Portal rozliczeniowy",
"help_contact_intro": "Jeśli nadal potrzebujesz pomocy lub napotkałeś problemy, skontaktuj się z nami pod adresem:",
"fill_required_fields": "Proszę wypełnić wymagane pola.",
"deleting_message": "Usuwanie wszystkiego, proszę czekać...",
"delete_warning": "Spowoduje to trwałe usunięcie wszystkich linków, kolekcji, tagów i zarchiwizowanych danych, które posiadasz. Spowoduje to również wylogowanie się. Ta akcja jest nieodwracalna!",
"optional": "Opcjonalne",
"feedback_help": "(ale naprawdę pomaga nam się rozwijać!)",
"reason_for_cancellation": "Powód anulowania",
"please_specify": "Proszę określić",
"customer_service": "Obsługa klienta",
"low_quality": "Niska jakość",
"missing_features": "Brak funkcji",
"switched_service": "Zmiana usługi",
"too_complex": "Zbyt skomplikowane",
"too_expensive": "Zbyt drogie",
"unused": "Nieużywane",
"other": "Inne",
"more_information": "Więcej informacji (im więcej szczegółów, tym lepiej)",
"feedback_placeholder": "np. Potrzebowałem funkcji, która...",
"delete_your_account": "Usuń swoje konto",
"change_password": "Zmień hasło",
"password_length_error": "Hasło musi mieć co najmniej 8 znaków.",
"applying_changes": "Zastosowywanie...",
"password_change_instructions": "Aby zmienić hasło, wypełnij poniższe pola. Twoje hasło powinno mieć co najmniej 8 znaków.",
"old_password": "Stare hasło",
"new_password": "Nowe hasło",
"preference": "Preferencje",
"select_theme": "Wybierz motyw",
"dark": "Ciemny",
"light": "Jasny",
"archive_settings": "Ustawienia archiwizacji",
"formats_to_archive": "Formaty do archiwizacji/zachowywania stron internetowych:",
"screenshot": "Zrzut ekranu",
"pdf": "PDF",
"archive_org_snapshot": "Zrzut na Archive.org",
"link_settings": "Ustawienia linków",
"prevent_duplicate_links": "Zapobiegaj duplikatom linków",
"clicking_on_links_should": "Klikanie w linki powinno:",
"open_original_content": "Otworzyć oryginalną treść",
"open_pdf_if_available": "Otworzyć PDF, jeśli dostępny",
"open_readable_if_available": "Otworzyć wersję do czytania, jeśli dostępna",
"open_screenshot_if_available": "Otworzyć zrzut ekranu, jeśli dostępny",
"open_webpage_if_available": "Otworzyć kopię strony internetowej, jeśli dostępna",
"tag_renamed": "Zmieniono tag!",
"tag_deleted": "Usunięto tag!",
"rename_tag": "Zmień nazwę taga",
"delete_tag": "Usuń tag",
"list_created_with_linkwarden": "Lista utworzona z Linkwarden",
"by_author": "Przez {{author}}.",
"by_author_and_other": "Przez {{author}} i {{count}} innego.",
"by_author_and_others": "Przez {{author}} i {{count}} innych.",
"search_count_link": "Wyszukano {{count}} link",
"search_count_links": "Wyszukano {{count}} linków",
"collection_is_empty": "Ta kolekcja jest pusta...",
"all_links": "Wszystkie linki",
"all_links_desc": "Linki z każdej kolekcji",
"you_have_not_added_any_links": "Nie utworzyłeś jeszcze żadnych linków",
"collections_you_own": "Kolekcje, które posiadasz",
"new_collection": "Nowa kolekcja",
"other_collections": "Inne kolekcje",
"other_collections_desc": "Udostępnione kolekcje, w których jesteś członkiem",
"showing_count_results": "Pokazywanie {{count}} wyników",
"showing_count_result": "Pokazywanie {{count}} wyniku",
"edit_collection_info": "Edytuj informacje o kolekcji",
"share_and_collaborate": "Udostępniaj i współpracuj",
"view_team": "Zobacz zespół",
"team": "Zespół",
"create_subcollection": "Utwórz podkolekcję",
"delete_collection": "Usuń kolekcję",
"leave_collection": "Opuść kolekcję",
"email_verified_signing_out": "E-mail zweryfikowany. Wylogowywanie...",
"invalid_token": "Nieprawidłowy token.",
"sending_password_recovery_link": "Wysyłanie linku do odzyskiwania hasła...",
"please_fill_all_fields": "Proszę wypełnić wszystkie pola.",
"password_updated": "Hasło zaktualizowane!",
"reset_password": "Zresetuj hasło",
"enter_email_for_new_password": "Podaj swój e-mail, abyśmy mogli wysłać Ci link do utworzenia nowego hasła.",
"update_password": "Zaktualizuj hasło",
"password_successfully_updated": "Twoje hasło zostało pomyślnie zaktualizowane.",
"user_already_member": "Użytkownik już istnieje.",
"you_are_already_collection_owner": "Jesteś już właścicielem kolekcji.",
"date_newest_first": "Data (od najnowszych)",
"date_oldest_first": "Data (od najstarszych)",
"name_az": "Nazwa (A-Z)",
"name_za": "Nazwa (Z-A)",
"description_az": "Opis (A-Z)",
"description_za": "Opis (Z-A)",
"all_rights_reserved": "© {{date}} <0>Linkwarden</0>. Wszelkie prawa zastrzeżone.",
"you_have_no_collections": "Nie masz żadnych kolekcji...",
"you_have_no_tags": "Nie masz żadnych tagów...",
"cant_change_collection_you_dont_own": "Nie możesz wprowadzać zmian w kolekcji, której nie posiadasz.",
"account": "Konto",
"billing": "Rozliczenia",
"linkwarden_version": "Linkwarden {{version}}",
"help": "Pomoc",
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"link_preservation_in_queue": "Zachowywanie linków jest obecnie w kolejce",
"check_back_later": "Sprawdź ponownie później, aby zobaczyć wyniki",
"there_are_more_formats": "Istnieje więcej zapisanych formatów w kolejce",
"settings": "Ustawienia",
"switch_to": "Zmień na {{theme}}",
"logout": "Wyloguj się",
"start_journey": "Rozpocznij swoją podróż, tworząc nowy link!",
"create_new_link": "Utwórz nowy link",
"new_link": "Nowy link",
"create_new": "Utwórz nowy...",
"pwa_install_prompt": "Zainstaluj Linkwarden na swoim ekranie głównym, aby uzyskać szybszy dostęp i poprawić doświadczenia. <0>Dowiedz się więcej</0>",
"full_content": "Pełna treść",
"slower": "Wolniej",
"new_version_announcement": "Zobacz, co nowego w <0>Linkwarden {{version}}!</0>",
"creating": "Tworzenie...",
"upload_file": "Prześlij plik",
"file": "Plik",
"file_types": "PDF, PNG, JPG (do {{size}} MB)",
"description": "Opis",
"auto_generated": "Zostanie automatycznie wygenerowany, jeśli nic nie podasz.",
"example_link": "np. Przykładowy link",
"hide": "Ukryj",
"more": "Więcej",
"options": "Opcje",
"description_placeholder": "Notatki, przemyślenia itp.",
"deleting": "Usuwanie...",
"token_revoked": "Token unieważniony.",
"revoke_token": "Unieważnij token",
"revoke_confirmation": "Czy na pewno chcesz unieważnić ten token dostępu? Każda aplikacja lub usługa korzystająca z tego tokena nie będzie już mogła uzyskać dostępu do Linkwarden za jego pomocą.",
"revoke": "Unieważnij",
"sending_request": "Wysyłanie zapytania...",
"link_being_archived": "Link jest archiwizowany...",
"preserved_formats": "Zachowane formaty",
"available_formats": "Dostępne formaty dla tego linku",
"readable": "Wersja do czytania",
"preservation_in_queue": "Zachowywanie linku w kolejce",
"view_latest_snapshot": "Zobacz najnowszy zapis na archive.org",
"refresh_preserved_formats": "Odśwież zachowane formaty",
"this_deletes_current_preservations": "Spowoduje to usunięcie obecnych zapisów",
"create_new_user": "Utwórz nowego użytkownika",
"placeholder_johnny": "Johnny",
"placeholder_email": "johnny@example.com",
"placeholder_john": "john",
"user_created": "Użytkownik utworzony!",
"fill_all_fields_error": "Proszę wypełnić wszystkie pola.",
"password_change_note": "<0>Uwaga:</0> Proszę upewnić się, że poinformowałeś użytkownika, aby zmienił swoje hasło.",
"create_user": "Utwórz użytkownika",
"creating_token": "Tworzenie tokena...",
"token_created": "Token utworzony!",
"access_token_created": "Token dostępu utworzony",
"token_creation_notice": "Twój nowy token został utworzony. Proszę skopiować go i przechowywać w bezpiecznym miejscu. Nie będziesz mógł go ponownie zobaczyć.",
"copied_to_clipboard": "Skopiowano do schowka!",
"copy_to_clipboard": "Kopiuj do schowka",
"create_access_token": "Utwórz token dostępu",
"expires_in": "Wygasa za",
"token_name_placeholder": "np. Skrót dla iOS",
"create_token": "Utwórz token dostępu",
"7_days": "7 dni",
"30_days": "30 dni",
"60_days": "60 dni",
"90_days": "90 dni",
"no_expiration": "Bez wygaśnięcia",
"creating_link": "Tworzenie linku...",
"link_created": "Link utworzony!",
"link_name_placeholder": "Zostanie automatycznie wygenerowany, jeśli pozostawisz puste.",
"link_url_placeholder": "np. http://example.com/",
"link_description_placeholder": "Notatki, przemyślenia itp.",
"more_options": "Więcej opcji",
"hide_options": "Ukryj opcje",
"create_link": "Utwórz link",
"new_sub_collection": "Nowa podkolekcja",
"for_collection": "Dla {{name}}",
"create_new_collection": "Utwórz nową kolekcję",
"color": "Kolor",
"reset_defaults": "Resetuj do domyślnych",
"updating_collection": "Aktualizowanie kolekcji...",
"collection_name_placeholder": "np. Przykładowa kolekcja",
"collection_description_placeholder": "Cel tej kolekcji...",
"create_collection_button": "Utwórz kolekcję",
"password_change_warning": "Proszę potwierdzić swoje hasło przed zmianą adresu e-mail.",
"stripe_update_note": " Aktualizowanie tego pola zmieni również Twój e-mail rozliczeniowy w Stripe.",
"sso_will_be_removed_warning": "Jeśli zmienisz adres e-mail, wszystkie istniejące połączenia {{service}} SSO zostaną usunięte.",
"old_email": "Aktualny e-mail",
"new_email": "Nowy e-mail",
"confirm": "Potwierdź",
"edit_link": "Edytuj link",
"updating": "Aktualizowanie...",
"updated": "Zaktualizowano!",
"placeholder_example_link": "np. Przykładowy link",
"make_collection_public": "Upublicznij kolekcję",
"make_collection_public_checkbox": "Uczyń tę kolekcję publiczną",
"make_collection_public_desc": "To pozwoli każdemu zobaczyć tę kolekcję i jej użytkowników.",
"sharable_link": "Udostępnialny link",
"copied": "Skopiowano!",
"members": "Członkowie",
"add_member_placeholder": "Dodaj członków poprzez e-mail lub nazwę użytkownika",
"owner": "Właściciel",
"admin": "Administrator",
"contributor": "Współpracownik",
"viewer": "Przeglądający",
"viewer_desc": "Dostęp tylko do odczytu",
"contributor_desc": "Może przeglądać i tworzyć linki",
"admin_desc": "Pełny dostęp do wszystkich linków",
"remove_member": "Usuń członka",
"placeholder_example_collection": "np. Przykładowa kolekcja",
"placeholder_collection_purpose": "Cel tej kolekcji...",
"deleting_user": "Usuwanie...",
"user_deleted": "Użytkownik usunięty.",
"delete_user": "Usuń użytkownika",
"confirm_user_deletion": "Czy na pewno chcesz usunąć tego użytkownika?",
"irreversible_action_warning": "Ta akcja jest nieodwracalna!",
"delete_confirmation": "Usuń, wiem, co robię",
"delete_link": "Usuń link",
"deleted": "Usunięto.",
"link_deletion_confirmation_message": "Czy na pewno chcesz usunąć ten link?",
"warning": "Ostrzeżenie",
"irreversible_warning": "Ta akcja jest nieodwracalna!",
"shift_key_tip": "Przytrzymaj klawisz Shift podczas klikania 'Usuń', aby w przyszłości pominąć to potwierdzenie.",
"deleting_collection": "Usuwanie...",
"collection_deleted": "Kolekcja usunięta.",
"confirm_deletion_prompt": "Aby potwierdzić, wpisz \"{{name}}\" w polu poniżej:",
"type_name_placeholder": "Wpisz \"{{name}}\" tutaj.",
"deletion_warning": "Usunięcie tej kolekcji spowoduje trwałe usunięcie wszystkich jej zawartości i stanie się ona niedostępna dla wszystkich, w tym dla członków z poprzednim dostępem.",
"leave_prompt": "Kliknij przycisk poniżej, aby opuścić obecną kolekcję.",
"leave": "Opuść",
"edit_links": "Edytuj {{count}} linki",
"move_to_collection": "Przenieś do kolekcji",
"add_tags": "Dodaj tagi",
"remove_previous_tags": "Usuń poprzednie tagi",
"delete_links": "Usuń {{count}} linków",
"links_deletion_confirmation_message": "Czy na pewno chcesz usunąć {{count}} linków?",
"warning_irreversible": "Ostrzeżenie: Ta akcja jest nieodwracalna!",
"shift_key_instruction": "Przytrzymaj klawisz 'Shift', klikając 'Usuń', aby w przyszłości pominąć to potwierdzenie.",
"link_selection_error": "Nie masz uprawnień do edycji ani usunięcia tego elementu.",
"no_description": "Brak opisu.",
"applying": "Zastosowywanie...",
"unpin": "Odepnij",
"pin_to_dashboard": "Przypnij do pulpitu",
"show_link_details": "Pokaż szczegóły linku",
"link_pinned": "Link przypięty!",
"link_unpinned": "Link odpięty!",
"webpage": "Strona internetowa",
"server_administration": "Administracja serwera",
"all_collections": "Wszystkie kolekcje",
"dashboard": "Pulpit nawigacyjny",
"demo_title": "Demo tylko",
"demo_desc": "To jest tylko wersja demo Linkwardena i przesyłanie plików jest wyłączone.",
"demo_desc_2": "Jeśli chcesz wypróbować pełną wersję, możesz zapisać się na bezpłatny okres próbny na:",
"demo_button": "Zaloguj się jako użytkownik demo",
"regular": "Regularne",
"thin": "Cienkie",
"bold": "Pogrubione",
"fill": "Wypełnione",
"duotone": "Dwukolorowe",
"light_icon": "Jasne",
"search": "Szukaj",
"set_custom_icon": "Ustaw niestandardową ikonę",
"view": "Widok",
"show": "Pokaż",
"image": "Obraz",
"icon": "Ikona",
"date": "Data",
"preview_unavailable": "Podgląd niedostępny",
"saved": "Zapisano",
"untitled": "Bez tytułu",
"no_tags": "Brak tagów.",
"no_description_provided": "Brak opisu.",
"change_icon": "Zmień ikonę",
"upload_preview_image": "Prześlij obraz podglądu",
"columns": "Kolumny",
"default": "Domyślne",
"invalid_url_guide": "Proszę podać prawidłowy adres dla linku. (Powinien zaczynać się od http/https)",
"email_invalid": "Proszę podać prawidłowy adres e-mail.",
"username_invalid_guide": "Nazwa użytkownika musi mieć co najmniej 3 znaki, bez spacji i znaków specjalnych.",
"team_management": "Zarządzanie zespołem",
"invite_user": "Zaproś użytkownika",
"invite_users": "Zaproś użytkowników",
"invite_user_desc": "Aby zaprosić kogoś do swojego zespołu, wpisz poniżej jego adres e-mail:",
"invite_user_note": "Proszę pamiętać, że po zaakceptowaniu zaproszenia, zostanie zakupione dodatkowe miejsce, a Twoje konto automatycznie zostanie obciążone opłatą za to dodanie.",
"invite_user_price": "Koszt każdego miejsca wynosi {{price}} USD miesięcznie lub {{priceAnnual}} USD rocznie, w zależności od Twojego obecnego planu subskrypcji.",
"send_invitation": "Wyślij zaproszenie",
"learn_more": "Dowiedz się więcej",
"invitation_desc": "{{owner}} zaprosił Cię do dołączenia do Linkwardena. \nAby kontynuować, zakończ konfigurowanie swojego konta.",
"invitation_accepted": "Zaproszenie zaakceptowane!",
"status": "Status",
"pending": "Oczekujące",
"active": "Aktywne",
"manage_seats": "Zarządzaj miejscami",
"seats_purchased": "{{count}} miejsc zakupionych",
"seat_purchased": "{{count}} miejsce zakupione",
"date_added": "Data dodania",
"resend_invite": "Wyślij ponownie zaproszenie",
"resend_invite_success": "Zaproszenie wysłane ponownie!",
"remove_user": "Usuń użytkownika",
"continue_to_dashboard": "Przejdź do pulpitu",
"confirm_user_removal_desc": "Będą musieli posiadać subskrypcję, aby ponownie uzyskać dostęp do Linkwardena.",
"click_out_to_apply": "Kliknij poza, aby zastosować",
"submit": "Wyślij",
"thanks_for_feedback": "Dziękujemy za Twoją opinię!",
"quick_survey": "Krótka ankieta",
"how_did_you_discover_linkwarden": "Jak odkryłeś Linkwardena?",
"rather_not_say": "Wolę nie mówić",
"search_engine": "Wyszukiwarka (Google, Bing, itd.)",
"reddit": "Reddit",
"lemmy": "Lemmy",
"people_recommendation": "Polecenie (przyjaciel, rodzina, itd.)",
"open_all_links": "Otwórz wszystkie linki"
}

View File

@@ -2,6 +2,8 @@ import "dotenv/config";
import { Collection, Link, User } from "@prisma/client";
import { prisma } from "../lib/api/db";
import archiveHandler from "../lib/api/archiveHandler";
import Parser from "rss-parser";
import { hasPassedLimit } from "../lib/api/verifyCapacity";
const args = process.argv.slice(2).join(" ");
@@ -105,23 +107,107 @@ async function processBatch() {
await Promise.allSettled(processingPromises);
}
const intervalInSeconds = Number(process.env.ARCHIVE_SCRIPT_INTERVAL) || 10;
async function fetchAndProcessRSS() {
const rssSubscriptions = await prisma.rssSubscription.findMany({});
const parser = new Parser();
rssSubscriptions.forEach(async (rssSubscription) => {
try {
const feed = await parser.parseURL(rssSubscription.url);
if (
rssSubscription.lastBuildDate &&
new Date(rssSubscription.lastBuildDate) < new Date(feed.lastBuildDate)
) {
console.log(
"\x1b[34m%s\x1b[0m",
`Processing new RSS feed items for ${rssSubscription.name}`
);
const newItems = feed.items.filter((item) => {
const itemPubDate = item.pubDate ? new Date(item.pubDate) : null;
return itemPubDate && itemPubDate > rssSubscription.lastBuildDate!; // We know lastBuildDate is not null here
});
const hasTooManyLinks = await hasPassedLimit(
rssSubscription.ownerId,
newItems.length
);
if (hasTooManyLinks) {
console.log(
"\x1b[34m%s\x1b[0m",
`User ${rssSubscription.ownerId} has too many links. Skipping new RSS feed items.`
);
return;
}
newItems.forEach(async (item) => {
await prisma.link.create({
data: {
name: item.title,
url: item.link,
type: "link",
createdBy: {
connect: {
id: rssSubscription.ownerId,
},
},
collection: {
connect: {
id: rssSubscription.collectionId,
},
},
},
});
});
// Update the lastBuildDate in the database
await prisma.rssSubscription.update({
where: { id: rssSubscription.id },
data: { lastBuildDate: new Date(feed.lastBuildDate) },
});
}
} catch (error) {
console.error(
"\x1b[34m%s\x1b[0m",
`Error processing RSS feed ${rssSubscription.url}:`,
error
);
}
});
}
function delay(sec: number) {
return new Promise((resolve) => setTimeout(resolve, sec * 1000));
}
async function init() {
console.log("\x1b[34m%s\x1b[0m", "Processing the links...");
const pollingIntervalInSeconds =
(Number(process.env.NEXT_PUBLIC_RSS_POLLING_INTERVAL_MINUTES) || 60) * 60; // Default to one hour if not set
async function startRSSPolling() {
console.log("\x1b[34m%s\x1b[0m", "Starting RSS polling...");
while (true) {
try {
await processBatch();
await delay(intervalInSeconds);
} catch (error) {
console.error("\x1b[34m%s\x1b[0m", "Error processing link:", error);
await delay(intervalInSeconds);
}
await fetchAndProcessRSS();
await delay(pollingIntervalInSeconds);
}
}
const archiveIntervalInSeconds =
Number(process.env.ARCHIVE_SCRIPT_INTERVAL) || 10;
async function startArchiveProcessing() {
console.log("\x1b[34m%s\x1b[0m", "Starting link preservation...");
while (true) {
await processBatch();
await delay(archiveIntervalInSeconds);
}
}
async function init() {
console.log("\x1b[34m%s\x1b[0m", "Initializing application...");
startRSSPolling();
startArchiveProcessing();
}
init();

View File

@@ -3049,6 +3049,11 @@ enhanced-resolve@^5.10.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"
entities@^2.0.3:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^4.4.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
@@ -5547,6 +5552,14 @@ rrweb-cssom@^0.6.0:
resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1"
integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==
rss-parser@^3.13.0:
version "3.13.0"
resolved "https://registry.yarnpkg.com/rss-parser/-/rss-parser-3.13.0.tgz#f1f83b0a85166b8310ec531da6fbaa53ff0f50f0"
integrity sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==
dependencies:
entities "^2.0.3"
xml2js "^0.5.0"
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -6461,6 +6474,14 @@ xml2js@^0.4.5:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xml2js@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7"
integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"