mirror of
https://github.com/linkwarden/linkwarden.git
synced 2026-03-04 06:27:01 +00:00
Compare commits
40 Commits
chore/reac
...
v2.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c24e76adac | ||
|
|
5d26617251 | ||
|
|
0e47ad9920 | ||
|
|
ca45076b6c | ||
|
|
3bf6dcad2f | ||
|
|
23860b8511 | ||
|
|
8758976f8d | ||
|
|
550dbd2bf0 | ||
|
|
04d2b3c6b2 | ||
|
|
7bd0e29538 | ||
|
|
5baf55694c | ||
|
|
193a70c6e8 | ||
|
|
5b430cf31e | ||
|
|
684609a1dd | ||
|
|
ebb2016915 | ||
|
|
c103b66694 | ||
|
|
863bcc3838 | ||
|
|
66b0aacc3f | ||
|
|
299498ffa6 | ||
|
|
c5602dc79f | ||
|
|
0158e58d90 | ||
|
|
102690fc10 | ||
|
|
237499fd03 | ||
|
|
9a287d1aef | ||
|
|
299a2331ff | ||
|
|
9d91d2064b | ||
|
|
15a0084fb7 | ||
|
|
c0abf2f411 | ||
|
|
a886437589 | ||
|
|
a82c4ef85f | ||
|
|
7036b46084 | ||
|
|
2bba8198b8 | ||
|
|
96a70a9689 | ||
|
|
288fd9df87 | ||
|
|
7d43ed52a4 | ||
|
|
614653bf29 | ||
|
|
1b9dafbe47 | ||
|
|
abc93f1bf9 | ||
|
|
c23964a46d | ||
|
|
a76e996fc1 |
21
README.md
21
README.md
@@ -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!
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"] });
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: ["en","it"],
|
||||
locales: ["en", "it", "fr", "zh"],
|
||||
},
|
||||
reloadOnPrerender: process.env.NODE_ENV === "development",
|
||||
};
|
||||
|
||||
@@ -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>",
|
||||
|
||||
@@ -1186,7 +1186,7 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
||||
providerAccountId: account?.providerAccountId,
|
||||
},
|
||||
});
|
||||
if (existingUser && newSsoUsersDisabled) {
|
||||
if (!existingUser && newSsoUsersDisabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
374
public/locales/fr/common.json
Normal file
374
public/locales/fr/common.json
Normal 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 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.",
|
||||
"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"
|
||||
}
|
||||
374
public/locales/zh/common.json
Normal file
374
public/locales/zh/common.json
Normal 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": "从 Wallabag(JSON 文件)",
|
||||
"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": "PDF,PNG,JPG (不超过 {{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": "以演示用户登录"
|
||||
}
|
||||
Reference in New Issue
Block a user