Compare commits

...

40 Commits

Author SHA1 Message Date
Daniel
c24e76adac Merge pull request #706 from linkwarden/dev
v2.7.0
2024-08-16 12:36:43 -04:00
daniel31x13
5d26617251 bug fixed 2024-08-16 12:35:04 -04:00
daniel31x13
0e47ad9920 bump version 2024-08-15 16:42:36 -04:00
daniel31x13
ca45076b6c minor fix 2024-08-15 15:37:47 -04:00
Daniel
3bf6dcad2f Merge pull request #692 from phillibl/main
Update [...nextauth].ts to allow existing SSO user sign
2024-08-15 13:45:07 -04:00
daniel31x13
23860b8511 minor fix 2024-08-15 11:00:29 -04:00
daniel31x13
8758976f8d minor fix 2024-08-15 10:30:44 -04:00
Daniel
550dbd2bf0 Merge pull request #704 from shichen437/dev
feat(lang): add chinese translate
2024-08-15 08:49:26 -04:00
shichen437
04d2b3c6b2 feat(lang): add chinese translate 2024-08-15 17:20:46 +08:00
daniel31x13
7bd0e29538 small improvement 2024-08-14 20:07:06 -04:00
daniel31x13
5baf55694c minor improvement 2024-08-14 19:23:51 -04:00
daniel31x13
193a70c6e8 fix dropdown text wrapping in other languages 2024-08-14 19:13:19 -04:00
daniel31x13
5b430cf31e add french translation 2024-08-14 17:49:53 -04:00
Daniel
684609a1dd Merge pull request #654 from zarevskaya/patch-1
Add french translation
2024-08-14 17:43:15 -04:00
Daniel
ebb2016915 Merge pull request #671 from jlssmt/main
handle undefined
2024-08-14 17:40:28 -04:00
daniel31x13
c103b66694 Merge branch 'dev' of https://github.com/linkwarden/linkwarden into dev 2024-08-14 17:26:40 -04:00
daniel31x13
863bcc3838 bug fixed 2024-08-14 17:26:38 -04:00
Daniel
66b0aacc3f Merge pull request #660 from IsaacWise06/issue-646
fix(collections): Redirect to dashboard or login for non-public collections
2024-08-14 17:04:25 -04:00
Daniel
299498ffa6 Merge pull request #703 from linkwarden/chore/react-query-implementation
Chore/react query implementation
2024-08-14 16:45:40 -04:00
phillibl
c5602dc79f Merge pull request #1 from phillibl/phillibl-SSO-user-signin
Update [...nextauth].ts to allow existing SSO user sign
2024-08-07 05:44:19 -04:00
phillibl
0158e58d90 Update [...nextauth].ts
Fixed issue where sign in would fail for existing user if DISABLE_NEW_SSO_USERS  = true
2024-08-07 05:29:10 -04:00
jlssmt
102690fc10 handle undefined 2024-08-02 09:07:13 +02:00
Daniel
237499fd03 Merge pull request #684 from linkwarden/daniel31x13-patch-1
Update README
2024-08-01 15:55:20 -04:00
Daniel
9a287d1aef Merge pull request #683 from linkwarden/daniel31x13-patch-1
Update README.md
2024-08-01 15:54:29 -04:00
Daniel
299a2331ff Update README.md 2024-08-01 15:54:00 -04:00
Isaac Wise
9d91d2064b Merge branch 'linkwarden:main' into issue-646 2024-07-27 17:57:25 -05:00
Daniel
15a0084fb7 Merge pull request #677 from linkwarden/dev
bump version
2024-07-26 12:01:38 -04:00
Daniel
c0abf2f411 Merge pull request #676 from linkwarden/dev
bug fixed
2024-07-26 11:55:07 -04:00
Daniel
a886437589 Merge pull request #674 from linkwarden/dev
merged the two migration scripts for v2.6.1
2024-07-25 23:44:46 -04:00
Daniel
a82c4ef85f Merge pull request #670 from linkwarden/dev
Dev
2024-07-25 14:24:24 -04:00
Daniel
7036b46084 Merge pull request #668 from linkwarden/dev
made script more efficient
2024-07-25 14:16:16 -04:00
Daniel
2bba8198b8 Merge pull request #667 from linkwarden/dev
minor fix
2024-07-25 13:58:13 -04:00
Daniel
96a70a9689 Merge pull request #666 from linkwarden/dev
update version number
2024-07-25 13:46:59 -04:00
Daniel
288fd9df87 Merge pull request #665 from linkwarden/dev
bug fixed
2024-07-25 13:45:03 -04:00
Isaac Wise
7d43ed52a4 format 2024-07-22 17:50:24 -05:00
Isaac Wise
614653bf29 Merge branch 'linkwarden:main' into issue-646 2024-07-22 17:41:34 -05:00
Isaac Wise
1b9dafbe47 Handle 400 error code when accesing a non public collection 2024-07-22 17:39:38 -05:00
zarev
abc93f1bf9 Update common.json
Correction
2024-07-20 09:55:51 +02:00
zarev
c23964a46d Create common.json
In french, if you want it ;)
2024-07-19 22:37:46 +02:00
Daniel
a76e996fc1 Merge pull request #653 from linkwarden/dev
v2.6.0
2024-07-19 08:59:54 -04:00
31 changed files with 932 additions and 126 deletions

View File

