mirror of
https://github.com/linkwarden/linkwarden.git
synced 2026-03-03 00:37:00 +00:00
Merge branch 'dev' into ai-tagging
This commit is contained in:
@@ -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=
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
60
components/ModalContent/DeleteRssSubscriptionModal.tsx
Normal file
60
components/ModalContent/DeleteRssSubscriptionModal.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
104
components/ModalContent/NewRssSubscriptionModal.tsx
Normal file
104
components/ModalContent/NewRssSubscriptionModal.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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
69
hooks/store/rss.tsx
Normal 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 };
|
||||
@@ -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 };
|
||||
|
||||
@@ -6,6 +6,7 @@ export default async function exportData(userId: number) {
|
||||
include: {
|
||||
collections: {
|
||||
include: {
|
||||
rssSubscriptions: true,
|
||||
links: {
|
||||
include: {
|
||||
tags: true,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 -`,
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -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(),
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ module.exports = {
|
||||
"de",
|
||||
"nl",
|
||||
"tr",
|
||||
"pl",
|
||||
],
|
||||
},
|
||||
reloadOnPrerender: process.env.NODE_ENV === "development",
|
||||
|
||||
@@ -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",
|
||||
|
||||
29
pages/api/v1/user/rss/[id].ts
Normal file
29
pages/api/v1/user/rss/[id].ts
Normal 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." });
|
||||
}
|
||||
}
|
||||
98
pages/api/v1/user/rss/index.ts
Normal file
98
pages/api/v1/user/rss/index.ts
Normal 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 });
|
||||
}
|
||||
}
|
||||
95
pages/settings/rss-subscriptions.tsx
Normal file
95
pages/settings/rss-subscriptions.tsx
Normal 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 };
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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": "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",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -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 d’utilisateur",
|
||||
"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 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": "Renvoyer l’e-mail",
|
||||
"invalid_credentials": "Informations d’identification 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 d’identification",
|
||||
"username_or_email": "Nom d’utilisateur 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": "S’inscrire",
|
||||
"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 d’un 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. S’il n’apparaî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 d’un 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": "L’inscription est désactivée pour cette instance, veuillez contacter l’administrateur 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 d’utilisation</0> et notre <1>Politique de confidentialité</1>.",
|
||||
"need_help": "Besoin d’aide ?",
|
||||
"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 n’a é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, n’hé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 d’essai gratuit, puis ${{annualPrice}} annuellement",
|
||||
"total_monthly_desc": "{{count}}-jour d’essai 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 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",
|
||||
"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 d’utilisateur 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 d’autres 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 d’aide ou rencontrez des problèmes, n’hésitez pas à nous contacter à l’adresse 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 l’annulation",
|
||||
"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 l’utilité",
|
||||
"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 d’informations (n’hésitez pas à détailler pour nous aider à mieux comprendre)",
|
||||
"feedback_placeholder": "Par exemple: J’avais besoin d’une 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 d’archivage",
|
||||
"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é d’Archive.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 n’avez 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": "L’utilisateur 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 n’avez pas de collections...",
|
||||
"you_have_no_tags": "Vous n’avez 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 d’attente",
|
||||
"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 d’autres formats conservés dans la file d’attente",
|
||||
"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 l’expé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 d’accueil 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 qu’il 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 n’est 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 d’accès ? Les applications ou services utilisant ce jeton ne pourront plus l’exploiter 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 d’archivage...",
|
||||
"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 d’attente",
|
||||
"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 l’utilisateur qu’il 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 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 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 d’accè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 d’accè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 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/",
|
||||
"link_description_placeholder": "Notes, réflexions, etc.",
|
||||
"more_options": "Plus d'options",
|
||||
"more_options": "Plus d’options",
|
||||
"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": "L’objectif 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 d’adresse é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 d’exemple",
|
||||
"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 à n’importe 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 d’utilisateur (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": "L’objectif de cette collection...",
|
||||
"deleting_user": "Suppression...",
|
||||
"user_deleted": "Utilisateur supprimé.",
|
||||
"delete_user": "Supprimer l'utilisateur",
|
||||
"delete_user": "Supprimer l’utilisateur",
|
||||
"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 à l’avenir.",
|
||||
"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 à 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.",
|
||||
"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 s’agit d’une 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 à l’adresse suivante :",
|
||||
"demo_button": "Se connecter en tant qu’utilisateur 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 l’icô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 d’utilisateur 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 d’inviter une personne dans votre équipe, veuillez entrer son adresse email juste en-dessous :",
|
||||
"invite_user_note": "Veuillez noter qu’une fois l’invitation acceptée, cela déclenche l’achat d’un emplacement supplémentaire et votre compte sera ainsi facturé automatiquement pour cet ajout.",
|
||||
"invite_user_price": "Le prix d’un emplacement à l’unité est de ${{price}} par mois ou ${{priceAnnual}} par an, en fonction de votre type d’abonnement en cours.",
|
||||
"send_invitation": "Envoyer l’invitation",
|
||||
"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 l’invitation",
|
||||
"resend_invite_success": "Invitation renvoyée !",
|
||||
"remove_user": "Supprimer l’utilisateur",
|
||||
"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"
|
||||
}
|
||||
|
||||
433
public/locales/pl/common.json
Normal file
433
public/locales/pl/common.json
Normal 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"
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
21
yarn.lock
21
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user