@@ -1,17 +1,18 @@
<div align="center">
<img src="./assets/logo.png" width="100px" />
<h1>Linkwarden</h1>
<h3>Bookmark Preservation for Individuals and Teams</h3>
<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>
<img alt="GitHub commits since latest release" src="https://img.shields.io/github/commits-since/linkwarden/linkwarden/latest/dev?style=for-the-badge&label=COMMITS%20SINCE%20LATEST%20RELEASE">
<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>
</div>
<div align='center'>
[Website](https://linkwarden.app) | [Getting Started](https://docs.linkwarden.app) | [Features](https://github.com/linkwarden/linkwarden#features) | [Roadmap](https://github.com/orgs/linkwarden/projects/1) | [Support ❤](https://github.com/linkwarden/linkwarden#support-)
[« LAUNCH DEMO »](https://demo.linkwarden.app)
[Cloud](https://cloud.linkwarden.app) · [Website](https://linkwarden.app) · [Features](https://github.com/linkwarden/linkwarden#features)
</div>
@@ -24,7 +25,7 @@ The objective is to organize useful webpages and articles you find across the we
Additionally, Linkwarden is designed with collaboration in mind, sharing links with the public and/or allowing multiple users to work together seamlessly.
> [!TIP]
> Our official [Cloud](https://linkwarden.app/#pricing) offering provides the simplest way to begin using Linkwarden and it's the preferred choice for many due to its time-saving benefits. <br> Your subscription supports our hosting infrastructure and ongoing development. <br> Alternatively, if you prefer [self-hosting](https://docs.linkwarden.app/self-hosting/installation) Linkwarden, no problem! You'll still have access to all the premium features.
> Our official [Cloud](https://linkwarden.app/#pricing) offering provides the simplest way to begin using Linkwarden and it's the preferred choice for many due to its time-saving benefits. <br> Your subscription supports our hosting infrastructure and ongoing development. <br> Alternatively, if you prefer self-hosting Linkwarden, you can do so by following our [Installation documentation](https://docs.linkwarden.app/self-hosting/installation).
<img src="./assets/dashboard.png" />
@@ -71,10 +72,14 @@ We've forked the old version from the current repository into [this repo](https:
- ⬇️ Import and export your bookmarks.
- 🔐 SSO integration. (Enterprise and Self-hosted users only)
- 📦 Installable Progressive Web App (PWA).
- 🍏 iOS and MacOS Apps, maintained by [JGeek00](https://github.com/JGeek00).
- 🍎 iOS Shortcut to save links to Linkwarden.
- 🔑 API keys.
- ✅ Bulk actions.
- ✨ And so many more features!
- 👥 User administration.
- 🌐 Support for Other Languages (i18n).
- 📁 Image and PDF Uploads.
- ✨ And many more features. (Literally!)
## Like what we're doing? Give us a Star ⭐
@@ -98,7 +103,7 @@ We _usually_ go after the [popular suggestions](https://github.com/linkwarden/li
Make sure to check out our [public roadmap](https://github.com/orgs/linkwarden/projects/1).
## Docs
## Documentation
For information on how to get started or to set up your own instance, please visit the [documentation](https://docs.linkwarden.app).
@@ -110,7 +115,7 @@ If you want to contribute, Thanks! Start by checking our [public roadmap](https:
If you found a security vulnerability, please do **not** create a public issue, instead send an email to [security@linkwarden.app](mailto:security@linkwarden.app) stating the vulnerability. Thanks!
## Support
## Support <3
Other than using our official [Cloud](https://linkwarden.app/#pricing) offering, any [donations](https://opencollective.com/linkwarden) are highly appreciated as well!

View File

@@ -80,7 +80,7 @@ export default function CollectionCard({ collection, className }: Props) {
>
<i className="bi-three-dots text-xl" title="More"></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box w-52 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1">
{permissions === true && (
<li>
<div
@@ -90,6 +90,7 @@ export default function CollectionCard({ collection, className }: Props) {
(document?.activeElement as HTMLElement)?.blur();
setEditCollectionModal(true);
}}
className="whitespace-nowrap"
>
{t("edit_collection_info")}
</div>
@@ -103,6 +104,7 @@ export default function CollectionCard({ collection, className }: Props) {
(document?.activeElement as HTMLElement)?.blur();
setEditCollectionSharingModal(true);
}}
className="whitespace-nowrap"
>
{permissions === true
? t("share_and_collaborate")
@@ -117,6 +119,7 @@ export default function CollectionCard({ collection, className }: Props) {
(document?.activeElement as HTMLElement)?.blur();
setDeleteCollectionModal(true);
}}
className="whitespace-nowrap"
>
{permissions === true
? t("delete_collection")

View File

@@ -29,7 +29,7 @@ export default function FilterSearchDropdown({
>
<i className="bi-funnel text-neutral text-2xl"></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box w-56 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<label
className="label cursor-pointer flex justify-start"
@@ -45,7 +45,7 @@ export default function FilterSearchDropdown({
setSearchFilter({ ...searchFilter, name: !searchFilter.name })
}
/>
<span className="label-text">{t("name")}</span>
<span className="label-text whitespace-nowrap">{t("name")}</span>
</label>
</li>
<li>
@@ -63,7 +63,7 @@ export default function FilterSearchDropdown({
setSearchFilter({ ...searchFilter, url: !searchFilter.url })
}
/>
<span className="label-text">{t("link")}</span>
<span className="label-text whitespace-nowrap">{t("link")}</span>
</label>
</li>
<li>
@@ -84,7 +84,9 @@ export default function FilterSearchDropdown({
})
}
/>
<span className="label-text">{t("description")}</span>
<span className="label-text whitespace-nowrap">
{t("description")}
</span>
</label>
</li>
<li>
@@ -102,7 +104,7 @@ export default function FilterSearchDropdown({
setSearchFilter({ ...searchFilter, tags: !searchFilter.tags })
}
/>
<span className="label-text">{t("tags")}</span>
<span className="label-text whitespace-nowrap">{t("tags")}</span>
</label>
</li>
<li>
@@ -123,8 +125,10 @@ export default function FilterSearchDropdown({
})
}
/>
<span className="label-text">{t("full_content")}</span>
<div className="ml-auto badge badge-sm badge-neutral">
<span className="label-text whitespace-nowrap">
{t("full_content")}
</span>
<div className="ml-auto badge badge-sm badge-neutral whitespace-nowrap">
{t("slower")}
</div>
</label>

View File

@@ -86,7 +86,7 @@ export default function LinkActions({
<i title="More" className="bi-three-dots text-xl" />
</div>
<ul
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box w-44 mr-1 ${
className={`dropdown-content z-[20] menu shadow bg-base-200 border border-neutral-content rounded-box mr-1 ${
alignToTop ? "" : "translate-y-10"
}`}
>
@@ -98,6 +98,7 @@ export default function LinkActions({
(document?.activeElement as HTMLElement)?.blur();
pinLink();
}}
className="whitespace-nowrap"
>
{link?.pinnedBy && link.pinnedBy[0]
? t("unpin")
@@ -113,6 +114,7 @@ export default function LinkActions({
(document?.activeElement as HTMLElement)?.blur();
toggleShowInfo();
}}
className="whitespace-nowrap"
>
{!linkInfo ? t("show_link_details") : t("hide_link_details")}
</div>
@@ -127,6 +129,7 @@ export default function LinkActions({
(document?.activeElement as HTMLElement)?.blur();
setEditLinkModal(true);
}}
className="whitespace-nowrap"
>
{t("edit_link")}
</div>
@@ -141,6 +144,7 @@ export default function LinkActions({
(document?.activeElement as HTMLElement)?.blur();
setPreservedFormatsModal(true);
}}
className="whitespace-nowrap"
>
{t("preserved_formats")}
</div>
@@ -171,6 +175,7 @@ export default function LinkActions({
}
: setDeleteLinkModal(true);
}}
className="whitespace-nowrap"
>
{t("delete")}
</div>

View File

@@ -41,7 +41,7 @@ export default function MobileNavigation({}: Props) {
<i className="bi-plus text-5xl pointer-events-none"></i>
</span>
</div>
<ul className="dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box w-40 mb-1 -ml-12">
<ul className="dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box mb-1 -ml-12">
<li>
<div
onClick={() => {
@@ -50,6 +50,7 @@ export default function MobileNavigation({}: Props) {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("new_link")}
</div>
@@ -62,6 +63,7 @@ export default function MobileNavigation({}: Props) {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("upload_file")}
</div>
@@ -74,6 +76,7 @@ export default function MobileNavigation({}: Props) {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("new_collection")}
</div>

View File

@@ -277,7 +277,7 @@ export default function EditCollectionSharingModal({
{roleLabel}
<i className="bi-chevron-down"></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl w-64 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl mt-1">
<li>
<label
className="label cursor-pointer flex justify-start"
@@ -316,10 +316,12 @@ export default function EditCollectionSharingModal({
}}
/>
<div>
<p className="font-bold">
<p className="font-bold whitespace-nowrap">
{t("viewer")}
</p>
<p>{t("viewer_desc")}</p>
<p className="whitespace-nowrap">
{t("viewer_desc")}
</p>
</div>
</label>
</li>
@@ -361,10 +363,12 @@ export default function EditCollectionSharingModal({
}}
/>
<div>
<p className="font-bold">
<p className="font-bold whitespace-nowrap">
{t("contributor")}
</p>
<p>{t("contributor_desc")}</p>
<p className="whitespace-nowrap">
{t("contributor_desc")}
</p>
</div>
</label>
</li>
@@ -406,10 +410,12 @@ export default function EditCollectionSharingModal({
}}
/>
<div>
<p className="font-bold">
<p className="font-bold whitespace-nowrap">
{t("admin")}
</p>
<p>{t("admin_desc")}</p>
<p className="whitespace-nowrap">
{t("admin_desc")}
</p>
</div>
</label>
</li>

View File

@@ -115,7 +115,7 @@ export default function NewTokenModal({ onClose }: Props) {
>
{getLabel(token.expires)}
</Button>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl w-full sm:w-52 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl mt-1">
<li>
<label
className="label cursor-pointer flex justify-start"
@@ -135,7 +135,9 @@ export default function NewTokenModal({ onClose }: Props) {
});
}}
/>
<span className="label-text">{t("7_days")}</span>
<span className="label-text whitespace-nowrap">
{t("7_days")}
</span>
</label>
</li>
<li>
@@ -154,7 +156,9 @@ export default function NewTokenModal({ onClose }: Props) {
setToken({ ...token, expires: TokenExpiry.oneMonth });
}}
/>
<span className="label-text">{t("30_days")}</span>
<span className="label-text whitespace-nowrap">
{t("30_days")}
</span>
</label>
</li>
<li>
@@ -176,7 +180,9 @@ export default function NewTokenModal({ onClose }: Props) {
});
}}
/>
<span className="label-text">{t("60_days")}</span>
<span className="label-text whitespace-nowrap">
{t("60_days")}
</span>
</label>
</li>
<li>
@@ -198,7 +204,9 @@ export default function NewTokenModal({ onClose }: Props) {
});
}}
/>
<span className="label-text">{t("90_days")}</span>
<span className="label-text whitespace-nowrap">
{t("90_days")}
</span>
</label>
</li>
<li>
@@ -217,7 +225,9 @@ export default function NewTokenModal({ onClose }: Props) {
setToken({ ...token, expires: TokenExpiry.never });
}}
/>
<span className="label-text">{t("no_expiration")}</span>
<span className="label-text whitespace-nowrap">
{t("no_expiration")}
</span>
</label>
</li>
</ul>

View File

@@ -66,7 +66,7 @@ export default function Navbar() {
</span>
</div>
</div>
<ul className="dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box w-40 mt-1">
<ul className="dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<div
onClick={() => {
@@ -75,6 +75,7 @@ export default function Navbar() {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("new_link")}
</div>
@@ -87,6 +88,7 @@ export default function Navbar() {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("upload_file")}
</div>
@@ -99,6 +101,7 @@ export default function Navbar() {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("new_collection")}
</div>

View File

@@ -32,9 +32,7 @@ export default function ProfileDropdown() {
/>
</div>
<ul
className={`dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box ${
isAdmin ? "w-48" : "w-40"
} mt-1`}
className={`dropdown-content z-[1] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1`}
>
<li>
<Link
@@ -42,6 +40,7 @@ export default function ProfileDropdown() {
onClick={() => (document?.activeElement as HTMLElement)?.blur()}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("settings")}
</Link>
@@ -54,6 +53,7 @@ export default function ProfileDropdown() {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("switch_to", {
theme: settings.theme === "light" ? t("dark") : t("light"),
@@ -67,6 +67,7 @@ export default function ProfileDropdown() {
onClick={() => (document?.activeElement as HTMLElement)?.blur()}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("server_administration")}
</Link>
@@ -80,6 +81,7 @@ export default function ProfileDropdown() {
}}
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("logout")}
</div>

View File

@@ -14,6 +14,7 @@ export default function SidebarHighlightLink({
return (
<Link href={href}>
<div
title={title}
className={`${
active || false
? "bg-primary/20"
@@ -28,7 +29,7 @@ export default function SidebarHighlightLink({
<i className={`${icon} text-primary text-2xl drop-shadow`}></i>
</div>
<div className={"mt-1"}>
<p className="truncate w-full font-semibold text-sm">{title}</p>
<p className="truncate w-full font-semibold text-xs">{title}</p>
</div>
</div>
</Link>

View File

@@ -27,7 +27,7 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
>
<i className="bi-chevron-expand text-neutral text-2xl"></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl w-52 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-xl mt-1">
<li>
<label
className="label cursor-pointer flex justify-start"
@@ -41,7 +41,9 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.DateNewestFirst}
onChange={() => setSort(Sort.DateNewestFirst)}
/>
<span className="label-text">{t("date_newest_first")}</span>
<span className="label-text whitespace-nowrap">
{t("date_newest_first")}
</span>
</label>
</li>
<li>
@@ -57,7 +59,9 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.DateOldestFirst}
onChange={() => setSort(Sort.DateOldestFirst)}
/>
<span className="label-text">{t("date_oldest_first")}</span>
<span className="label-text whitespace-nowrap">
{t("date_oldest_first")}
</span>
</label>
</li>
<li>
@@ -73,7 +77,7 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.NameAZ}
onChange={() => setSort(Sort.NameAZ)}
/>
<span className="label-text">{t("name_az")}</span>
<span className="label-text whitespace-nowrap">{t("name_az")}</span>
</label>
</li>
<li>
@@ -89,7 +93,7 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.NameZA}
onChange={() => setSort(Sort.NameZA)}
/>
<span className="label-text">{t("name_za")}</span>
<span className="label-text whitespace-nowrap">{t("name_za")}</span>
</label>
</li>
<li>
@@ -105,7 +109,9 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.DescriptionAZ}
onChange={() => setSort(Sort.DescriptionAZ)}
/>
<span className="label-text">{t("description_az")}</span>
<span className="label-text whitespace-nowrap">
{t("description_az")}
</span>
</label>
</li>
<li>
@@ -121,7 +127,9 @@ export default function SortDropdown({ sortBy, setSort, t }: Props) {
checked={sortBy === Sort.DescriptionZA}
onChange={() => setSort(Sort.DescriptionZA)}
/>
<span className="label-text">{t("description_za")}</span>
<span className="label-text whitespace-nowrap">
{t("description_za")}
</span>
</label>
</li>
</ul>

View File

@@ -398,14 +398,13 @@ const useBulkEditLinks = () => {
return data.response;
},
onSuccess: (data, { links, newData, removePreviousTags }) => {
queryClient.setQueryData(["dashboardData"], (oldData: any) => {
if (!oldData) return undefined;
return oldData.map((e: any) =>
data.find((d: any) => d.id === e.id) ? data : e
);
});
// TODO: Fix this
// TODO: Fix these
// queryClient.setQueryData(["dashboardData"], (oldData: any) => {
// if (!oldData) return undefined;
// return oldData.map((e: any) =>
// data.find((d: any) => d.id === e.id) ? data : e
// );
// });
// queryClient.setQueriesData({ queryKey: ["links"] }, (oldData: any) => {
// if (!oldData) return undefined;
// return {
@@ -417,6 +416,7 @@ const useBulkEditLinks = () => {
// };
// });
queryClient.invalidateQueries({ queryKey: ["links"] }); // Temporary workaround
queryClient.invalidateQueries({ queryKey: ["dashboardData"] }); // Temporary workaround
queryClient.invalidateQueries({ queryKey: ["collections"] });
queryClient.invalidateQueries({ queryKey: ["tags"] });

View File

@@ -86,9 +86,11 @@ export default async function postLink(
else if (contentType === "image/png") imageExtension = "png";
}
if (!link.tags) link.tags = [];
const newLink = await prisma.link.create({
data: {
url: link.url?.trim().replace(/\/+$/, "") || null,
url: link.url?.trim() || null,
name,
description: link.description,
type: linkType,
@@ -98,7 +100,7 @@ export default async function postLink(
},
},
tags: {
connectOrCreate: link.tags.map((tag) => ({
connectOrCreate: link.tags?.map((tag) => ({
where: {
name_ownerId: {
name: tag.name.trim(),

View File

@@ -63,7 +63,8 @@ async function processBookmarks(
) as Element;
if (collectionName) {
const collectionNameContent = (collectionName.children[0] as TextNode)?.content;
const collectionNameContent = (collectionName.children[0] as TextNode)
?.content;
if (collectionNameContent) {
collectionId = await createCollection(
userId,
@@ -274,4 +275,3 @@ function processNodes(nodes: Node[]) {
nodes.forEach(findAndProcessDL);
return nodes;
}

View File

@@ -49,12 +49,18 @@ export default async function postUser(
// Check username (if email was disabled)
const checkUsername = RegExp("^[a-z0-9_-]{3,31}$");
if (!emailEnabled && !checkUsername.test(body.username?.toLowerCase() || ""))
const autoGeneratedUsername = "user" + Math.round(Math.random() * 1000000000);
if (body.username && !checkUsername.test(body.username?.toLowerCase()))
return {
response:
"Username has to be between 3-30 characters, no spaces and special characters are allowed.",
status: 400,
};
else if (!body.username) {
body.username = autoGeneratedUsername;
}
const checkIfUserExists = await prisma.user.findFirst({
where: {
@@ -89,7 +95,8 @@ export default async function postUser(
data: {
name: body.name,
username: emailEnabled
? autoGeneratedUsername
? (body.username as string).toLowerCase().trim() ||
autoGeneratedUsername
: (body.username as string).toLowerCase().trim(),
email: emailEnabled ? body.email?.toLowerCase().trim() : undefined,
password: hashedPassword,

View File

@@ -24,7 +24,12 @@ const generatePreview = async (
1024 * 1024 * Number(process.env.PREVIEW_MAX_BUFFER || 0.1)
) {
console.log("Error generating preview: Buffer size exceeded");
return;
return prisma.link.update({
where: { id: linkId },
data: {
preview: "unavailable",
},
});
}
await createFile({

View File

@@ -1,17 +1,10 @@
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import { Dispatch, SetStateAction } from "react";
const getPublicCollectionData = async (
collectionId: number,
setData: Dispatch<
SetStateAction<CollectionIncludingMembersAndLinkCount | undefined>
>
) => {
const getPublicCollectionData = async (collectionId: number) => {
const res = await fetch("/api/v1/public/collections/" + collectionId);
const data = await res.json();
if (res.status === 400)
return { response: "Collection not found.", status: 400 };
setData(data.response);
const data = await res.json();
return data;
};

View File

@@ -2,7 +2,7 @@
module.exports = {
i18n: {
defaultLocale: "en",
locales: ["en","it"],
locales: ["en", "it", "fr", "zh"],
},
reloadOnPrerender: process.env.NODE_ENV === "development",
};

View File

@@ -1,6 +1,6 @@
{
"name": "linkwarden",
"version": "v2.6.2",
"version": "v2.7.0",
"main": "index.js",
"repository": "https://github.com/linkwarden/linkwarden.git",
"author": "Daniel31X13 <daniel31x13@gmail.com>",

View File

@@ -1186,7 +1186,7 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
providerAccountId: account?.providerAccountId,
},
});
if (existingUser && newSsoUsersDisabled) {
if (!existingUser && newSsoUsersDisabled) {
return false;
}
}

View File

@@ -134,7 +134,7 @@ export default function Index() {
>
<i className="bi-three-dots text-xl" title="More"></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box w-52 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1">
{permissions === true && (
<li>
<div
@@ -144,6 +144,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
setEditCollectionModal(true);
}}
className="whitespace-nowrap"
>
{t("edit_collection_info")}
</div>
@@ -157,6 +158,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
setEditCollectionSharingModal(true);
}}
className="whitespace-nowrap"
>
{permissions === true
? t("share_and_collaborate")
@@ -172,6 +174,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
setNewCollectionModal(true);
}}
className="whitespace-nowrap"
>
{t("create_subcollection")}
</div>
@@ -185,6 +188,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
setDeleteCollectionModal(true);
}}
className="whitespace-nowrap"
>
{permissions === true
? t("delete_collection")

View File

@@ -43,6 +43,12 @@ export default function EmailConfirmaion() {
<div className="divider my-3"></div>
{router.query.email && typeof router.query.email === "string" && (
<p className="text-center font-bold mb-3 break-all">
{decodeURIComponent(router.query.email)}
</p>
)}
<p>{t("verification_email_sent_desc")}</p>
<div className="mx-auto w-fit mt-3">

View File

@@ -214,13 +214,14 @@ export default function Dashboard() {
<i className="bi-cloud-upload text-xl duration-100"></i>
<p>{t("import_links")}</p>
</div>
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1 w-60">
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<label
tabIndex={0}
role="button"
htmlFor="import-linkwarden-file"
title={t("from_linkwarden")}
className="whitespace-nowrap"
>
{t("from_linkwarden")}
<input
@@ -241,6 +242,7 @@ export default function Dashboard() {
role="button"
htmlFor="import-html-file"
title={t("from_html")}
className="whitespace-nowrap"
>
{t("from_html")}
<input
@@ -261,6 +263,7 @@ export default function Dashboard() {
role="button"
htmlFor="import-wallabag-file"
title={t("from_wallabag")}
className="whitespace-nowrap"
>
{t("from_wallabag")}
<input

View File

@@ -51,6 +51,9 @@ export default function Index() {
/>
</LinkListOptions>
{!data.isLoading && links && !links[0] && (
<NoLinksFound text={t("you_have_not_added_any_links")} />
)}
<Links
editMode={editMode}
links={links}
@@ -58,9 +61,6 @@ export default function Index() {
placeholderCount={1}
useData={data}
/>
{!data.isLoading && links && !links[0] && (
<NoLinksFound text={t("you_have_not_added_any_links")} />
)}
</div>
</MainLayout>
);

View File

@@ -46,13 +46,6 @@ export default function PinnedLinks() {
/>
</LinkListOptions>
<Links
editMode={editMode}
links={links}
layout={viewMode}
placeholderCount={1}
useData={data}
/>
{!data.isLoading && links && !links[0] && (
<div
style={{ flex: "1 1 auto" }}
@@ -74,6 +67,13 @@ export default function PinnedLinks() {
</p>
</div>
)}
<Links
editMode={editMode}
links={links}
layout={viewMode}
placeholderCount={1}
useData={data}
/>
</div>
</MainLayout>
);

View File

@@ -19,7 +19,6 @@ import EditCollectionSharingModal from "@/components/ModalContent/EditCollection
import { useTranslation } from "next-i18next";
import getServerSideProps from "@/lib/client/getServerSideProps";
import LinkListOptions from "@/components/LinkListOptions";
import { useCollections } from "@/hooks/store/collections";
import { usePublicLinks } from "@/hooks/store/publicLinks";
import Links from "@/components/LinkViews/Links";
@@ -28,8 +27,6 @@ export default function PublicCollections() {
const { settings } = useLocalSettingsStore();
const { data: collections = [] } = useCollections();
const router = useRouter();
const [collectionOwner, setCollectionOwner] = useState({
@@ -71,19 +68,22 @@ export default function PublicCollections() {
useEffect(() => {
if (router.query.id) {
getPublicCollectionData(Number(router.query.id), setCollection);
getPublicCollectionData(Number(router.query.id)).then((res) => {
if (res.status === 400) {
router.push("/dashboard");
} else {
setCollection(res.response);
}
});
}
}, [collections]);
}, []);
useEffect(() => {
const fetchOwner = async () => {
if (collection) {
const owner = await getPublicUserData(collection.ownerId as number);
setCollectionOwner(owner);
}
};
fetchOwner();
if (collection) {
getPublicUserData(collection.ownerId as number).then((owner) =>
setCollectionOwner(owner)
);
}
}, [collection]);
const [editCollectionSharingModal, setEditCollectionSharingModal] =
@@ -230,9 +230,7 @@ export default function PublicCollections() {
placeholderCount={1}
useData={data}
/>
{!data.isLoading && links && !links[0] && (
<p>{t("collection_is_empty")}</p>
)}
{!data.isLoading && links && !links[0] && <p>{t("nothing_found")}</p>}
{/* <p className="text-center text-neutral">
List created with <span className="text-black">Linkwarden.</span>

View File

@@ -73,27 +73,7 @@ export default function Search() {
<PageHeader icon={"bi-search"} title={"Search Results"} />
</LinkListOptions>
{/* {
!isLoading &&
!links[0] ? (
<p>{t("nothing_found")}</p>
) : links[0] ? (
<LinkComponent
editMode={editMode}
links={links}
isLoading={isLoading}
/>
) : (
isLoading && (
<GridLoader
color="oklch(var(--p))"
loading={true}
size={20}
className="m-auto py-10"
/>
)
)} */}
{!data.isLoading && links && !links[0] && <p>{t("nothing_found")}</p>}
<Links
editMode={editMode}
links={links}

View File

@@ -210,7 +210,7 @@ export default function Account() {
className="select border border-neutral-content focus:outline-none focus:border-primary duration-100 w-full bg-base-200 rounded-[0.375rem] min-h-0 h-[2.625rem] leading-4 p-2"
>
{i18n.locales.map((locale) => (
<option key={locale} value={locale}>
<option key={locale} value={locale} className="capitalize">
{new Intl.DisplayNames(locale, { type: "language" }).of(
locale
) || ""}
@@ -242,9 +242,13 @@ export default function Account() {
<i className="bi-pencil-square text-md duration-100"></i>
{t("edit")}
</Button>
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1 w-60">
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<label tabIndex={0} role="button">
<label
tabIndex={0}
role="button"
className="whitespace-nowrap"
>
{t("upload_new_photo")}
<input
type="file"
@@ -267,6 +271,7 @@ export default function Account() {
image: "",
})
}
className="whitespace-nowrap"
>
{t("remove_photo")}
</div>
@@ -341,13 +346,14 @@ export default function Account() {
{t("import_links")}
</Button>
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1 w-60">
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<label
tabIndex={0}
role="button"
htmlFor="import-linkwarden-file"
title={t("from_linkwarden")}
className="whitespace-nowrap"
>
{t("from_linkwarden")}
<input
@@ -368,6 +374,7 @@ export default function Account() {
role="button"
htmlFor="import-html-file"
title={t("from_html")}
className="whitespace-nowrap"
>
{t("from_html")}
<input
@@ -388,6 +395,7 @@ export default function Account() {
role="button"
htmlFor="import-wallabag-file"
title={t("from_wallabag")}
className="whitespace-nowrap"
>
{t("from_wallabag")}
<input

View File

@@ -194,7 +194,7 @@ export default function Index() {
className={"bi-three-dots text-neutral text-2xl"}
></i>
</div>
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box w-36 mt-1">
<ul className="dropdown-content z-[30] menu shadow bg-base-200 border border-neutral-content rounded-box mt-1">
<li>
<div
role="button"
@@ -203,6 +203,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
setRenameTag(true);
}}
className="whitespace-nowrap"
>
{t("rename_tag")}
</div>
@@ -215,6 +216,7 @@ export default function Index() {
(document?.activeElement as HTMLElement)?.blur();
remove();
}}
className="whitespace-nowrap"
>
{t("delete_tag")}
</div>

View File

@@ -0,0 +1,374 @@
{
"user_administration": "Administration des utilisateurs",
"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",
"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.",
"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",
"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",
"sign_in_to_your_account": "Connectez-vous à votre compte",
"dashboard_desc": "Un bref aperçu de vos données",
"link": "Lien",
"links": "Liens",
"collection": "Collection",
"collections": "Collections",
"tag": "Étiquette",
"tags": "Étiquettes",
"recent": "Récent",
"recent_links_desc": "Liens récemment ajoutés",
"view_all": "Tout voir",
"view_added_links_here": "Consultez vos liens récemment ajoutés ici !",
"view_added_links_here_desc": "Cette section affichera vos derniers liens ajoutés dans toutes les collections auxquelles vous avez accès.",
"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_wallabag": "À partir de Wallabag (fichier JSON)",
"pinned": "Épinglé",
"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.",
"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éé!",
"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.",
"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?",
"get_in_touch": "Prenez contact avec nous",
"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é",
"nothing_selected": "Rien de sélectionné",
"edit": "Modifier",
"delete": "Supprimer",
"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.",
"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",
"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",
"name": "Nom",
"created_success": "Créé!",
"created": "Créé",
"expires": "Expire",
"accountSettings": "Paramètres du compte",
"language": "Langue",
"profile_photo": "Photo de profil",
"upload_new_photo": "Envoyer une nouvelle photo...",
"remove_photo": "Supprimer la photo",
"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_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.",
"download_data": "Téléchargez vos données instantanément.",
"export_data": "Exporter des données",
"delete_account": "Supprimer le compte",
"delete_account_warning": "Cela supprimera définitivement TOUS les liens, collections, balises et données archivées que vous possédez.",
"cancel_subscription_notice": "Cela annulera également votre abonnement.",
"account_deletion_page": "Page de suppression de compte",
"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_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!",
"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 :",
"fill_required_fields": "Veuillez remplir les champs requis.",
"deleting_message": "Tout supprimer, 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",
"please_specify": "Veuillez préciser",
"customer_service": "Service client",
"low_quality": "Qualité médiocre",
"missing_features": "Fonctionnalités manquantes",
"switched_service": "Service commuté",
"too_complex": "Trop compliqué",
"too_expensive": "Trop cher",
"unused": "Inutilisé",
"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...",
"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.",
"applying_changes": "Appliquer...",
"password_change_instructions": "Pour modifier votre mot de passe, veuillez remplir ce qui suit. Votre mot de passe doit comporter au moins 8 caractères.",
"old_password": "Ancien mot de passe",
"new_password": "Nouveau mot de passe",
"preference": "Préférence",
"select_theme": "Sélectionner un thème",
"dark": "Sombre",
"light": "Clair",
"archive_settings": "Paramètres d'archivage",
"formats_to_archive": "Formats pour archiver/préserver les pages Web:",
"screenshot": "Capture d'écran",
"pdf": "PDF",
"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_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",
"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",
"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",
"collections_you_own": "Collections que vous possédez",
"new_collection": "Nouvelle collection",
"other_collections": "Autres collections",
"other_collections_desc": "Collections partagées dont vous êtes membre",
"showing_count_results": "Afficher {{count}} résultats",
"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",
"create_subcollection": "Créer une sous-collection",
"delete_collection": "Supprimer la collection",
"leave_collection": "Quitter la collection",
"email_verified_signing_out": "Courriel vérifié. Déconnexion...",
"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!",
"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à.",
"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)",
"name_az": "Nom (A-Z)",
"name_za": "Nom (Z-A)",
"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_tags": "Vous navez pas détiquettes...",
"cant_change_collection_you_dont_own": "Vous ne pouvez pas modifier une collection dont vous n'êtes pas propriétaire.",
"account": "Compte",
"billing": "Facturation",
"linkwarden_version": "Linkwarden {{version}}",
"help": "Aide",
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"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",
"settings": "Paramètres",
"switch_to": "Basculer vers {{theme}}",
"logout": "Déconnexion",
"start_journey": "Commencez votre voyage 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>",
"full_content": "Contenu complet",
"slower": "Plus lent",
"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)",
"description": "Description",
"auto_generated": "Sera généré automatiquement si rien n'est fourni.",
"example_link": "Par exemple: Exemple de lien",
"hide": "Cacher",
"more": "Plus",
"options": "Options",
"description_placeholder": "Notes, réflexions, etc.",
"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": "Révoquer",
"sending_request": "Envoi de la demande...",
"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",
"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éé!",
"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..",
"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.",
"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",
"expires_in": "Expire dans",
"token_name_placeholder": "Par exemple: Pour le raccourci iOS",
"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/",
"link_description_placeholder": "Notes, réflexions, etc.",
"more_options": "Plus d'options",
"hide_options": "Cacher les options",
"create_link": "Créer un lien",
"new_sub_collection": "Nouvelle sous-collection",
"for_collection": "Pour {{name}}",
"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...",
"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.",
"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",
"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.",
"sharable_link_guide": "Lien partageable (Cliquez pour copier)",
"copied": "Copié!",
"members": "Membres",
"members_username_placeholder": "Nom d'utilisateur (sans le '@')",
"owner": "Propriétaire",
"admin": "Administrateur",
"contributor": "Contributeur",
"viewer": "Visualiseur",
"viewer_desc": "Accès en lecture seule",
"contributor_desc": "Peut consulter et créer des liens",
"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...",
"deleting_user": "Suppression...",
"user_deleted": "Utilisateur supprimé.",
"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",
"delete_link": "Supprimer le lien",
"deleted": "Supprimé.",
"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.",
"deleting_collection": "Suppression...",
"collection_deleted": "Collection supprimée.",
"confirm_deletion_prompt": "Pour confirmer, tapez \"{{name}}\" dans le champ ci-dessous:",
"type_name_placeholder": "Tapez \"{{name}}\" ici.",
"deletion_warning": "La suppression de cette collection effacera définitivement tout son contenu et deviendra inaccessible à tous, y compris aux membres qui y ont déjà eu accès.",
"leave_prompt": "Cliquez sur le bouton ci-dessous pour quitter la collection actuelle.",
"leave": "Quitter",
"edit_links": "Éditer {{count}} liens",
"move_to_collection": "Déplacer vers la collection",
"add_tags": "Ajouter des étiquettes",
"remove_previous_tags": "Supprimer les étiquettes précédentes",
"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.",
"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",
"server_administration": "Administration du serveur",
"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"
}

View File

@@ -0,0 +1,374 @@
{
"user_administration": "用户管理",
"search_users": "搜索用户",
"no_users_found": "未找到用户。",
"no_user_found_in_search": "在给定的搜索查询中未找到用户。",
"username": "用户名",
"email": "邮箱",
"subscribed": "订阅",
"created_at": "创建时间",
"not_available": "N/A",
"check_your_email": "检查你的邮箱",
"authenticating": "身份验证中...",
"verification_email_sent": "发送验证邮件。",
"verification_email_sent_desc": "登录链接已发送到您的邮箱。如果没有看到邮件,请检查你的垃圾邮件文件夹。",
"resend_email": "重新发送邮件",
"invalid_credentials": "无效凭证",
"fill_all_fields": "请填写所有字段",
"enter_credentials": "账号登录",
"username_or_email": "用户名或邮箱",
"password": "密码",
"confirm_password": "确认密码",
"forgot_password": "忘记密码?",
"login": "登录",
"or_continue_with": "第三方登录",
"new_here": "新用户?",
"sign_up": "注册",
"sign_in_to_your_account": "登录你的账户",
"dashboard_desc": "你的数据概览",
"link": "链接",
"links": "链接",
"collection": "收藏夹",
"collections": "收藏夹",
"tag": "标签",
"tags": "标签",
"recent": "最近",
"recent_links_desc": "最近添加的链接",
"view_all": "查看全部",
"view_added_links_here": "查看最近添加的链接!",
"view_added_links_here_desc": "此部分将查看您有权访问的每个收藏夹中最新添加的链接。",
"add_link": "添加新链接",
"import_links": "导入链接",
"from_linkwarden": "从 Linkwarden",
"from_html": "从书签 HTML 文件",
"from_wallabag": "从 WallabagJSON 文件)",
"pinned": "置顶",
"pinned_links_desc": "你置顶的链接",
"pin_favorite_links_here": "在这里置顶您最喜欢的链接!",
"pin_favorite_links_here_desc": "您可以通过点击每个链接上的三个点,然后点击“置顶到仪表板”来置顶您最喜欢的链接。",
"sending_password_link": "发送重置密码链接",
"password_email_prompt": "输入你的邮箱,我们会给你发送一个重置密码的链接。",
"send_reset_link": "发送重置链接",
"reset_email_sent_desc": "检查你的邮箱以获取重置密码的链接。如果它在几分钟内没有出现,请检查你的垃圾邮件文件夹。",
"back_to_login": "返回登录",
"email_sent": "邮件已发送!",
"passwords_mismatch": "密码错误。",
"password_too_short": "密码需至少 8 个字符。",
"creating_account": "正在创建账户...",
"account_created": "创建账户成功!",
"trial_offer_desc": "免费解锁 {{count}} 天的高级服务!",
"register_desc": "创建新的账户",
"registration_disabled_desc": "此实例已禁用注册,如有任何问题,请与管理员联系。",
"enter_details": "输入您的详细信息",
"display_name": "昵称",
"sign_up_agreement": "注册即表示您同意遵守我们的 <0>服务条款</0> 和 <1>隐私政策</1>。",
"need_help": "需要帮助吗?",
"get_in_touch": "保持联系",
"already_registered": "已有账户?",
"deleting_selections": "正在删除选中的链接...",
"links_deleted": "{{count}} 个链接已删除。",
"link_deleted": "1 个链接已删除。",
"links_selected": "{{count}} 个链接已选中。",
"link_selected": "1 个链接已选中。",
"nothing_selected": "未选中任何内容",
"edit": "编辑",
"delete": "删除",
"nothing_found": "沒有找到任何内容。",
"redirecting_to_stripe": "重定向到 Stripe...",
"subscribe_title": "订阅 Linkwarden",
"subscribe_desc": "你将跳转到 Stripe如有任何问题请随时联系我们 <0>support@linkwarden.app</0> 。",
"monthly": "每月",
"yearly": "每年",
"discount_percent": "{{percent}}% 折扣",
"billed_monthly": "月付",
"billed_yearly": "年付",
"total": "总计",
"total_annual_desc": "{{count}}-天免费体验,然后 ${{annualPrice}} 每年",
"total_monthly_desc": "{{count}}-天免费体验,然后 ${{monthlyPrice}} 每月",
"plus_tax": "+增值税(如适用)",
"complete_subscription": "完成订阅",
"sign_out": "退出",
"access_tokens": "Access Tokens",
"access_tokens_description": "Access Tokens 可用于从其他应用程序和服务访问 Linkwarden而无需泄露您的用户名和密码。",
"new_token": "新 Access Token",
"name": "名称",
"created_success": "创建成功!",
"created": "已创建",
"expires": "过期",
"accountSettings": "账户设置",
"language": "语言",
"profile_photo": "头像",
"upload_new_photo": "上传新头像",
"remove_photo": "移除照片",
"make_profile_private": "将个人资料设为私密",
"profile_privacy_info": "这将限制谁可以找到你并将你添加到新收藏夹。",
"whitelisted_users": "白名单用户",
"whitelisted_users_info": "请提供您希望允许查看您个人资料的用户的用户名,以逗号分隔。",
"whitelisted_users_placeholder": "你的资料现在对所有人都是隐藏的。",
"save_changes": "保存修改",
"import_export": "导入 & 导出",
"import_data": "从其它平台导入",
"download_data": "下载数据",
"export_data": "导出数据",
"delete_account": "删除账户",
"delete_account_warning": "这将永久删除您拥有的所有链接、收藏夹、标签和存档数据。",
"cancel_subscription_notice": "它将取消你的订阅。",
"account_deletion_page": "账户删除页面",
"applying_settings": "应用设置中...",
"settings_applied": "已应用设置!",
"email_change_request": "已发送电子邮件更改请求。请确认新的邮箱地址。",
"image_upload_size_error": "请选择小于 1 MB的 PNG 或 JPEG 文件。",
"image_upload_format_error": "无效的文件格式。",
"importing_bookmarks": "正在导入书签...",
"import_success": "已导入书签!请刷新页面。",
"more_coming_soon": "即将推出...",
"billing_settings": "账单设置",
"manage_subscription_intro": "要管理/取消订阅,请访问",
"billing_portal": "账单管理",
"help_contact_intro": "如果您仍然需要帮助或遇到任何问题,请随时与我们联系:",
"fill_required_fields": "请填写必填字段。",
"deleting_message": "删除所有内容,请稍等...",
"delete_warning": "这将永久删除您拥有的所有链接、收藏夹、标签和存档数据。它还将注销您的账户。此操作不可逆!",
"optional": "可选",
"feedback_help": "(但它确实能帮助我们改善!)",
"reason_for_cancellation": "取消原因",
"please_specify": "请指定",
"customer_service": "客户服务",
"low_quality": "低质量",
"missing_features": "缺失功能",
"switched_service": "选择服务",
"too_complex": "太复杂",
"too_expensive": "太昂贵",
"unused": "从未使用",
"other": "其它",
"more_information": "更多信息(越详细,越有帮助)",
"feedback_placeholder": "例如,我需要一个功能...",
"delete_your_account": "删除账户",
"change_password": "修改密码",
"password_length_error": "密码需至少 8 个字符。",
"applying_changes": "正在申请...",
"password_change_instructions": "要更改您的密码,请填写以下内容。您的密码至少应为 8 个字符。",
"old_password": "旧密码",
"new_password": "新密码",
"preference": "首选项",
"select_theme": "选择主题",
"dark": "Dark",
"light": "Light",
"archive_settings": "存档设置",
"formats_to_archive": "存档/保存网页格式:",
"screenshot": "截图",
"pdf": "PDF",
"archive_org_snapshot": "Archive.org 快照",
"link_settings": "链接设置",
"prevent_duplicate_links": "防止重复链接",
"clicking_on_links_should": "点击链接:",
"open_original_content": "打开原始内容",
"open_pdf_if_available": "打开PDF如果有的话",
"open_readable_if_available": "打开可读视图(如果可用)",
"open_screenshot_if_available": "打开屏幕截图,如果有的话",
"open_webpage_if_available": "打开网页副本,如果有的话",
"tag_renamed": "标签已重命名!",
"tag_deleted": "标签已删除!",
"rename_tag": "重命名标签",
"delete_tag": "删除标签",
"list_created_with_linkwarden": "使用 Linkwarden 创建的列表",
"by_author": "作者 {{author}}",
"by_author_and_other": "作者:{{author}} 和 {{count}} 其他人",
"by_author_and_others": "作者: {{author}} 和 {{count}} 其他人",
"search_count_link": "搜索 {{count}} 个链接",
"search_count_links": "搜索 {{count}} 个链接",
"collection_is_empty": "此收藏夹为空",
"all_links": "全部链接",
"all_links_desc": "所有收藏夹的链接",
"you_have_not_added_any_links": "你还没有创建任何链接",
"collections_you_own": "你的收藏夹",
"new_collection": "新收藏夹",
"other_collections": "其它收藏夹",
"other_collections_desc": "您所属的共享收藏夹",
"showing_count_results": "展示 {{count}} 条结果",
"showing_count_result": "展示 {{count}} 条结果",
"edit_collection_info": "编辑收藏夹信息",
"share_and_collaborate": "共享和协作",
"view_team": "查看团队",
"team": "团队",
"create_subcollection": "创建子收藏夹",
"delete_collection": "删除收藏夹",
"leave_collection": "离开收藏夹",
"email_verified_signing_out": "邮箱已验证。正在退出...",
"invalid_token": "无效 token",
"sending_password_recovery_link": "发送密码重置链接...",
"please_fill_all_fields": "请填写所有字段。",
"password_updated": "密码已修改!",
"reset_password": "重置密码",
"enter_email_for_new_password": "输入您的邮箱,以便我们向您发送创建新密码的链接。",
"update_password": "更新密码",
"password_successfully_updated": "您的密码已成功更新。",
"user_already_member": "用户已存在。",
"you_are_already_collection_owner": "您已经是收藏夹的所有者。",
"date_newest_first": "日期 (最新)",
"date_oldest_first": "日期 (最早)",
"name_az": "名字 (A-Z)",
"name_za": "名字 (Z-A)",
"description_az": "描述 (A-Z)",
"description_za": "描述 (Z-A)",
"all_rights_reserved": "© {{date}} <0>Linkwarden</0> 版权所有",
"you_have_no_collections": "你还没有收藏夹...",
"you_have_no_tags": "你还没有标签...",
"cant_change_collection_you_dont_own": "您无法对不属于您的收藏夹进行更改。",
"account": "账户",
"billing": "帐单",
"linkwarden_version": "Linkwarden {{version}}",
"help": "帮助",
"github": "GitHub",
"twitter": "Twitter",
"mastodon": "Mastodon",
"link_preservation_in_queue": "链接保存正在处理中...",
"check_back_later": "请稍后再查看结果",
"there_are_more_formats": "队列中有更多已保存的格式。",
"settings": "设置",
"switch_to": "切换到 {{theme}}",
"logout": "注销",
"start_journey": "通过创建一个新的链接开始你的旅程!",
"create_new_link": "创建新链接",
"new_link": "新链接",
"create_new": "新建",
"pwa_install_prompt": "安装 Linkwarden 到您的主屏幕,以获得更快的访问和增强的体验。 <0>了解更多</0>",
"full_content": "完整内容",
"slower": "较慢",
"new_version_announcement": "查看 <0>Linkwarden {{version}}</0> 了解新功能。",
"creating": "正在创建...",
"upload_file": "上传文件",
"file": "文件",
"file_types": "PDFPNGJPG (不超过 {{size}} MB",
"description": "描述",
"auto_generated": "若未提供将自动生成。",
"example_link": "例如:示例链接",
"hide": "隐藏",
"more": "更多",
"options": "选项",
"description_placeholder": "笔记,想法等等。",
"deleting": "正在删除...",
"token_revoked": "Token 已撤销。",
"revoke_token": "撤销 Token",
"revoke_confirmation": "确定要撤销此 Access Token 吗?任何使用此 token 的应用或服务将无法再使用它访问 Linkwarden。",
"revoke": "撤销",
"sending_request": "正在发送请求...",
"link_being_archived": "链接正在归档...",
"preserved_formats": "保存格式",
"available_formats": "以下格式可用于此链接:",
"readable": "可读视图",
"preservation_in_queue": "链接保存正在处理中...",
"view_latest_snapshot": "在 archive.org 上查看最新快照",
"refresh_preserved_formats": "刷新保留格式",
"this_deletes_current_preservations": "这将删除当前保存",
"create_new_user": "创建新用户",
"placeholder_johnny": "Johnny",
"placeholder_email": "johnny@example.com",
"placeholder_john": "john",
"user_created": "用户已创建",
"fill_all_fields_error": "请填写所有的字段。",
"password_change_note": "<0>注意:</0> 请务必通知用户他们需要更改密码。",
"create_user": "创建用户",
"creating_token": "创建 Token 中...",
"token_created": "Token 创建完成",
"access_token_created": "Access Token 已创建",
"token_creation_notice": "你的新 token 已创建。 请复制并保存在安全的地方。您将无法再看到它。",
"copied_to_clipboard": "已复制到剪贴板",
"copy_to_clipboard": "复制到剪贴板",
"create_access_token": "创建一个 Access Token",
"expires_in": "有效期:",
"token_name_placeholder": "例如iOS 快捷方式",
"create_token": "创建 Access Token",
"7_days": "7 天",
"30_days": "30 天",
"60_days": "60 天",
"90_days": "90 天",
"no_expiration": "永久",
"creating_link": "正在创建链接...",
"link_created": "链接已创建!",
"link_name_placeholder": "若留空将自动生成。",
"link_url_placeholder": "示例: http://example.com/",
"link_description_placeholder": "笔记,想法等等。",
"more_options": "更多选项",
"hide_options": "隐藏选项",
"create_link": "创建链接",
"new_sub_collection": "新子收藏夹",
"for_collection": "对于 {{name}}",
"create_new_collection": "创建新收藏夹",
"color": "颜色",
"reset": "重置",
"collection_name_placeholder": "例如:示例收藏夹",
"collection_description_placeholder": "该收藏夹用于...",
"create_collection_button": "创建收藏夹",
"password_change_warning": "更改邮箱前,请确认密码。",
"stripe_update_note": " 更新此字段也会更改 Stripe 上的账单邮箱。",
"sso_will_be_removed_warning": "如果您更改电子邮件地址,任何现有的 {{service}} SSO 连接都将被删除。",
"old_email": "旧邮箱",
"new_email": "新邮箱",
"confirm": "确认",
"edit_link": "编辑链接",
"updating": "正在更新...",
"updated": "已更新",
"placeholder_example_link": "例如:示例链接",
"make_collection_public": "公开收藏夹",
"make_collection_public_checkbox": "将其设为公开收藏夹",
"make_collection_public_desc": "这将允许任何人查看该收藏夹及其用户。",
"sharable_link_guide": "可分享的链接(点击复制)",
"copied": "已复制",
"members": "成员",
"members_username_placeholder": "用户名(不带'@'",
"owner": "所有者",
"admin": "管理员",
"contributor": "Contributor",
"viewer": "Viewer",
"viewer_desc": "只读访问",
"contributor_desc": "可以查看和创建链接",
"admin_desc": "完全访问所有链接",
"remove_member": "移除成员",
"placeholder_example_collection": "例如:示例收藏夹",
"placeholder_collection_purpose": "此收藏夹的目的...",
"deleting_user": "正在删除用户...",
"user_deleted": "用户已删除",
"delete_user": "删除用户",
"confirm_user_deletion": "确定删除该用户吗?",
"irreversible_action_warning": "此操作不可逆!",
"delete_confirmation": "删除,我知道我在做什么。",
"delete_link": "删除链接",
"deleted": "已删除",
"link_deletion_confirmation_message": "要删除此链接吗?",
"warning": "警告",
"irreversible_warning": "此操作不可逆!",
"shift_key_tip": "按住 Shift 键并单击“删除”即可在以后绕过此确认。",
"deleting_collection": "删除中...",
"collection_deleted": "收藏夹已删除。",
"confirm_deletion_prompt": "请确认, 在下面的输入框中输入 \"{{name}}\" :",
"type_name_placeholder": "在此输入 \"{{name}}\" 。",
"deletion_warning": "删除此收藏夹将永久清除其所有内容,并且所有人都将无法访问,包括之前具有访问权限的成员。",
"leave_prompt": "单击下面的按钮离开当前收藏夹。",
"leave": "离开",
"edit_links": "编辑 {{count}} 链接",
"move_to_collection": "移至收藏夹",
"add_tags": "添加标签",
"remove_previous_tags": "删除以前的标签",
"delete_links": "删除 {{count}} 个链接",
"links_deletion_confirmation_message": "确定要删除 {{count}} 个链接吗?",
"warning_irreversible": "警告:此操作不可逆!",
"shift_key_instruction": "按住“Shift”键并单击“删除”即可在以后绕过此确认。",
"link_selection_error": "您无权编辑或删除此项目。",
"no_description": "未提供描述",
"applying": "正在申请...",
"unpin": "取消置顶",
"pin_to_dashboard": "置顶到仪表盘",
"show_link_details": "显示链接详细信息",
"hide_link_details": "隐藏链接详细信息",
"link_pinned": "链接已置顶!",
"link_unpinned": "链接已取消置顶!",
"webpage": "网页",
"server_administration": "后台管理",
"all_collections": "全部收藏夹",
"dashboard": "仪表盘",
"demo_title": "仅限演示",
"demo_desc": "这只是 Linkwarden 的演示实例,禁止上传文件。",
"demo_desc_2": "如果你想尝试完整版,你可以注册免费试用:",
"demo_button": "以演示用户登录"
}