diff --git a/API.md b/API.md index 6de5f5c4..609b813c 100644 --- a/API.md +++ b/API.md @@ -56,8 +56,8 @@ page to a comment. at any time. - **A username** is 1 to 32 letters, digits and underscores, unique in any case. A page writes it in its stored case, and another case still resolves. - A user may rename themselves once every 30 days from `/settings`; those - with `user:manage` rename anyone, at any time, from `/@/manage`. + Users can't rename themselves: those with `user:manage` rename anyone, from + `/@/manage`. - **Aliases.** A slug or a name left behind stays an alias for good, which always names the item or user itself, so redirects never chain. Nobody else can take one. An item can take back one of its own. Deleting an item frees @@ -84,7 +84,7 @@ thumbnail, cover or attachment takes the owner's media token as `?token=`. ### me - `GET /api/me` — signed in. The caller's own user. -- `PATCH /api/me` — signed in. Changes the description, email and username. +- `PATCH /api/me` — signed in. Changes the description and email. - `GET /api/me/media-token` — signed in. A media token. - `GET`, `PATCH /api/me/settings` — signed in. The caller's settings. - `PUT /api/me/password` — signed in, with the current one. A new password, @@ -115,7 +115,7 @@ thumbnail, cover or attachment takes the owner's media token as `?token=`. follow. - `GET /api/users/:key/invitees` — anyone. The tree of users they invited. - `PATCH /api/users/:key/role`, `/username` — `user:manage`. Another user's - role, or anyone's name. + role, or anyone's name, theirs included. ### dumps diff --git a/api/routes/me.ts b/api/routes/me.ts index d3123804..8511da4c 100644 --- a/api/routes/me.ts +++ b/api/routes/me.ts @@ -6,9 +6,9 @@ * `routes/avatars.ts`. * * - `GET /api/me` — signed in. Answers the caller's own user. - * - `PATCH /api/me` — signed in. Changes the caller's description, email and - * username, and answers their user. A new username keeps the old one as an - * alias, at most once in 30 days but for those with `user:manage`. + * - `PATCH /api/me` — signed in. Changes the caller's description and email, + * and answers their user. A username changes only by `user:manage`, through + * `PATCH /api/users/:key/username`. * - `GET /api/me/media-token` — signed in. Answers a media token, for a * session that predates them. * - `GET /api/me/settings` — signed in. Answers the caller's settings. @@ -38,19 +38,11 @@ import { isUpdateSettingsRequest } from "../validators/settings.ts"; import { toPublicUser, toSelfUser } from "../model/user.ts"; import { createMediaToken, issueTokens } from "../lib/jwt.ts"; import { verifyPassword } from "../lib/password.ts"; -import { can } from "../lib/permissions.ts"; import { type AuthContext, authMiddleware } from "../middleware/auth.ts"; -import { - getUserById, - renameUser, - updateUser, -} from "../services/user-service.ts"; +import { getUserById, updateUser } from "../services/user-service.ts"; import { getSettings, updateSettings } from "../services/settings-service.ts"; import { getPlaylistMembershipsForDump } from "../services/playlist-service.ts"; -import { - broadcastUserUpdated, - updateClientUsername, -} from "../services/ws-service.ts"; +import { broadcastUserUpdated } from "../services/ws-service.ts"; import { VALIDATION } from "../../shared/validation.ts"; /** @@ -74,16 +66,7 @@ router.patch("/", authMiddleware, async (ctx: AuthContext) => { } const userId = ctx.state.user.userId; // Only these fields: a stolen token alone must not change the role or the - // password, which needs the current one. The rename first: it is the change - // most likely refused, and a refusal should leave the rest as it was. - if (body.username !== undefined) { - const renamed = renameUser( - userId, - body.username, - can(ctx.state.user, "user:manage"), - ); - updateClientUsername(userId, renamed.username); - } + // password, which needs the current one. const updated = await updateUser(userId, { description: body.description, email: body.email, diff --git a/api/routes/users.ts b/api/routes/users.ts index 121187f2..9f634e5d 100644 --- a/api/routes/users.ts +++ b/api/routes/users.ts @@ -31,7 +31,7 @@ * - `PATCH /api/users/:key/role` — permission `user:manage`. Sets another * user's role. * - `PATCH /api/users/:key/username` — permission `user:manage`. Renames a - * user, with no rate limit. + * user, the caller included. It is the only way a name changes. * * @module */ @@ -293,7 +293,7 @@ router.patch( throw new APIException(400, "Invalid request"); } const user = resolveUserKey(ctx.params.key); - const publicUser = toPublicUser(renameUser(user.id, username, true)); + const publicUser = toPublicUser(renameUser(user.id, username)); updateClientUsername(publicUser.id, publicUser.username); broadcastUserUpdated(publicUser); const responseBody: APIResponse = { diff --git a/api/services/user-service.ts b/api/services/user-service.ts index 75095011..6a8bf552 100644 --- a/api/services/user-service.ts +++ b/api/services/user-service.ts @@ -32,11 +32,7 @@ import { LIKE, likePattern } from "../lib/like.ts"; import { parseRows } from "../model/rows.ts"; import { isEmailEnabled, sendEmail } from "./email-service.ts"; import { renderWelcomeEmail } from "../mail/welcome.ts"; -import { - RENAME_LIMITS, - USERNAME_RE, - VALIDATION, -} from "../../shared/validation.ts"; +import { USERNAME_RE, VALIDATION } from "../../shared/validation.ts"; /** Selects full `UserRow`s from `users u`, with the inviter's name joined. */ const USER_SELECT = @@ -206,24 +202,18 @@ export function resolveUserKey(key: string): User { } /** - * Renames user `userId` to `username`, for `PATCH /api/me` and, by a - * moderator, `PATCH /api/users/:key/username`, and returns them renamed. The - * old name becomes an alias of theirs for good, so `/@old/…` still reaches - * them and nobody else may take it; a change of case alone leaves none, since - * names are looked up in any case. Taking back a former name of one's own is - * allowed. Their dumps' and playlists' slugs belong to their id, so their - * paths follow the new name. Unless `exempt` (a moderator), a user may change - * names once in `RENAME_LIMITS.USERNAME`'s window. + * Renames user `userId` to `username`, for `PATCH /api/users/:key/username`, + * which only `user:manage` may call: users can't rename themselves. Returns + * them renamed. The old name becomes an alias of theirs for good, so + * `/@old/…` still reaches them and nobody else may take it; a change of case + * alone leaves none, since names are looked up in any case. Taking back a + * former name of their own is allowed. Their dumps' and playlists' slugs + * belong to their id, so their paths follow the new name. * * @throws {APIException} 400 when `username` isn't a valid name, 404 when - * there is no such user, 409 when the name is or was someone else's, 429 when - * the user changed names too recently. + * there is no such user, 409 when the name is or was someone else's. */ -export function renameUser( - userId: string, - username: string, - exempt: boolean, -): User { +export function renameUser(userId: string, username: string): User { if (!USERNAME_RE.test(username)) { throw new APIException( 400, @@ -233,33 +223,19 @@ export function renameUser( const user = getUserById(userId); if (username === user.username) return user; - const now = new Date(); + const now = new Date().toISOString(); transaction(() => { assertUsernameAvailable(username, userId); if (username.toLowerCase() !== user.username.toLowerCase()) { - const { count, windowMs } = RENAME_LIMITS.USERNAME; - const recent = (db.prepare( - `SELECT COUNT(*) AS n FROM user_aliases WHERE user_id = ? AND created_at > ?;`, - ).get(userId, new Date(now.getTime() - windowMs).toISOString()) as { - n: number; - }).n; - if (!exempt && recent >= count) { - throw new APIException( - 429, - `A username can only be changed once every ${ - windowMs / 86_400_000 - } days`, - ); - } db.prepare( `DELETE FROM user_aliases WHERE user_id = ? AND lower(username) = lower(?);`, ).run(userId, username); db.prepare( `INSERT INTO user_aliases (username, user_id, created_at) VALUES (?, ?, ?);`, - ).run(user.username, userId, now.toISOString()); + ).run(user.username, userId, now); } db.prepare(`UPDATE users SET username = ?, updated_at = ? WHERE id = ?;`) - .run(username, now.toISOString(), userId); + .run(username, now, userId); }); return getUserById(userId); } @@ -342,10 +318,9 @@ export function searchUsersGlobal(query: string, limit: number): PublicUser[] { * Applies `request` to user `userId` and returns the updated user. It trusts * `request`: the routes pick the fields a caller may change. The username * changes through {@link renameUser} alone, which keeps the old one as an - * alias, so a `username` here is ignored. A new password - * also ends every session opened before it and closes the user's sockets, so - * the caller must issue fresh tokens if the user is to stay signed in. A - * `description` of null keeps the current one. + * alias. A new password also ends every session opened before it and closes + * the user's sockets, so the caller must issue fresh tokens if the user is to + * stay signed in. A `description` of null keeps the current one. * * @throws {APIException} 404 when there is no such user, 409 when the new * email belongs to another account. @@ -356,8 +331,7 @@ export async function updateUser( ): Promise { const user = getUserById(userId); - const { password, description, email, username: _, ...requestFields } = - request; + const { password, description, email, ...requestFields } = request; if (email !== undefined && email !== user.email) { assertEmailAvailable(email, userId); } diff --git a/api/test/routes/users.test.ts b/api/test/routes/users.test.ts index 25df357c..de86522a 100644 --- a/api/test/routes/users.test.ts +++ b/api/test/routes/users.test.ts @@ -23,7 +23,12 @@ import type { SelfUser, UserSuggestion, } from "../../../shared/user.ts"; -import { assert, assertEquals, assertFalse } from "@std/assert"; +import { + assert, + assertEquals, + assertFalse, + assertNotEquals, +} from "@std/assert"; import { createPasswordResetToken, verifyJWT, @@ -69,7 +74,7 @@ test("public profile: by @name in any case or id, without private fields", async assertFalse("passwordHash" in me.data); }); -test("PATCH /api/me: description, email and username only", async () => { +test("PATCH /api/me: description and email only", async () => { const user = await createUser(); const taken = await createUser(); const res = await patch("/api/me", { @@ -618,17 +623,19 @@ test("settings: signed in only, and a bad value or unknown key is refused", asyn // ── Usernames and paths ────────────────────────────────────────────────────── test("renaming: the old name redirects for good, and nobody else can take it", async () => { + const admin = await createUser({ role: "admin" }); const bob = await createUser(); const old = bob.username; const renamed = uniqueName("robert"); const dump = insertDump({ userId: bob.id, title: "Never gonna" }); const playlist = insertPlaylist({ userId: bob.id, title: "Sunday mix" }); + const rename = (key: string, username: string) => + patch(`/api/users/${key}/username`, { + auth: admin.auth, + json: { username }, + }); - const res = await patch("/api/me", { - auth: bob.auth, - json: { username: renamed }, - }); - assertEquals(res.data.username, renamed); + assertEquals((await rename(bob.id, renamed)).data.username, renamed); // The old name, in any case, still answers the user, under the new one. for (const name of [old, old.toUpperCase(), renamed]) { @@ -648,7 +655,7 @@ test("renaming: the old name redirects for good, and nobody else can take it", a assertEquals([p.data.id, p.data.ownerUsername], [playlist.id, renamed]); } - // Nobody can register or rename to the old name, in any case. + // Nobody can register or be renamed to the old name, in any case. const other = await createUser(); const register = await post("/api/users", { json: { @@ -659,19 +666,9 @@ test("renaming: the old name redirects for good, and nobody else can take it", a }, }); assertEquals(register.status, 409); - assertEquals( - (await patch("/api/me", { auth: other.auth, json: { username: old } })) - .status, - 409, - ); + assertEquals((await rename(other.id, old)).status, 409); // Nor to a current one. - assertEquals( - (await patch("/api/me", { - auth: other.auth, - json: { username: renamed }, - })).status, - 409, - ); + assertEquals((await rename(other.id, renamed)).status, 409); // Signing in takes the current name only. assertEquals( (await post("/api/sessions", { @@ -681,60 +678,43 @@ test("renaming: the old name redirects for good, and nobody else can take it", a ); }); -test("renaming: once in 30 days, a case change aside, and a moderator is exempt", async () => { +test("renaming: only by user:manage, at any time, and never by oneself", async () => { const user = await createUser(); - const first = uniqueName("first"); - assertEquals( - (await patch("/api/me", { auth: user.auth, json: { username: first } })) - .status, - 200, - ); - // A change of case alone leaves no alias, so it isn't counted. - assertEquals( - (await patch("/api/me", { - auth: user.auth, - json: { username: first.toUpperCase() }, - })).data.username, - first.toUpperCase(), - ); - const again = await patch("/api/me", { + // PATCH /api/me refuses a name, and changes nothing else with it. + const self = await patch("/api/me", { auth: user.auth, - json: { username: uniqueName("second") }, + json: { username: uniqueName("self"), description: "hi" }, }); - assertEquals(again.status, 429); - assertEquals( - (await patch("/api/me", { auth: user.auth, json: { username: "a b" } })) - .status, - 400, - ); + assertEquals(self.status, 400); + const me = await get("/api/me", { auth: user.auth }); + assertEquals(me.data.username, user.username); + assertNotEquals(me.data.description, "hi"); - // An admin renames anyone at any time, and themselves too. + // An admin renames anyone at any time, themselves too. const admin = await createUser({ role: "admin" }); - const third = uniqueName("third"); - assertEquals( - (await patch(`/api/users/${user.id}/username`, { + const rename = (key: string, username: string) => + patch(`/api/users/${key}/username`, { auth: admin.auth, - json: { username: third }, - })).data.username, - third, + json: { username }, + }); + const first = uniqueName("first"); + const second = uniqueName("second"); + assertEquals((await rename(user.id, first)).data.username, first); + assertEquals((await rename(user.id, second)).data.username, second); + // A change of case alone leaves no alias. + assertEquals( + (await rename(`@${second}`, second.toUpperCase())).data.username, + second.toUpperCase(), ); assertEquals( - (await patch(`/api/users/@${third}/username`, { - auth: admin.auth, - json: { username: first }, - })).data.username, + (await rename(`@${first}`, first)).data.username, first, "a former name of one's own can be taken back", ); - for (let i = 0; i < 2; i++) { - assertEquals( - (await patch("/api/me", { - auth: admin.auth, - json: { username: uniqueName("admin") }, - })).status, - 200, - ); - } + assertEquals((await rename(user.id, "a b")).status, 400); + const adminName = uniqueName("admin"); + assertEquals((await rename(admin.id, adminName)).data.username, adminName); + // Others may not. const moderator = await createUser({ role: "moderator" }); assertEquals( diff --git a/api/test/validators/user.test.ts b/api/test/validators/user.test.ts index dcba9815..fae50a3f 100644 --- a/api/test/validators/user.test.ts +++ b/api/test/validators/user.test.ts @@ -44,7 +44,8 @@ Deno.test("isUpdateUserRequest", () => { assert(isUpdateUserRequest({ description: "hi", email: "a@b.co" })); assertFalse(isUpdateUserRequest({ description: "x".repeat(2001) })); assertFalse(isUpdateUserRequest({ email: "nope" })); - assertFalse(isUpdateUserRequest({ username: "no spaces" })); + // A name changes only through a moderator's PATCH /api/users/:key/username. + assertFalse(isUpdateUserRequest({ username: "well_formed" })); assertFalse(isUpdateUserRequest({ password: "short" })); assertFalse(isUpdateUserRequest({ role: "root" })); assertFalse(isUpdateUserRequest("string")); diff --git a/api/validators/user.ts b/api/validators/user.ts index 35a8a00b..3d43f7d7 100644 --- a/api/validators/user.ts +++ b/api/validators/user.ts @@ -81,10 +81,9 @@ export function validateRegisterUserRequest(obj: unknown): string | null { export function isUpdateUserRequest(obj: unknown): obj is UpdateUserRequest { if (!obj || typeof obj !== "object") return false; const o = obj as Record; - if ("username" in o) { - if (typeof o.username !== "string") return false; - if (!USERNAME_RE.test(o.username as string)) return false; - } + // A user can't rename themselves: a moderator does, through + // `PATCH /api/users/:key/username`. + if ("username" in o) return false; if ("password" in o) { if (typeof o.password !== "string") return false; const len = (o.password as string).length; diff --git a/locales/en.mjs b/locales/en.mjs index 26c18022..a6d767e9 100644 --- a/locales/en.mjs +++ b/locales/en.mjs @@ -1 +1 @@ -/*eslint-disable*/export const messages=JSON.parse("{\"-K9EZb\":[\"Add email…\"],\"-Ya-b9\":[\"Failed to save\"],\"-iDCtG\":[\"Your settings\"],\"-siMqD\":[\"Journal\"],\"1CalO6\":[\"Style\"],\"1HfJWf\":[\"Search dumps, users, playlists…\"],\"1QfxQT\":[\"Dismiss\"],\"1YKhJ3\":[\"1 new message\"],\"1njn7W\":[\"Light\"],\"1utXA6\":[\"Dumps\"],\"26iNma\":[\"Post comment\"],\"2DoBvq\":[\"Feeds\"],\"2Hlmdt\":[\"Write a reply…\"],\"2_DHgA\":[\"URL slug\"],\"2nrsEn\":[\"deleted message\"],\"2wxgft\":[\"Rename\"],\"2ygf_L\":[\"← Back\"],\"3KKSM4\":[\"private\"],\"3QuNUr\":[\"Several people are typing…\"],\"3SEwY1\":[\"Delete category \\\"\",[\"0\"],\"\\\"? This cannot be undone.\"],\"3qkggm\":[\"Fullscreen\"],\"3yfh3D\":[\"<0>\",[\"0\"],\" followed your playlist <1>\",[\"1\"],\"\"],\"48e2R8\":[\"Edit email\"],\"49voTZ\":[[\"label\"],\" (\",[\"count\"],\")\"],\"4B6w_o\":[\"Dumped!\"],\"4GKuCs\":[\"Login failed\"],\"4HH9iB\":[\"Not following anyone yet.\"],\"4OjqAQ\":[\"Keep editing\"],\"4RtQ1k\":[\"Unfollow \",[\"targetUsername\"]],\"4c-qBx\":[\"Your password has been changed. You can now log in.\"],\"4yj9xV\":[\"Live updates are temporarily disconnected. Trying to reconnect…\"],\"5KHDCi\":[[\"0\"],\" and \",[\"1\"],\" are typing…\"],\"5NZ3tg\":[\"Turning off…\"],\"5TviPn\":[\"Cancel search\"],\"5cC8f2\":[\"Edited \",[\"0\"]],\"5oD9f_\":[\"Earlier\"],\"6Qly-0\":[\"a comment\"],\"6YE0P6\":[\"Editing \",[\"0\"]],\"6YtxFj\":[\"Name\"],\"6_dCYd\":[\"Overview\"],\"6gRgw8\":[\"Retry\"],\"7PHCIN\":[\"Cancel removal\"],\"7PzzBU\":[\"User\"],\"7d1a0d\":[\"Public\"],\"7sNhEz\":[\"Username\"],\"8-8abB\":[\"Disconnected — reconnecting…\"],\"8F1i42\":[\"Page not found\"],\"8JplJJ\":[\"Unsubscribed\"],\"8Ug9jB\":[\"Related\"],\"8ZsakT\":[\"Password\"],\"92o1-U\":[\"Failed to delete\"],\"9BruTc\":[\"Why?\"],\"9l4qcT\":[\"Drop a replacement here\"],\"9uI_rE\":[\"Undo\"],\"A0y396\":[\"+ Invite someone\"],\"A1taO8\":[\"Search\"],\"AEfu-D\":[\"In collections\"],\"AHZflp\":[\"Chat\"],\"AQbgNR\":[\"Follow \",[\"targetUsername\"]],\"AS7WoE\":[\"Start fresh\"],\"ATGYL1\":[\"Email address\"],\"AZctoV\":[[\"0\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}]],\"Ade-6d\":[\"Live updates unavailable.\"],\"AeXO77\":[\"Account\"],\"AhNP40\":[\"Seek\"],\"AnRu_j\":[\"Cancel reply\"],\"B4LeYP\":[\"Delete failed (\",[\"0\"],\")\"],\"BjitBo\":[\"Download \",[\"0\"]],\"BlpJ2G\":[\"Picked up where you left off.\"],\"CI50ct\":[\"Upvoted\"],\"C_qQE4\":[\"Already dumped by \",[\"0\"],\" \",[\"1\"]],\"Cf_gxi\":[\"Turn off the weekly digest?\"],\"Cj24wt\":[\"Registering…\"],\"CnqayG\":[\"Couldn't update. Try again.\"],\"D8DRYV\":[\"Delete this message?\"],\"DI3NBO\":[\"Open on \",[\"0\"]],\"DPfwMq\":[\"Done\"],\"DdeHXH\":[\"Delete this dump? This cannot be undone.\"],\"Dp1JhP\":[\"<0>\",[\"0\"],\" upvoted <1>\",[\"1\"],\"\"],\"E417RG\":[\"You won't get the weekly digest any more. You can turn it back on in your settings.\"],\"EBU6O6\":[\"Connection lost — this message may not have been sent\"],\"ECiS12\":[\"View dump →\"],\"ED8n0z\":[\"the chat\"],\"EarrCe\":[\"Search emoji\"],\"Evcm2Z\":[\"Close without posting? The attached file won't be kept — everything you typed will.\"],\"EwVrWF\":[\"The dump couldn't be removed. Try again.\"],\"ExR0Fr\":[\"URL is required.\"],\"F5Js1v\":[\"Unfollow playlist\"],\"FdTlEG\":[\"Track \",[\"0\"]],\"FgAxTj\":[\"Log out\"],\"Fxf4jq\":[\"Description (optional)\"],\"GCWCCo\":[\"Close player\"],\"GDvlUT\":[\"Role\"],\"GLukaH\":[\"Moderator\"],\"GNSsCc\":[\"Failed to create playlist\"],\"GQ38dl\":[\"Chat (\",[\"unreadChatCount\"],\" unread)\"],\"GY6uSc\":[\"Next →\"],\"GbqhrN\":[[\"0\"],\"–\",[\"1\"],\" characters: letters, numbers, or underscores\"],\"GptGxg\":[\"Change password\"],\"Gv5nA5\":[\"Unexpected response format\"],\"H4MGkI\":[\"Expand player\"],\"H4o4sk\":[\"No one yet.\"],\"H8pzW-\":[\"Failed to update avatar\"],\"HTLDA4\":[\"Loading more…\"],\"H_PnT8\":[\"Insert emoji\"],\"I-x669\":[\"Invitees\"],\"IO9KL_\":[\"Next track\"],\"IZX7TO\":[\"Failed to generate invite\"],\"IagCbF\":[\"URL\"],\"IgrLD_\":[\"Pause\"],\"IhK90M\":[\"Letters, numbers and underscores only\"],\"ImOQa9\":[\"Reply\"],\"JJ-Bhk\":[\"Your email address\"],\"JLasL-\":[\"Couldn't unsubscribe\"],\"JRQitQ\":[\"Confirm new password\"],\"JXr41k\":[\"<0>\",[\"0\"],\" commented on <1>\",[\"1\"],\"\"],\"Jd58Fo\":[\"Hot\"],\"Jf0PuK\":[\"Go to login\"],\"JlFcis\":[\"Send\"],\"JzARG2\":[\"Links to the old address keep working.\"],\"K7tIrx\":[\"Category\"],\"KDGWg5\":[\"Remove from playlist\"],\"KDw4GX\":[\"Try again\"],\"KKZxBU\":[\"Uploading \",[\"0\"],\"%\"],\"K_F6pa\":[\"Saving…\"],\"KtYF-t\":[\"Sounds\"],\"L-rMC9\":[\"Reset to default\"],\"L7_rsx\":[\"Uploading image…\"],\"MHrjPM\":[\"Title\"],\"MKEPCY\":[\"Follow\"],\"MTb2Ot\":[\"No messages yet. Say hello!\"],\"Mj6oCJ\":[[\"0\"],\" is typing…\"],\"N40H-G\":[\"All\"],\"N6j2JH\":[\"Edit \",[\"0\"]],\"NUrY9o\":[\"Categories\"],\"O32mmL\":[\"Edit description\"],\"O3oNi5\":[\"Email\"],\"Oprv1v\":[\"Password (min. \",[\"0\"],\" characters)\"],\"Oz0N9s\":[\"new\"],\"P5MuiB\":[\"Add image\"],\"PN3pBl\":[\"No reply from the server — this message may not have been sent\"],\"PUe8Yk\":[\"Collapse player\"],\"PiH3UR\":[\"Copied!\"],\"Pn2B7_\":[\"Current password\"],\"Pwqkdw\":[\"Loading…\"],\"Q4WUjR\":[\"Search emoji…\"],\"Q6n4F4\":[\"Refresh metadata\"],\"QGaQRo\":[\"Failed to update role\"],\"QKsaQr\":[\"or <0>browse files\"],\"QLtPBd\":[\"No dumps in this playlist yet.\"],\"QctOgc\":[\"Could not connect to the server. Check your connection and try again.\"],\"R7SsBE\":[\"Mute\"],\"R9Khdg\":[\"Auto\"],\"RCcPrX\":[\"Delete this playlist? This cannot be undone.\"],\"RTksSy\":[\"<0>\",[\"0\"],\" started following you\"],\"RaKjrM\":[\"Failed to save edit\"],\"RcUHRT\":[\"Followed\"],\"RfwZxd\":[\"Reset password\"],\"Rrp6-J\":[\"Log in to like\"],\"RvVJBH\":[\"Refreshing stream…\"],\"S1xV5B\":[\"At most \",[\"maxLength\"],\" characters.\"],\"SBTElJ\":[\"Searching…\"],\"SMaFdc\":[\"Unsubscribe\"],\"Sad2tK\":[\"Sending…\"],\"So7gCD\":[\"Paste a link…\"],\"StovX6\":[\"This page does not exist.\"],\"Sxm8rQ\":[\"Users\"],\"T62TQc\":[\"You can change it once every \",[\"renameDays\"],\" days. Links to your old name keep working, and nobody else can take it.\"],\"T8ejtZ\":[\"This comment couldn't be deleted. Try again.\"],\"T91vKp\":[\"Play\"],\"T9bjWt\":[\"<0>\",[\"0\"],\" was added to <1>\",[\"1\"],\"\"],\"TAtbho\":[\"Loading messages…\"],\"TN382O\":[\"Invalid link\"],\"Tv9vbB\":[\"Follow playlist\"],\"Tz0i8g\":[\"Settings\"],\"U3pytU\":[\"Admin\"],\"UNMVei\":[\"Forgot password?\"],\"UOZith\":[\"Failed to post\"],\"URAieT\":[\"Log in to vote\"],\"V38P77\":[\"Only its poster or a moderator can edit this dump.\"],\"VCoEm-\":[\"Back to login\"],\"VKsaTi\":[\"Replying to\"],\"VLAd8f\":[\"Max \",[\"limit\"]],\"V_e7nf\":[\"Set new password\"],\"VnNJbN\":[\"From playlists\"],\"VskHIx\":[\"Delete category\"],\"VyTYmS\":[\"Change avatar\"],\"W5A0Ly\":[\"An unexpected error occurred.\"],\"W9FRBT\":[\"Like\"],\"WAcnnR\":[\"The image couldn't be uploaded.\"],\"WE_r9K\":[\"Failed to create category\"],\"WfffrI\":[\"slug\"],\"WhimMi\":[\"Reset failed\"],\"WpXcBJ\":[\"Nothing here yet.\"],\"XHqhoS\":[\"Couldn't load the chat.\"],\"XJVYo8\":[\"Why & where\"],\"XJy2oN\":[\"Logging in…\"],\"Xan6QP\":[\"New dump\"],\"XgRtUf\":[\"Could not change password\"],\"XnL-Eu\":[\"No users match \\\"\",[\"q\"],\"\\\".\"],\"Xs2Lez\":[\"This reset link is missing or malformed.\"],\"YK1Dhc\":[\"a post\"],\"YWnSuq\":[\"This unsubscribe link is missing or malformed.\"],\"YZxXK8\":[\"Close emoji picker\"],\"YcjhhX\":[\"User management\"],\"Ye9RMF\":[\"<0>\",[\"0\"],\" liked your comment on <1>\",[\"1\"],\"\"],\"YpkCca\":[\"No followed playlists yet.\"],\"Z5HWHd\":[\"On\"],\"ZBdbv9\":[\"Edit title\"],\"ZCpU0u\":[\"No playlists match \\\"\",[\"q\"],\"\\\".\"],\"ZWxjuD\":[\"Some images couldn't be uploaded.\"],\"ZmD2o6\":[\"Create & Add\"],\"_3O5R_\":[\"Request failed\"],\"_DwR-n\":[\"Creating…\"],\"_I1_HN\":[\"Lowercase letters, numbers and single hyphens only\"],\"_R_sGB\":[\"Password changed successfully.\"],\"_aept4\":[\"Post reply\"],\"_cF7Rs\":[\"Volume\"],\"_karOe\":[\"You're offline — not sent\"],\"_nT6AE\":[\"New password\"],\"_t4W-i\":[\"From people\"],\"aAIQg2\":[\"Appearance\"],\"aDvLhk\":[\"Add a comment…\"],\"az8lvo\":[\"Off\"],\"azhsgG\":[\"Their old name keeps leading to them, and nobody else can take it.\"],\"b3Thhd\":[\"Upload failed\"],\"b8XMJ8\":[[\"visibleCount\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}]],\"bQhwn-\":[\"Loading playlist…\"],\"b_1P1D\":[\"Renaming…\"],\"bzSI52\":[\"Discard\"],\"cILfnJ\":[\"Remove file\"],\"cNu4my\":[[\"0\"],\" more online: \",[\"restNames\"]],\"cYP9Sb\":[\"+ Playlist\"],\"cbeBbZ\":[\"At least \",[\"0\"],\" characters\"],\"cnGeoo\":[\"Delete\"],\"dAs22m\":[\"Replace file\"],\"dEgA5A\":[\"Cancel\"],\"dMizp8\":[\"New playlist\"],\"dau1Pl\":[[\"unseenCount\"],\" new messages\"],\"digest.monthly\":[\"Monthly recap\"],\"digest.monthly.off\":[\"Turn off monthly recaps\"],\"digest.weekly\":[\"Weekly digest\"],\"digest.weekly.off\":[\"Turn off weekly digests\"],\"dqwCfR\":[\"Couldn't load a preview for this link — check it, or post it as-is and refresh the preview later.\"],\"dznB-Y\":[\"Only images can be dropped here.\"],\"eFSqvc\":[\"Failed to post reply\"],\"eIEHHV\":[\"Change skin tone\"],\"eOfXq3\":[\"Title is required.\"],\"ePK91l\":[\"Edit\"],\"eaUTwS\":[\"Send reset link\"],\"ecUA8p\":[\"Today\"],\"ef9nPf\":[\"Loading dump…\"],\"eloqXQ\":[\"You can post it anyway.\"],\"email.digest.addedTo\":[\"added to \",[\"playlist\"]],\"email.digest.andMore\":[\"and \",[\"n\"],\" more\"],\"email.digest.by\":[\"by @\",[\"username\"]],\"email.digest.chatWeek\":[[\"n\",\"plural\",{\"one\":[\"#\",\" message this week\"],\"other\":[\"#\",\" messages this week\"]}]],\"email.digest.community\":[\"Community\"],\"email.digest.count.comments\":[[\"n\",\"plural\",{\"one\":[\"comment\"],\"other\":[\"comments\"]}]],\"email.digest.count.followers\":[[\"n\",\"plural\",{\"one\":[\"new follower\"],\"other\":[\"new followers\"]}]],\"email.digest.count.likes\":[[\"n\",\"plural\",{\"one\":[\"comment like\"],\"other\":[\"comment likes\"]}]],\"email.digest.count.mentions\":[[\"n\",\"plural\",{\"one\":[\"mention\"],\"other\":[\"mentions\"]}]],\"email.digest.count.playlistFollows\":[[\"n\",\"plural\",{\"one\":[\"playlist follow\"],\"other\":[\"playlist follows\"]}]],\"email.digest.count.upvotes\":[[\"n\",\"plural\",{\"one\":[\"upvote\"],\"other\":[\"upvotes\"]}]],\"email.digest.forYou\":[\"For you\"],\"email.digest.hl.comment\":[[\"who\"],\" commented on your dump \",[\"target\"]],\"email.digest.hl.commentLike\":[[\"who\"],\" liked your comment on \",[\"target\"]],\"email.digest.hl.mentionComment\":[[\"who\"],\" mentioned you in a comment on \",[\"target\"]],\"email.digest.hl.mentionIn\":[[\"who\"],\" mentioned you in \",[\"target\"]],\"email.digest.hl.mentionPlaylist\":[[\"who\"],\" mentioned you in the playlist \",[\"target\"]],\"email.digest.hl.playlistFollow\":[[\"who\"],\" followed your playlist \",[\"target\"]],\"email.digest.hl.userFollow\":[[\"who\"],\" started following you\"],\"email.digest.inChat\":[\"In the chat\"],\"email.digest.invitedBy\":[\"invited by @\",[\"username\"]],\"email.digest.invitedByYou\":[\"invited by you\"],\"email.digest.monthly.reason\":[\"You're getting the monthly recap because it's on in your settings.\"],\"email.digest.monthly.subject\":[\"The month on \",[\"site\"],\", \",[\"range\"]],\"email.digest.monthly.title\":[\"The month on \",[\"site\"]],\"email.digest.more\":[[\"n\"],\" more on your Followed feed\"],\"email.digest.network\":[\"From people you follow\"],\"email.digest.newMembers\":[\"New members\"],\"email.digest.newPlaylists\":[\"New public playlists\"],\"email.digest.openChat\":[\"Open the chat\"],\"email.digest.playlistMeta\":[\"by @\",[\"username\"],\" · \",[\"n\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}]],\"email.digest.pre.members\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new member\"],\"other\":[\"#\",\" new members\"]}]],\"email.digest.pre.network\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new dump from people you follow\"],\"other\":[\"#\",\" new dumps from people you follow\"]}]],\"email.digest.pre.playlists\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new playlist\"],\"other\":[\"#\",\" new playlists\"]}]],\"email.digest.pre.topMonth\":[[\"n\",\"plural\",{\"one\":[\"the most upvoted dump of the month\"],\"other\":[\"the \",\"#\",\" most upvoted dumps of the month\"]}]],\"email.digest.pre.topWeek\":[[\"n\",\"plural\",{\"one\":[\"the most upvoted dump of the week\"],\"other\":[\"the \",\"#\",\" most upvoted dumps of the week\"]}]],\"email.digest.seeAll\":[[\"n\",\"plural\",{\"one\":[\"See the notification\"],\"other\":[\"See all \",\"#\",\" notifications\"]}]],\"email.digest.stat.chat\":[[\"n\",\"plural\",{\"one\":[\"chat message\"],\"other\":[\"chat messages\"]}]],\"email.digest.stat.dumps\":[[\"n\",\"plural\",{\"one\":[\"dump\"],\"other\":[\"dumps\"]}]],\"email.digest.theChat\":[\"the chat\"],\"email.digest.topMonth\":[\"Top this month\"],\"email.digest.topWeek\":[\"Top this week\"],\"email.digest.totals\":[[\"dumps\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}],\", \",[\"upvotes\",\"plural\",{\"one\":[\"#\",\" upvote\"],\"other\":[\"#\",\" upvotes\"]}],\" and \",[\"comments\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}],\".\"],\"email.digest.upload\":[\"upload\"],\"email.digest.weekly.reason\":[\"You're getting the weekly digest because it's on in your settings.\"],\"email.digest.weekly.subject\":[\"Your week on \",[\"site\"],\", \",[\"range\"]],\"email.digest.weekly.title\":[\"Your week, \",[\"username\"]],\"email.digest.yours\":[\"yours\"],\"email.reset.body\":[\"Someone asked to reset the password of your \",[\"site\"],\" account. The button below lets you choose a new one. It works once, for \",[\"hours\",\"plural\",{\"one\":[\"#\",\" hour\"],\"other\":[\"#\",\" hours\"]}],\".\"],\"email.reset.button\":[\"Reset password\"],\"email.reset.fallback\":[\"If the button doesn't work, paste this link into your browser:\"],\"email.reset.hi\":[\"Hi \",[\"username\"],\",\"],\"email.reset.ignore\":[\"If you didn't ask for this, ignore this email. Your password stays the same.\"],\"email.reset.preheader\":[\"The link works once, for \",[\"hours\",\"plural\",{\"one\":[\"#\",\" hour\"],\"other\":[\"#\",\" hours\"]}],\".\"],\"email.reset.reason\":[\"You're getting this email because a password reset was requested for this address.\"],\"email.reset.subject\":[\"Reset your \",[\"site\"],\" password\"],\"email.reset.title\":[\"Reset your password\"],\"email.settings\":[\"Settings\"],\"email.welcome.body\":[\"# Welcome to \",[\"site\"],\"!\\n\\nHi **\",[\"username\"],\"**,\\n\\nYour account has been created successfully. Welcome aboard!\"],\"email.welcome.button\":[\"Open \",[\"site\"]],\"email.welcome.digests\":[\"Once a week you'll get a digest of what happened here, and once a month a recap of the most upvoted dumps. You can turn either off in your settings.\"],\"email.welcome.preheader\":[\"Your account is ready.\"],\"email.welcome.reason\":[\"You're getting this email because an account was created on \",[\"site\"],\" with this address.\"],\"email.welcome.subject\":[\"Welcome to \",[\"site\"]],\"en9o7K\":[\"Failed to post comment\"],\"etFQQS\":[\"What makes it worth it?\"],\"f3cEDO\":[\"Change image\"],\"fC6mXb\":[\"Upvote\"],\"fI-mNw\":[\"Playlists\"],\"fgLNSM\":[\"Register\"],\"gANddk\":[\"Uploading…\"],\"gGx5tM\":[\"Editing\"],\"gIQQwD\":[\"Failed to load\"],\"gIpLuy\":[\"A link is required\"],\"gL9KKi\":[\"Who can see it\"],\"gLfZlz\":[\"Add to playlist\"],\"gjJ-sb\":[\"Can't connect to the live updates server. Upvotes and notifications may not sync until it reconnects.\"],\"goWEhR\":[\"Previous track\"],\"hBuUKa\":[\"Change password…\"],\"hD7w09\":[\"You've reached the end.\"],\"hJSliC\":[\"<0>\",[\"0\"],\" posted <1>\",[\"1\"],\"\"],\"hT8hgO\":[\"Reset to the suggested title\"],\"hYgDIe\":[\"Create\"],\"he3ygx\":[\"Copy\"],\"hpCc_Z\":[\"Close anyway\"],\"i7K_Te\":[\"Who am I?\"],\"iDNBZe\":[\"Notifications\"],\"iWpEwy\":[\"Go home\"],\"ipYn7W\":[\"If that address is registered you'll receive a reset link shortly.\"],\"isRobC\":[\"New\"],\"jTGs8A\":[\"This site doesn't send email, so no digest goes out.\"],\"jbernk\":[\"Loading profile…\"],\"jnrIbz\":[\"Upvoted (\",[\"0\"],\")\"],\"joEmfT\":[\"Server unreachable\"],\"jrZTZl\":[\"No dumps yet. Be the first!\"],\"k112DD\":[\"Load older messages\"],\"k36iJ-\":[\"Failed to save category\"],\"k4b5_X\":[\"edited\"],\"kLttbL\":[\"Registration failed\"],\"lUanmi\":[\"You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content.\"],\"lY5h1V\":[[\"0\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}]],\"lcfvr_\":[\"Delete this comment?\"],\"lpIMne\":[\"Passwords do not match\"],\"mDJo8W\":[\"Dumps (\",[\"0\"],\")\"],\"mb_Wju\":[\"Drop a file here, or paste one\"],\"mt6O6E\":[\"This is a mirage.\"],\"nbm5sI\":[\"No dumps match \\\"\",[\"q\"],\"\\\".\"],\"nrjqON\":[\"Checking invite…\"],\"nwtY4N\":[\"Something went wrong\"],\"nx4kaN\":[\"Could not save\"],\"oTYvvN\":[[\"0\",\"plural\",{\"one\":[\"and \",\"#\",\" earlier dump\"],\"other\":[\"and \",\"#\",\" earlier dumps\"]}]],\"ogtYkT\":[\"Password updated\"],\"pCpd9p\":[\"<0>\",[\"0\"],\" mentioned you in <1>\",[\"where\"],\"\"],\"pSheLH\":[\"No invitees yet.\"],\"pd3FCq\":[\"That file is over \",[\"limit\"],\".\"],\"pvnfJD\":[\"Dark\"],\"qIMfNQ\":[\"Delete playlist\"],\"qItrqH\":[\"You won't get the monthly recap any more. You can turn it back on in your settings.\"],\"qbDAcy\":[\"Dump it\"],\"qgx_78\":[\"Follow some public playlists to see their dumps here.\"],\"qnUJBI\":[\"The new order couldn't be saved. Try again.\"],\"qsqt0w\":[\"Invite link\"],\"qvFa8r\":[\"public\"],\"qvz_Pp\":[\"Dump\"],\"rCbqPX\":[\"This invite link is missing, expired, or already used.\"],\"rDMzDU\":[\"Type a message…\"],\"rg9pXu\":[\"Search failed\"],\"rn6SBY\":[\"Unmute\"],\"sEXJPi\":[\"Link or file\"],\"sGeXL3\":[\"Thumbnail\"],\"sQia9P\":[\"Log in\"],\"sTiqbm\":[\"invited by\"],\"sdP5Aa\":[\"[deleted]\"],\"sgX7B-\":[\"Single use, valid for 7 days.\"],\"shHs8T\":[\"Enter a query to search.\"],\"smeBfS\":[\"Invalid invite\"],\"teaneZ\":[\"image\"],\"tfDRzk\":[\"Save\"],\"tvmuQ0\":[\"Color scheme\"],\"u1lDX2\":[\"Fetching preview…\"],\"uD0qXQ\":[\"Drop a file here\"],\"uISnsY\":[\"Could not delete\"],\"uMGUnV\":[\"No playlists yet.\"],\"ub1EEL\":[\"edited \",[\"0\"]],\"uomLas\":[\"Add category\"],\"v-Wp--\":[\"Open source\"],\"vJBF1r\":[\"Posting…\"],\"vLhLLO\":[\"Notifications (\",[\"unreadNotificationCount\"],\" unread)\"],\"vQMkHu\":[\"Remove vote\"],\"vuosjb\":[\"User menu\"],\"wckWOP\":[\"Manage\"],\"wixIgH\":[\"Already have an account? <0>Log in\"],\"wtxFp4\":[\"File too large (max \",[\"max\"],\").\"],\"x6tjuK\":[\"Default tab\"],\"xEWkgZ\":[\"← Back to all dumps\"],\"xPHtx0\":[\"Submit search\"],\"xVuNgt\":[\"+ New playlist\"],\"xc9O_u\":[\"Delete dump\"],\"y1G_DI\":[\"Could not rename\"],\"y6sq5j\":[\"Following\"],\"y7oaHj\":[\"Playlist title\"],\"yA_6BX\":[\"View all →\"],\"yBBtRm\":[\"Follow some users to see their dumps here.\"],\"yQ2kGp\":[\"Load more\"],\"y_0uwd\":[\"Yesterday\"],\"yxlcv5\":[\"Turn off the monthly recap?\"],\"yz7wBu\":[\"Close\"],\"yzF66j\":[\"Link\"],\"z0ROB3\":[\"Remove like\"],\"z1uNN0\":[\"No emoji found.\"],\"zVuxvN\":[\"Refreshing…\"],\"zwBp5t\":[\"Private\"]}"); \ No newline at end of file +/*eslint-disable*/export const messages=JSON.parse("{\"-K9EZb\":[\"Add email…\"],\"-Ya-b9\":[\"Failed to save\"],\"-iDCtG\":[\"Your settings\"],\"-siMqD\":[\"Journal\"],\"1CalO6\":[\"Style\"],\"1HfJWf\":[\"Search dumps, users, playlists…\"],\"1QfxQT\":[\"Dismiss\"],\"1YKhJ3\":[\"1 new message\"],\"1njn7W\":[\"Light\"],\"1utXA6\":[\"Dumps\"],\"26iNma\":[\"Post comment\"],\"2DoBvq\":[\"Feeds\"],\"2Hlmdt\":[\"Write a reply…\"],\"2_DHgA\":[\"URL slug\"],\"2nrsEn\":[\"deleted message\"],\"2wxgft\":[\"Rename\"],\"2ygf_L\":[\"← Back\"],\"3KKSM4\":[\"private\"],\"3QuNUr\":[\"Several people are typing…\"],\"3SEwY1\":[\"Delete category \\\"\",[\"0\"],\"\\\"? This cannot be undone.\"],\"3qkggm\":[\"Fullscreen\"],\"3yfh3D\":[\"<0>\",[\"0\"],\" followed your playlist <1>\",[\"1\"],\"\"],\"48e2R8\":[\"Edit email\"],\"49voTZ\":[[\"label\"],\" (\",[\"count\"],\")\"],\"4B6w_o\":[\"Dumped!\"],\"4GKuCs\":[\"Login failed\"],\"4HH9iB\":[\"Not following anyone yet.\"],\"4OjqAQ\":[\"Keep editing\"],\"4RtQ1k\":[\"Unfollow \",[\"targetUsername\"]],\"4c-qBx\":[\"Your password has been changed. You can now log in.\"],\"4yj9xV\":[\"Live updates are temporarily disconnected. Trying to reconnect…\"],\"5KHDCi\":[[\"0\"],\" and \",[\"1\"],\" are typing…\"],\"5NZ3tg\":[\"Turning off…\"],\"5TviPn\":[\"Cancel search\"],\"5cC8f2\":[\"Edited \",[\"0\"]],\"5oD9f_\":[\"Earlier\"],\"6Qly-0\":[\"a comment\"],\"6YE0P6\":[\"Editing \",[\"0\"]],\"6YtxFj\":[\"Name\"],\"6_dCYd\":[\"Overview\"],\"6gRgw8\":[\"Retry\"],\"7PHCIN\":[\"Cancel removal\"],\"7PzzBU\":[\"User\"],\"7d1a0d\":[\"Public\"],\"7sNhEz\":[\"Username\"],\"8-8abB\":[\"Disconnected — reconnecting…\"],\"8F1i42\":[\"Page not found\"],\"8JplJJ\":[\"Unsubscribed\"],\"8Ug9jB\":[\"Related\"],\"8ZsakT\":[\"Password\"],\"92o1-U\":[\"Failed to delete\"],\"9BruTc\":[\"Why?\"],\"9l4qcT\":[\"Drop a replacement here\"],\"9uI_rE\":[\"Undo\"],\"A0y396\":[\"+ Invite someone\"],\"A1taO8\":[\"Search\"],\"AEfu-D\":[\"In collections\"],\"AHZflp\":[\"Chat\"],\"AQbgNR\":[\"Follow \",[\"targetUsername\"]],\"AS7WoE\":[\"Start fresh\"],\"ATGYL1\":[\"Email address\"],\"AZctoV\":[[\"0\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}]],\"Ade-6d\":[\"Live updates unavailable.\"],\"AeXO77\":[\"Account\"],\"AhNP40\":[\"Seek\"],\"AnRu_j\":[\"Cancel reply\"],\"B4LeYP\":[\"Delete failed (\",[\"0\"],\")\"],\"BjitBo\":[\"Download \",[\"0\"]],\"BlpJ2G\":[\"Picked up where you left off.\"],\"CI50ct\":[\"Upvoted\"],\"C_qQE4\":[\"Already dumped by \",[\"0\"],\" \",[\"1\"]],\"Cf_gxi\":[\"Turn off the weekly digest?\"],\"Cj24wt\":[\"Registering…\"],\"CnqayG\":[\"Couldn't update. Try again.\"],\"D8DRYV\":[\"Delete this message?\"],\"DI3NBO\":[\"Open on \",[\"0\"]],\"DPfwMq\":[\"Done\"],\"DdeHXH\":[\"Delete this dump? This cannot be undone.\"],\"Dp1JhP\":[\"<0>\",[\"0\"],\" upvoted <1>\",[\"1\"],\"\"],\"E417RG\":[\"You won't get the weekly digest any more. You can turn it back on in your settings.\"],\"EBU6O6\":[\"Connection lost — this message may not have been sent\"],\"ECiS12\":[\"View dump →\"],\"ED8n0z\":[\"the chat\"],\"EarrCe\":[\"Search emoji\"],\"Evcm2Z\":[\"Close without posting? The attached file won't be kept — everything you typed will.\"],\"EwVrWF\":[\"The dump couldn't be removed. Try again.\"],\"ExR0Fr\":[\"URL is required.\"],\"F5Js1v\":[\"Unfollow playlist\"],\"FdTlEG\":[\"Track \",[\"0\"]],\"FgAxTj\":[\"Log out\"],\"Fxf4jq\":[\"Description (optional)\"],\"GCWCCo\":[\"Close player\"],\"GDvlUT\":[\"Role\"],\"GLukaH\":[\"Moderator\"],\"GNSsCc\":[\"Failed to create playlist\"],\"GQ38dl\":[\"Chat (\",[\"unreadChatCount\"],\" unread)\"],\"GY6uSc\":[\"Next →\"],\"GbqhrN\":[[\"0\"],\"–\",[\"1\"],\" characters: letters, numbers, or underscores\"],\"GptGxg\":[\"Change password\"],\"Gv5nA5\":[\"Unexpected response format\"],\"H4MGkI\":[\"Expand player\"],\"H4o4sk\":[\"No one yet.\"],\"H8pzW-\":[\"Failed to update avatar\"],\"HTLDA4\":[\"Loading more…\"],\"H_PnT8\":[\"Insert emoji\"],\"I-x669\":[\"Invitees\"],\"IO9KL_\":[\"Next track\"],\"IZX7TO\":[\"Failed to generate invite\"],\"IagCbF\":[\"URL\"],\"IgrLD_\":[\"Pause\"],\"IhK90M\":[\"Letters, numbers and underscores only\"],\"ImOQa9\":[\"Reply\"],\"JJ-Bhk\":[\"Your email address\"],\"JLasL-\":[\"Couldn't unsubscribe\"],\"JRQitQ\":[\"Confirm new password\"],\"JXr41k\":[\"<0>\",[\"0\"],\" commented on <1>\",[\"1\"],\"\"],\"Jd58Fo\":[\"Hot\"],\"Jf0PuK\":[\"Go to login\"],\"JlFcis\":[\"Send\"],\"JzARG2\":[\"Links to the old address keep working.\"],\"K7tIrx\":[\"Category\"],\"KDGWg5\":[\"Remove from playlist\"],\"KDw4GX\":[\"Try again\"],\"KKZxBU\":[\"Uploading \",[\"0\"],\"%\"],\"K_F6pa\":[\"Saving…\"],\"KtYF-t\":[\"Sounds\"],\"L-rMC9\":[\"Reset to default\"],\"L7_rsx\":[\"Uploading image…\"],\"MHrjPM\":[\"Title\"],\"MKEPCY\":[\"Follow\"],\"MTb2Ot\":[\"No messages yet. Say hello!\"],\"Mj6oCJ\":[[\"0\"],\" is typing…\"],\"N40H-G\":[\"All\"],\"N6j2JH\":[\"Edit \",[\"0\"]],\"NUrY9o\":[\"Categories\"],\"O32mmL\":[\"Edit description\"],\"O3oNi5\":[\"Email\"],\"Oprv1v\":[\"Password (min. \",[\"0\"],\" characters)\"],\"Oz0N9s\":[\"new\"],\"P5MuiB\":[\"Add image\"],\"PN3pBl\":[\"No reply from the server — this message may not have been sent\"],\"PUe8Yk\":[\"Collapse player\"],\"PiH3UR\":[\"Copied!\"],\"Pn2B7_\":[\"Current password\"],\"Pwqkdw\":[\"Loading…\"],\"Q4WUjR\":[\"Search emoji…\"],\"Q6n4F4\":[\"Refresh metadata\"],\"QGaQRo\":[\"Failed to update role\"],\"QKsaQr\":[\"or <0>browse files\"],\"QLtPBd\":[\"No dumps in this playlist yet.\"],\"QctOgc\":[\"Could not connect to the server. Check your connection and try again.\"],\"R7SsBE\":[\"Mute\"],\"R9Khdg\":[\"Auto\"],\"RCcPrX\":[\"Delete this playlist? This cannot be undone.\"],\"RTksSy\":[\"<0>\",[\"0\"],\" started following you\"],\"RaKjrM\":[\"Failed to save edit\"],\"RcUHRT\":[\"Followed\"],\"RfwZxd\":[\"Reset password\"],\"Rrp6-J\":[\"Log in to like\"],\"RvVJBH\":[\"Refreshing stream…\"],\"S1xV5B\":[\"At most \",[\"maxLength\"],\" characters.\"],\"SBTElJ\":[\"Searching…\"],\"SMaFdc\":[\"Unsubscribe\"],\"Sad2tK\":[\"Sending…\"],\"So7gCD\":[\"Paste a link…\"],\"StovX6\":[\"This page does not exist.\"],\"Sxm8rQ\":[\"Users\"],\"T8ejtZ\":[\"This comment couldn't be deleted. Try again.\"],\"T91vKp\":[\"Play\"],\"T9bjWt\":[\"<0>\",[\"0\"],\" was added to <1>\",[\"1\"],\"\"],\"TAtbho\":[\"Loading messages…\"],\"TN382O\":[\"Invalid link\"],\"Tv9vbB\":[\"Follow playlist\"],\"Tz0i8g\":[\"Settings\"],\"U3pytU\":[\"Admin\"],\"UNMVei\":[\"Forgot password?\"],\"UOZith\":[\"Failed to post\"],\"URAieT\":[\"Log in to vote\"],\"V38P77\":[\"Only its poster or a moderator can edit this dump.\"],\"VCoEm-\":[\"Back to login\"],\"VKsaTi\":[\"Replying to\"],\"VLAd8f\":[\"Max \",[\"limit\"]],\"V_e7nf\":[\"Set new password\"],\"VnNJbN\":[\"From playlists\"],\"VskHIx\":[\"Delete category\"],\"VyTYmS\":[\"Change avatar\"],\"W5A0Ly\":[\"An unexpected error occurred.\"],\"W9FRBT\":[\"Like\"],\"WAcnnR\":[\"The image couldn't be uploaded.\"],\"WE_r9K\":[\"Failed to create category\"],\"WfffrI\":[\"slug\"],\"WhimMi\":[\"Reset failed\"],\"WpXcBJ\":[\"Nothing here yet.\"],\"XHqhoS\":[\"Couldn't load the chat.\"],\"XJVYo8\":[\"Why & where\"],\"XJy2oN\":[\"Logging in…\"],\"Xan6QP\":[\"New dump\"],\"XgRtUf\":[\"Could not change password\"],\"XnL-Eu\":[\"No users match \\\"\",[\"q\"],\"\\\".\"],\"Xs2Lez\":[\"This reset link is missing or malformed.\"],\"YK1Dhc\":[\"a post\"],\"YWnSuq\":[\"This unsubscribe link is missing or malformed.\"],\"YZxXK8\":[\"Close emoji picker\"],\"YcjhhX\":[\"User management\"],\"Ye9RMF\":[\"<0>\",[\"0\"],\" liked your comment on <1>\",[\"1\"],\"\"],\"YpkCca\":[\"No followed playlists yet.\"],\"Z5HWHd\":[\"On\"],\"ZBdbv9\":[\"Edit title\"],\"ZCpU0u\":[\"No playlists match \\\"\",[\"q\"],\"\\\".\"],\"ZWxjuD\":[\"Some images couldn't be uploaded.\"],\"ZmD2o6\":[\"Create & Add\"],\"_3O5R_\":[\"Request failed\"],\"_DwR-n\":[\"Creating…\"],\"_I1_HN\":[\"Lowercase letters, numbers and single hyphens only\"],\"_R_sGB\":[\"Password changed successfully.\"],\"_aept4\":[\"Post reply\"],\"_cF7Rs\":[\"Volume\"],\"_karOe\":[\"You're offline — not sent\"],\"_nT6AE\":[\"New password\"],\"_t4W-i\":[\"From people\"],\"aAIQg2\":[\"Appearance\"],\"aDvLhk\":[\"Add a comment…\"],\"az8lvo\":[\"Off\"],\"azhsgG\":[\"Their old name keeps leading to them, and nobody else can take it.\"],\"b3Thhd\":[\"Upload failed\"],\"b8XMJ8\":[[\"visibleCount\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}]],\"bQhwn-\":[\"Loading playlist…\"],\"b_1P1D\":[\"Renaming…\"],\"bzSI52\":[\"Discard\"],\"cILfnJ\":[\"Remove file\"],\"cNu4my\":[[\"0\"],\" more online: \",[\"restNames\"]],\"cYP9Sb\":[\"+ Playlist\"],\"cbeBbZ\":[\"At least \",[\"0\"],\" characters\"],\"cnGeoo\":[\"Delete\"],\"dAs22m\":[\"Replace file\"],\"dEgA5A\":[\"Cancel\"],\"dMizp8\":[\"New playlist\"],\"dau1Pl\":[[\"unseenCount\"],\" new messages\"],\"digest.monthly\":[\"Monthly recap\"],\"digest.monthly.off\":[\"Turn off monthly recaps\"],\"digest.weekly\":[\"Weekly digest\"],\"digest.weekly.off\":[\"Turn off weekly digests\"],\"dqwCfR\":[\"Couldn't load a preview for this link — check it, or post it as-is and refresh the preview later.\"],\"dznB-Y\":[\"Only images can be dropped here.\"],\"eFSqvc\":[\"Failed to post reply\"],\"eIEHHV\":[\"Change skin tone\"],\"eOfXq3\":[\"Title is required.\"],\"ePK91l\":[\"Edit\"],\"eaUTwS\":[\"Send reset link\"],\"ecUA8p\":[\"Today\"],\"ef9nPf\":[\"Loading dump…\"],\"eloqXQ\":[\"You can post it anyway.\"],\"email.digest.addedTo\":[\"added to \",[\"playlist\"]],\"email.digest.andMore\":[\"and \",[\"n\"],\" more\"],\"email.digest.by\":[\"by @\",[\"username\"]],\"email.digest.chatWeek\":[[\"n\",\"plural\",{\"one\":[\"#\",\" message this week\"],\"other\":[\"#\",\" messages this week\"]}]],\"email.digest.community\":[\"Community\"],\"email.digest.count.comments\":[[\"n\",\"plural\",{\"one\":[\"comment\"],\"other\":[\"comments\"]}]],\"email.digest.count.followers\":[[\"n\",\"plural\",{\"one\":[\"new follower\"],\"other\":[\"new followers\"]}]],\"email.digest.count.likes\":[[\"n\",\"plural\",{\"one\":[\"comment like\"],\"other\":[\"comment likes\"]}]],\"email.digest.count.mentions\":[[\"n\",\"plural\",{\"one\":[\"mention\"],\"other\":[\"mentions\"]}]],\"email.digest.count.playlistFollows\":[[\"n\",\"plural\",{\"one\":[\"playlist follow\"],\"other\":[\"playlist follows\"]}]],\"email.digest.count.upvotes\":[[\"n\",\"plural\",{\"one\":[\"upvote\"],\"other\":[\"upvotes\"]}]],\"email.digest.forYou\":[\"For you\"],\"email.digest.hl.comment\":[[\"who\"],\" commented on your dump \",[\"target\"]],\"email.digest.hl.commentLike\":[[\"who\"],\" liked your comment on \",[\"target\"]],\"email.digest.hl.mentionComment\":[[\"who\"],\" mentioned you in a comment on \",[\"target\"]],\"email.digest.hl.mentionIn\":[[\"who\"],\" mentioned you in \",[\"target\"]],\"email.digest.hl.mentionPlaylist\":[[\"who\"],\" mentioned you in the playlist \",[\"target\"]],\"email.digest.hl.playlistFollow\":[[\"who\"],\" followed your playlist \",[\"target\"]],\"email.digest.hl.userFollow\":[[\"who\"],\" started following you\"],\"email.digest.inChat\":[\"In the chat\"],\"email.digest.invitedBy\":[\"invited by @\",[\"username\"]],\"email.digest.invitedByYou\":[\"invited by you\"],\"email.digest.monthly.reason\":[\"You're getting the monthly recap because it's on in your settings.\"],\"email.digest.monthly.subject\":[\"The month on \",[\"site\"],\", \",[\"range\"]],\"email.digest.monthly.title\":[\"The month on \",[\"site\"]],\"email.digest.more\":[[\"n\"],\" more on your Followed feed\"],\"email.digest.network\":[\"From people you follow\"],\"email.digest.newMembers\":[\"New members\"],\"email.digest.newPlaylists\":[\"New public playlists\"],\"email.digest.openChat\":[\"Open the chat\"],\"email.digest.playlistMeta\":[\"by @\",[\"username\"],\" · \",[\"n\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}]],\"email.digest.pre.members\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new member\"],\"other\":[\"#\",\" new members\"]}]],\"email.digest.pre.network\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new dump from people you follow\"],\"other\":[\"#\",\" new dumps from people you follow\"]}]],\"email.digest.pre.playlists\":[[\"n\",\"plural\",{\"one\":[\"#\",\" new playlist\"],\"other\":[\"#\",\" new playlists\"]}]],\"email.digest.pre.topMonth\":[[\"n\",\"plural\",{\"one\":[\"the most upvoted dump of the month\"],\"other\":[\"the \",\"#\",\" most upvoted dumps of the month\"]}]],\"email.digest.pre.topWeek\":[[\"n\",\"plural\",{\"one\":[\"the most upvoted dump of the week\"],\"other\":[\"the \",\"#\",\" most upvoted dumps of the week\"]}]],\"email.digest.seeAll\":[[\"n\",\"plural\",{\"one\":[\"See the notification\"],\"other\":[\"See all \",\"#\",\" notifications\"]}]],\"email.digest.stat.chat\":[[\"n\",\"plural\",{\"one\":[\"chat message\"],\"other\":[\"chat messages\"]}]],\"email.digest.stat.dumps\":[[\"n\",\"plural\",{\"one\":[\"dump\"],\"other\":[\"dumps\"]}]],\"email.digest.theChat\":[\"the chat\"],\"email.digest.topMonth\":[\"Top this month\"],\"email.digest.topWeek\":[\"Top this week\"],\"email.digest.totals\":[[\"dumps\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}],\", \",[\"upvotes\",\"plural\",{\"one\":[\"#\",\" upvote\"],\"other\":[\"#\",\" upvotes\"]}],\" and \",[\"comments\",\"plural\",{\"one\":[\"#\",\" comment\"],\"other\":[\"#\",\" comments\"]}],\".\"],\"email.digest.upload\":[\"upload\"],\"email.digest.weekly.reason\":[\"You're getting the weekly digest because it's on in your settings.\"],\"email.digest.weekly.subject\":[\"Your week on \",[\"site\"],\", \",[\"range\"]],\"email.digest.weekly.title\":[\"Your week, \",[\"username\"]],\"email.digest.yours\":[\"yours\"],\"email.reset.body\":[\"Someone asked to reset the password of your \",[\"site\"],\" account. The button below lets you choose a new one. It works once, for \",[\"hours\",\"plural\",{\"one\":[\"#\",\" hour\"],\"other\":[\"#\",\" hours\"]}],\".\"],\"email.reset.button\":[\"Reset password\"],\"email.reset.fallback\":[\"If the button doesn't work, paste this link into your browser:\"],\"email.reset.hi\":[\"Hi \",[\"username\"],\",\"],\"email.reset.ignore\":[\"If you didn't ask for this, ignore this email. Your password stays the same.\"],\"email.reset.preheader\":[\"The link works once, for \",[\"hours\",\"plural\",{\"one\":[\"#\",\" hour\"],\"other\":[\"#\",\" hours\"]}],\".\"],\"email.reset.reason\":[\"You're getting this email because a password reset was requested for this address.\"],\"email.reset.subject\":[\"Reset your \",[\"site\"],\" password\"],\"email.reset.title\":[\"Reset your password\"],\"email.settings\":[\"Settings\"],\"email.welcome.body\":[\"# Welcome to \",[\"site\"],\"!\\n\\nHi **\",[\"username\"],\"**,\\n\\nYour account has been created successfully. Welcome aboard!\"],\"email.welcome.button\":[\"Open \",[\"site\"]],\"email.welcome.digests\":[\"Once a week you'll get a digest of what happened here, and once a month a recap of the most upvoted dumps. You can turn either off in your settings.\"],\"email.welcome.preheader\":[\"Your account is ready.\"],\"email.welcome.reason\":[\"You're getting this email because an account was created on \",[\"site\"],\" with this address.\"],\"email.welcome.subject\":[\"Welcome to \",[\"site\"]],\"en9o7K\":[\"Failed to post comment\"],\"etFQQS\":[\"What makes it worth it?\"],\"f3cEDO\":[\"Change image\"],\"fC6mXb\":[\"Upvote\"],\"fI-mNw\":[\"Playlists\"],\"fgLNSM\":[\"Register\"],\"gANddk\":[\"Uploading…\"],\"gGx5tM\":[\"Editing\"],\"gIQQwD\":[\"Failed to load\"],\"gIpLuy\":[\"A link is required\"],\"gL9KKi\":[\"Who can see it\"],\"gLfZlz\":[\"Add to playlist\"],\"gjJ-sb\":[\"Can't connect to the live updates server. Upvotes and notifications may not sync until it reconnects.\"],\"goWEhR\":[\"Previous track\"],\"hBuUKa\":[\"Change password…\"],\"hD7w09\":[\"You've reached the end.\"],\"hJSliC\":[\"<0>\",[\"0\"],\" posted <1>\",[\"1\"],\"\"],\"hT8hgO\":[\"Reset to the suggested title\"],\"hYgDIe\":[\"Create\"],\"he3ygx\":[\"Copy\"],\"hpCc_Z\":[\"Close anyway\"],\"i7K_Te\":[\"Who am I?\"],\"iDNBZe\":[\"Notifications\"],\"iWpEwy\":[\"Go home\"],\"ipYn7W\":[\"If that address is registered you'll receive a reset link shortly.\"],\"isRobC\":[\"New\"],\"jTGs8A\":[\"This site doesn't send email, so no digest goes out.\"],\"jbernk\":[\"Loading profile…\"],\"jnrIbz\":[\"Upvoted (\",[\"0\"],\")\"],\"joEmfT\":[\"Server unreachable\"],\"jrZTZl\":[\"No dumps yet. Be the first!\"],\"k112DD\":[\"Load older messages\"],\"k36iJ-\":[\"Failed to save category\"],\"k4b5_X\":[\"edited\"],\"kLttbL\":[\"Registration failed\"],\"lUanmi\":[\"You'll be notified when someone follows your playlists, upvotes your dumps, or posts new content.\"],\"lY5h1V\":[[\"0\",\"plural\",{\"one\":[\"#\",\" dump\"],\"other\":[\"#\",\" dumps\"]}]],\"lcfvr_\":[\"Delete this comment?\"],\"lpIMne\":[\"Passwords do not match\"],\"mDJo8W\":[\"Dumps (\",[\"0\"],\")\"],\"mb_Wju\":[\"Drop a file here, or paste one\"],\"mt6O6E\":[\"This is a mirage.\"],\"nbm5sI\":[\"No dumps match \\\"\",[\"q\"],\"\\\".\"],\"nrjqON\":[\"Checking invite…\"],\"nwtY4N\":[\"Something went wrong\"],\"nx4kaN\":[\"Could not save\"],\"oTYvvN\":[[\"0\",\"plural\",{\"one\":[\"and \",\"#\",\" earlier dump\"],\"other\":[\"and \",\"#\",\" earlier dumps\"]}]],\"ogtYkT\":[\"Password updated\"],\"pCpd9p\":[\"<0>\",[\"0\"],\" mentioned you in <1>\",[\"where\"],\"\"],\"pSheLH\":[\"No invitees yet.\"],\"pd3FCq\":[\"That file is over \",[\"limit\"],\".\"],\"pvnfJD\":[\"Dark\"],\"qIMfNQ\":[\"Delete playlist\"],\"qItrqH\":[\"You won't get the monthly recap any more. You can turn it back on in your settings.\"],\"qbDAcy\":[\"Dump it\"],\"qgx_78\":[\"Follow some public playlists to see their dumps here.\"],\"qnUJBI\":[\"The new order couldn't be saved. Try again.\"],\"qsqt0w\":[\"Invite link\"],\"qvFa8r\":[\"public\"],\"qvz_Pp\":[\"Dump\"],\"rCbqPX\":[\"This invite link is missing, expired, or already used.\"],\"rDMzDU\":[\"Type a message…\"],\"rg9pXu\":[\"Search failed\"],\"rn6SBY\":[\"Unmute\"],\"sEXJPi\":[\"Link or file\"],\"sGeXL3\":[\"Thumbnail\"],\"sQia9P\":[\"Log in\"],\"sTiqbm\":[\"invited by\"],\"sdP5Aa\":[\"[deleted]\"],\"sgX7B-\":[\"Single use, valid for 7 days.\"],\"shHs8T\":[\"Enter a query to search.\"],\"smeBfS\":[\"Invalid invite\"],\"teaneZ\":[\"image\"],\"tfDRzk\":[\"Save\"],\"tvmuQ0\":[\"Color scheme\"],\"u1lDX2\":[\"Fetching preview…\"],\"uD0qXQ\":[\"Drop a file here\"],\"uISnsY\":[\"Could not delete\"],\"uMGUnV\":[\"No playlists yet.\"],\"ub1EEL\":[\"edited \",[\"0\"]],\"uomLas\":[\"Add category\"],\"v-Wp--\":[\"Open source\"],\"vJBF1r\":[\"Posting…\"],\"vLhLLO\":[\"Notifications (\",[\"unreadNotificationCount\"],\" unread)\"],\"vQMkHu\":[\"Remove vote\"],\"vuosjb\":[\"User menu\"],\"wckWOP\":[\"Manage\"],\"wixIgH\":[\"Already have an account? <0>Log in\"],\"wtxFp4\":[\"File too large (max \",[\"max\"],\").\"],\"x6tjuK\":[\"Default tab\"],\"xEWkgZ\":[\"← Back to all dumps\"],\"xPHtx0\":[\"Submit search\"],\"xVuNgt\":[\"+ New playlist\"],\"xc9O_u\":[\"Delete dump\"],\"y1G_DI\":[\"Could not rename\"],\"y6sq5j\":[\"Following\"],\"y7oaHj\":[\"Playlist title\"],\"yA_6BX\":[\"View all →\"],\"yBBtRm\":[\"Follow some users to see their dumps here.\"],\"yQ2kGp\":[\"Load more\"],\"y_0uwd\":[\"Yesterday\"],\"yxlcv5\":[\"Turn off the monthly recap?\"],\"yz7wBu\":[\"Close\"],\"yzF66j\":[\"Link\"],\"z0ROB3\":[\"Remove like\"],\"z1uNN0\":[\"No emoji found.\"],\"zVuxvN\":[\"Refreshing…\"],\"zwBp5t\":[\"Private\"]}"); \ No newline at end of file diff --git a/locales/en.po b/locales/en.po index dfdaa91b..7c2bc85b 100644 --- a/locales/en.po +++ b/locales/en.po @@ -120,13 +120,13 @@ msgstr "yours" #. js-lingui-explicit-id #: api/mail/digest.ts:221 -#: src/pages/Settings.tsx:190 +#: src/pages/Settings.tsx:157 msgid "digest.monthly" msgstr "Monthly recap" #. js-lingui-explicit-id #: api/mail/digest.ts:221 -#: src/pages/Settings.tsx:181 +#: src/pages/Settings.tsx:148 msgid "digest.weekly" msgstr "Weekly digest" @@ -501,7 +501,7 @@ msgstr "A link is required" msgid "a post" msgstr "a post" -#: src/pages/Settings.tsx:108 +#: src/pages/Settings.tsx:86 msgid "Account" msgstr "Account" @@ -550,7 +550,7 @@ msgstr "Already have an account? <0>Log in" msgid "An unexpected error occurred." msgstr "An unexpected error occurred." -#: src/pages/Settings.tsx:135 +#: src/pages/Settings.tsx:102 msgid "Appearance" msgstr "Appearance" @@ -565,7 +565,7 @@ msgstr "At least {0} characters" msgid "At most {maxLength} characters." msgstr "At most {maxLength} characters." -#: src/pages/Settings.tsx:51 +#: src/pages/Settings.tsx:44 msgid "Auto" msgstr "Auto" @@ -629,7 +629,7 @@ msgstr "Change image" msgid "Change password" msgstr "Change password" -#: src/pages/Settings.tsx:130 +#: src/pages/Settings.tsx:97 msgid "Change password…" msgstr "Change password…" @@ -678,7 +678,7 @@ msgstr "Close without posting? The attached file won't be kept — everything yo msgid "Collapse player" msgstr "Collapse player" -#: src/pages/Settings.tsx:144 +#: src/pages/Settings.tsx:111 msgid "Color scheme" msgstr "Color scheme" @@ -752,11 +752,11 @@ msgstr "Creating…" msgid "Current password" msgstr "Current password" -#: src/pages/Settings.tsx:53 +#: src/pages/Settings.tsx:46 msgid "Dark" msgstr "Dark" -#: src/pages/Settings.tsx:155 +#: src/pages/Settings.tsx:122 msgid "Default tab" msgstr "Default tab" @@ -944,7 +944,7 @@ msgstr "Editing" msgid "Editing {0}" msgstr "Editing {0}" -#: src/pages/Settings.tsx:179 +#: src/pages/Settings.tsx:146 msgid "Email" msgstr "Email" @@ -1017,7 +1017,7 @@ msgstr "Failed to update avatar" msgid "Failed to update role" msgstr "Failed to update role" -#: src/pages/Settings.tsx:153 +#: src/pages/Settings.tsx:120 msgid "Feeds" msgstr "Feeds" @@ -1052,7 +1052,7 @@ msgstr "Follow some users to see their dumps here." #: src/components/feed/FeedTabBar.tsx:43 #: src/components/profile/ProfileFollowTab.tsx:163 -#: src/pages/Settings.tsx:60 +#: src/pages/Settings.tsx:53 #: src/pages/UserPublicProfile.tsx:157 msgid "Followed" msgstr "Followed" @@ -1087,7 +1087,7 @@ msgid "Go to login" msgstr "Go to login" #: src/components/feed/FeedTabBar.tsx:39 -#: src/pages/Settings.tsx:57 +#: src/pages/Settings.tsx:50 msgid "Hot" msgstr "Hot" @@ -1132,7 +1132,7 @@ msgid "Invitees" msgstr "Invitees" #: src/components/feed/FeedTabBar.tsx:41 -#: src/pages/Settings.tsx:59 +#: src/pages/Settings.tsx:52 msgid "Journal" msgstr "Journal" @@ -1144,7 +1144,7 @@ msgstr "Keep editing" msgid "Letters, numbers and underscores only" msgstr "Letters, numbers and underscores only" -#: src/pages/Settings.tsx:52 +#: src/pages/Settings.tsx:45 msgid "Light" msgstr "Light" @@ -1276,7 +1276,7 @@ msgstr "new" #: src/components/chat/ChatModal.tsx:669 #: src/components/feed/FeedTabBar.tsx:40 -#: src/pages/Settings.tsx:58 +#: src/pages/Settings.tsx:51 msgid "New" msgstr "New" @@ -1368,7 +1368,7 @@ msgstr "Nothing here yet." #: src/components/layout/NotificationBell.tsx:54 #: src/pages/Notifications.tsx:308 #: src/pages/Notifications.tsx:364 -#: src/pages/Settings.tsx:164 +#: src/pages/Settings.tsx:131 msgid "Notifications" msgstr "Notifications" @@ -1376,11 +1376,11 @@ msgstr "Notifications" msgid "Notifications ({unreadNotificationCount} unread)" msgstr "Notifications ({unreadNotificationCount} unread)" -#: src/pages/Settings.tsx:65 +#: src/pages/Settings.tsx:58 msgid "Off" msgstr "Off" -#: src/pages/Settings.tsx:64 +#: src/pages/Settings.tsx:57 msgid "On" msgstr "On" @@ -1413,7 +1413,7 @@ msgstr "Overview" msgid "Page not found" msgstr "Page not found" -#: src/pages/Settings.tsx:124 +#: src/pages/Settings.tsx:91 #: src/pages/UserLogin.tsx:82 msgid "Password" msgstr "Password" @@ -1690,8 +1690,8 @@ msgstr "Set new password" #: src/components/layout/UserMenu.tsx:89 #: src/components/profile/ProfileHeader.tsx:177 -#: src/pages/Settings.tsx:77 -#: src/pages/Settings.tsx:105 +#: src/pages/Settings.tsx:69 +#: src/pages/Settings.tsx:83 msgid "Settings" msgstr "Settings" @@ -1718,7 +1718,7 @@ msgstr "Some images couldn't be uploaded." msgid "Something went wrong" msgstr "Something went wrong" -#: src/pages/Settings.tsx:166 +#: src/pages/Settings.tsx:133 msgid "Sounds" msgstr "Sounds" @@ -1726,7 +1726,7 @@ msgstr "Sounds" msgid "Start fresh" msgstr "Start fresh" -#: src/pages/Settings.tsx:137 +#: src/pages/Settings.tsx:104 msgid "Style" msgstr "Style" @@ -1778,7 +1778,7 @@ msgstr "This page does not exist." msgid "This reset link is missing or malformed." msgstr "This reset link is missing or malformed." -#: src/pages/Settings.tsx:202 +#: src/pages/Settings.tsx:169 msgid "This site doesn't send email, so no digest goes out." msgstr "This site doesn't send email, so no digest goes out." @@ -1915,7 +1915,7 @@ msgstr "User menu" #: src/components/profile/ProfileManageTab.tsx:61 #: src/components/profile/UsernameField.tsx:55 -#: src/pages/Settings.tsx:110 +#: src/pages/Settings.tsx:88 #: src/pages/UserLogin.tsx:75 #: src/pages/UserRegister.tsx:137 msgid "Username" @@ -1970,10 +1970,6 @@ msgstr "Write a reply…" msgid "Yesterday" msgstr "Yesterday" -#: src/pages/Settings.tsx:116 -msgid "You can change it once every {renameDays} days. Links to your old name keep working, and nobody else can take it." -msgstr "You can change it once every {renameDays} days. Links to your old name keep working, and nobody else can take it." - #: src/components/dump/create/DuplicateNotice.tsx:61 msgid "You can post it anyway." msgstr "You can post it anyway." diff --git a/locales/fr.mjs b/locales/fr.mjs index 8df58b1f..a26fb0bc 100644 --- a/locales/fr.mjs +++ b/locales/fr.mjs @@ -1 +1 @@ -/*eslint-disable*/export const messages=JSON.parse("{\"-K9EZb\":[\"Ajouter un e-mail…\"],\"-Ya-b9\":[\"Enregistrement échoué\"],\"-iDCtG\":[\"Vos paramètres\"],\"-siMqD\":[\"Journal\"],\"1CalO6\":[\"Style\"],\"1HfJWf\":[\"Rechercher des recos, utilisateurs, collections…\"],\"1QfxQT\":[\"Fermer\"],\"1YKhJ3\":[\"1 nouveau message\"],\"1njn7W\":[\"Clair\"],\"1utXA6\":[\"Recos\"],\"26iNma\":[\"Publier le commentaire\"],\"2DoBvq\":[\"Flux\"],\"2Hlmdt\":[\"Écrire une réponse…\"],\"2_DHgA\":[\"Identifiant d’URL\"],\"2nrsEn\":[\"message supprimé\"],\"2wxgft\":[\"Renommer\"],\"2ygf_L\":[\"← Retour\"],\"3KKSM4\":[\"privé\"],\"3QuNUr\":[\"Plusieurs personnes écrivent…\"],\"3SEwY1\":[\"Supprimer la catégorie \\\"\",[\"0\"],\"\\\" ? Cette action est irréversible.\"],\"3qkggm\":[\"Plein écran\"],\"3yfh3D\":[\"<0>\",[\"0\"],\" a suivi votre collection <1>\",[\"1\"],\"\"],\"48e2R8\":[\"Modifier l'e-mail\"],\"49voTZ\":[[\"label\"],\" (\",[\"count\"],\")\"],\"4B6w_o\":[\"Recommandé !\"],\"4GKuCs\":[\"Connexion échouée\"],\"4HH9iB\":[\"Aucun abonnement pour le moment.\"],\"4OjqAQ\":[\"Continuer l'édition\"],\"4RtQ1k\":[\"Ne plus suivre \",[\"targetUsername\"]],\"4c-qBx\":[\"Votre mot de passe a été modifié. Vous pouvez maintenant vous connecter.\"],\"4yj9xV\":[\"Les mises à jour en direct sont temporairement interrompues. Tentative de reconnexion…\"],\"5KHDCi\":[[\"0\"],\" et \",[\"1\"],\" sont en train d'écrire…\"],\"5NZ3tg\":[\"Désactivation…\"],\"5TviPn\":[\"Annuler la recherche\"],\"5cC8f2\":[\"Modifié le \",[\"0\"]],\"5oD9f_\":[\"Plus tôt\"],\"6Qly-0\":[\"un commentaire\"],\"6YE0P6\":[\"Édition de \",[\"0\"]],\"6YtxFj\":[\"Nom\"],\"6_dCYd\":[\"Aperçu\"],\"6gRgw8\":[\"Réessayer\"],\"7PHCIN\":[\"Annuler la suppression\"],\"7PzzBU\":[\"Utilisateur\"],\"7d1a0d\":[\"Public\"],\"7sNhEz\":[\"Nom d'utilisateur\"],\"8-8abB\":[\"Déconnecté — reconnexion…\"],\"8F1i42\":[\"Page introuvable\"],\"8JplJJ\":[\"Désabonné\"],\"8Ug9jB\":[\"Connexe\"],\"8ZsakT\":[\"Mot de passe\"],\"92o1-U\":[\"Suppression échouée\"],\"9BruTc\":[\"Pourquoi ?\"],\"9l4qcT\":[\"Déposez un fichier de remplacement ici\"],\"9uI_rE\":[\"Annuler\"],\"A0y396\":[\"+ Inviter quelqu'un\"],\"A1taO8\":[\"Rechercher\"],\"AEfu-D\":[\"Dans les collections\"],\"AHZflp\":[\"Tribune\"],\"AQbgNR\":[\"Suivre \",[\"targetUsername\"]],\"AS7WoE\":[\"Repartir de zéro\"],\"ATGYL1\":[\"Adresse e-mail\"],\"AZctoV\":[[\"0\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}]],\"Ade-6d\":[\"Mises à jour en direct indisponibles.\"],\"AeXO77\":[\"Compte\"],\"AhNP40\":[\"Position\"],\"AnRu_j\":[\"Annuler la réponse\"],\"B4LeYP\":[\"Échec de la suppression (\",[\"0\"],\")\"],\"BjitBo\":[\"Télécharger \",[\"0\"]],\"BlpJ2G\":[\"Reprise de votre brouillon.\"],\"CI50ct\":[\"Voté\"],\"C_qQE4\":[\"Déjà recommandé par \",[\"0\"],\" \",[\"1\"]],\"Cf_gxi\":[\"Ne plus recevoir le résumé hebdomadaire ?\"],\"Cj24wt\":[\"Inscription…\"],\"CnqayG\":[\"La mise à jour a échoué. Réessayez.\"],\"D8DRYV\":[\"Supprimer ce message ?\"],\"DI3NBO\":[\"Ouvrir sur \",[\"0\"]],\"DPfwMq\":[\"Terminé\"],\"DdeHXH\":[\"Supprimer cette reco ? Cette action est irréversible.\"],\"Dp1JhP\":[\"<0>\",[\"0\"],\" a voté pour <1>\",[\"1\"],\"\"],\"E417RG\":[\"Vous ne recevrez plus le résumé hebdomadaire. Vous pouvez le réactiver dans vos paramètres.\"],\"EBU6O6\":[\"Connexion perdue — ce message n'a peut-être pas été envoyé\"],\"ECiS12\":[\"Voir la reco →\"],\"ED8n0z\":[\"la tribune\"],\"EarrCe\":[\"Rechercher un emoji\"],\"Evcm2Z\":[\"Fermer sans publier ? Le fichier joint sera perdu — ce que vous avez écrit sera conservé.\"],\"EwVrWF\":[\"Le dump n'a pas pu être retiré. Réessayez.\"],\"ExR0Fr\":[\"L'URL est obligatoire.\"],\"F5Js1v\":[\"Ne plus suivre la collection\"],\"FdTlEG\":[\"Piste \",[\"0\"]],\"FgAxTj\":[\"Se déconnecter\"],\"Fxf4jq\":[\"Description (facultatif)\"],\"GCWCCo\":[\"Fermer le lecteur\"],\"GDvlUT\":[\"Rôle\"],\"GLukaH\":[\"Modérateur\"],\"GNSsCc\":[\"Impossible de créer la collection\"],\"GQ38dl\":[[\"unreadChatCount\"],\" messages non lus\"],\"GY6uSc\":[\"Suivant →\"],\"GbqhrN\":[[\"0\"],\"–\",[\"1\"],\" caractères : lettres, chiffres ou tirets bas\"],\"GptGxg\":[\"Changer le mot de passe\"],\"Gv5nA5\":[\"Format de réponse inattendu\"],\"H4MGkI\":[\"Agrandir le lecteur\"],\"H4o4sk\":[\"Personne pour le moment.\"],\"H8pzW-\":[\"Impossible de mettre à jour l'avatar\"],\"HTLDA4\":[\"Chargement…\"],\"H_PnT8\":[\"Insérer un emoji\"],\"I-x669\":[\"Invités\"],\"IO9KL_\":[\"Piste suivante\"],\"IZX7TO\":[\"Impossible de générer une invitation\"],\"IagCbF\":[\"URL\"],\"IgrLD_\":[\"Pause\"],\"IhK90M\":[\"Lettres, chiffres et tirets bas seulement\"],\"ImOQa9\":[\"Répondre\"],\"JJ-Bhk\":[\"Votre adresse e-mail\"],\"JLasL-\":[\"Désabonnement impossible\"],\"JRQitQ\":[\"Confirmer le nouveau mot de passe\"],\"JXr41k\":[\"<0>\",[\"0\"],\" a commenté sur <1>\",[\"1\"],\"\"],\"Jd58Fo\":[\"Tendances\"],\"Jf0PuK\":[\"Aller à la connexion\"],\"JlFcis\":[\"Envoyer\"],\"JzARG2\":[\"Les liens vers l’ancienne adresse marchent toujours.\"],\"K7tIrx\":[\"Catégorie\"],\"KDGWg5\":[\"Retirer de la collection\"],\"KDw4GX\":[\"Réessayer\"],\"KKZxBU\":[\"Envoi \",[\"0\"],\" %\"],\"K_F6pa\":[\"Enregistrement…\"],\"KtYF-t\":[\"Sons\"],\"L-rMC9\":[\"Réinitialiser par défaut\"],\"L7_rsx\":[\"Envoi de l'image…\"],\"MHrjPM\":[\"Titre\"],\"MKEPCY\":[\"Suivre\"],\"MTb2Ot\":[\"Aucun message pour l'instant. Dites bonjour !\"],\"Mj6oCJ\":[[\"0\"],\" est en train d'écrire…\"],\"N40H-G\":[\"Tout\"],\"N6j2JH\":[\"Modifier \",[\"0\"]],\"NUrY9o\":[\"Catégories\"],\"O32mmL\":[\"Modifier la description\"],\"O3oNi5\":[\"E-mail\"],\"Oprv1v\":[\"Mot de passe (min. \",[\"0\"],\" caractères)\"],\"Oz0N9s\":[\"nouveau\"],\"P5MuiB\":[\"Ajouter une image\"],\"PN3pBl\":[\"Pas de réponse du serveur — ce message n'a peut-être pas été envoyé\"],\"PUe8Yk\":[\"Réduire le lecteur\"],\"PiH3UR\":[\"Copié !\"],\"Pn2B7_\":[\"Mot de passe actuel\"],\"Pwqkdw\":[\"Chargement…\"],\"Q4WUjR\":[\"Rechercher un emoji…\"],\"Q6n4F4\":[\"Actualiser les métadonnées\"],\"QGaQRo\":[\"Erreur lors de la mise à jour du rôle\"],\"QKsaQr\":[\"ou <0>parcourir les fichiers\"],\"QLtPBd\":[\"Aucune reco dans cette collection pour l'instant.\"],\"QctOgc\":[\"Impossible de joindre le serveur. Vérifiez votre connexion et réessayez.\"],\"R7SsBE\":[\"Couper le son\"],\"R9Khdg\":[\"Auto\"],\"RCcPrX\":[\"Supprimer cette collection ? Cette action est irréversible.\"],\"RTksSy\":[\"<0>\",[\"0\"],\" a commencé à vous suivre\"],\"RaKjrM\":[\"Impossible d'enregistrer la modification\"],\"RcUHRT\":[\"Suivi\"],\"RfwZxd\":[\"Réinitialiser le mot de passe\"],\"Rrp6-J\":[\"Se connecter pour aimer\"],\"RvVJBH\":[\"Actualisation du flux…\"],\"S1xV5B\":[[\"maxLength\"],\" caractères au maximum.\"],\"SBTElJ\":[\"Recherche…\"],\"SMaFdc\":[\"Se désabonner\"],\"Sad2tK\":[\"Envoi…\"],\"So7gCD\":[\"Collez un lien…\"],\"StovX6\":[\"Rien à voir, circulez.\"],\"Sxm8rQ\":[\"Utilisateurs\"],\"T62TQc\":[\"Vous pouvez le changer une fois tous les \",[\"renameDays\"],\" jours. Les liens vers votre ancien nom marchent toujours, et personne d’autre ne peut le prendre.\"],\"T8ejtZ\":[\"Ce commentaire n'a pas pu être supprimé. Réessayez.\"],\"T91vKp\":[\"Lecture\"],\"T9bjWt\":[\"<0>\",[\"0\"],\" a été ajouté à <1>\",[\"1\"],\"\"],\"TAtbho\":[\"Chargement des messages…\"],\"TN382O\":[\"Lien invalide\"],\"Tv9vbB\":[\"Suivre la collection\"],\"Tz0i8g\":[\"Paramètres\"],\"U3pytU\":[\"Administrateur\"],\"UNMVei\":[\"Mot de passe oublié ?\"],\"UOZith\":[\"Publication échouée\"],\"URAieT\":[\"Se connecter pour voter\"],\"V38P77\":[\"Seuls son auteur et les modérateurs peuvent modifier ce dump.\"],\"VCoEm-\":[\"Retour à la connexion\"],\"VKsaTi\":[\"En réponse à\"],\"VLAd8f\":[\"Max \",[\"limit\"]],\"V_e7nf\":[\"Définir un nouveau mot de passe\"],\"VnNJbN\":[\"De collections\"],\"VskHIx\":[\"Supprimer la catégorie\"],\"VyTYmS\":[\"Changer l'avatar\"],\"W5A0Ly\":[\"Une erreur inattendue s'est produite.\"],\"W9FRBT\":[\"Aimer\"],\"WAcnnR\":[\"L'image n'a pas pu être envoyée.\"],\"WE_r9K\":[\"Échec de la création de la catégorie\"],\"WfffrI\":[\"identifiant\"],\"WhimMi\":[\"Échec de la réinitialisation\"],\"WpXcBJ\":[\"Rien ici pour l'instant.\"],\"XHqhoS\":[\"Impossible de charger le chat.\"],\"XJVYo8\":[\"Pourquoi & où\"],\"XJy2oN\":[\"Connexion…\"],\"Xan6QP\":[\"Nouvelle reco\"],\"XgRtUf\":[\"Impossible de changer le mot de passe\"],\"XnL-Eu\":[\"Aucun utilisateur ne correspond à « \",[\"q\"],\" ».\"],\"Xs2Lez\":[\"Ce lien de réinitialisation est absent ou malformé.\"],\"YK1Dhc\":[\"une publication\"],\"YWnSuq\":[\"Ce lien de désabonnement est absent ou mal formé.\"],\"YZxXK8\":[\"Fermer le sélecteur d'emoji\"],\"YcjhhX\":[\"Gestion utilisateur\"],\"Ye9RMF\":[\"<0>\",[\"0\"],\" a aimé votre commentaire sur <1>\",[\"1\"],\"\"],\"YpkCca\":[\"Pas encore de collections suivies.\"],\"Z5HWHd\":[\"Activés\"],\"ZBdbv9\":[\"Modifier le titre\"],\"ZCpU0u\":[\"Aucune collection ne correspond à « \",[\"q\"],\" ».\"],\"ZWxjuD\":[\"Certaines images n'ont pas pu être envoyées.\"],\"ZmD2o6\":[\"Créer et ajouter\"],\"_3O5R_\":[\"Échec de la demande\"],\"_DwR-n\":[\"Création…\"],\"_I1_HN\":[\"Minuscules, chiffres et tirets simples seulement\"],\"_R_sGB\":[\"Mot de passe modifié avec succès.\"],\"_aept4\":[\"Publier la réponse\"],\"_cF7Rs\":[\"Volume\"],\"_karOe\":[\"Hors ligne — message non envoyé\"],\"_nT6AE\":[\"Nouveau mot de passe\"],\"_t4W-i\":[\"De personnes\"],\"aAIQg2\":[\"Apparence\"],\"aDvLhk\":[\"Ajouter un commentaire…\"],\"az8lvo\":[\"Désactivés\"],\"azhsgG\":[\"Son ancien nom mène toujours à son profil, et personne d’autre ne peut le prendre.\"],\"b3Thhd\":[\"Envoi échoué\"],\"b8XMJ8\":[[\"visibleCount\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}]],\"bQhwn-\":[\"Chargement de la collection…\"],\"b_1P1D\":[\"Renommage…\"],\"bzSI52\":[\"Abandonner\"],\"cILfnJ\":[\"Supprimer le fichier\"],\"cNu4my\":[[\"0\"],\" de plus en ligne : \",[\"restNames\"]],\"cYP9Sb\":[\"+ Collection\"],\"cbeBbZ\":[\"Au moins \",[\"0\"],\" caractères\"],\"cnGeoo\":[\"Supprimer\"],\"dAs22m\":[\"Remplacer le fichier\"],\"dEgA5A\":[\"Annuler\"],\"dMizp8\":[\"Nouvelle collection\"],\"dau1Pl\":[[\"unseenCount\"],\" nouveaux messages\"],\"digest.monthly\":[\"Récap mensuel\"],\"digest.monthly.off\":[\"Ne plus recevoir le récap mensuel\"],\"digest.weekly\":[\"Résumé hebdomadaire\"],\"digest.weekly.off\":[\"Ne plus recevoir le résumé hebdomadaire\"],\"dqwCfR\":[\"Impossible de charger un aperçu pour ce lien — vérifiez-le, ou publiez-le tel quel et actualisez l'aperçu plus tard.\"],\"dznB-Y\":[\"Seules les images peuvent être déposées ici.\"],\"eFSqvc\":[\"Impossible de publier la réponse\"],\"eIEHHV\":[\"Changer la couleur de peau\"],\"eOfXq3\":[\"Un titre est requis.\"],\"ePK91l\":[\"Modifier\"],\"eaUTwS\":[\"Envoyer le lien de réinitialisation\"],\"ecUA8p\":[\"Aujourd'hui\"],\"ef9nPf\":[\"Chargement de la reco…\"],\"eloqXQ\":[\"Vous pouvez la publier quand même.\"],\"email.digest.addedTo\":[\"ajoutée à \",[\"playlist\"]],\"email.digest.andMore\":[\"et \",[\"n\"],\" de plus\"],\"email.digest.by\":[\"par @\",[\"username\"]],\"email.digest.chatWeek\":[[\"n\",\"plural\",{\"one\":[\"#\",\" message cette semaine\"],\"other\":[\"#\",\" messages cette semaine\"]}]],\"email.digest.community\":[\"La communauté\"],\"email.digest.count.comments\":[[\"n\",\"plural\",{\"one\":[\"commentaire\"],\"other\":[\"commentaires\"]}]],\"email.digest.count.followers\":[[\"n\",\"plural\",{\"one\":[\"nouvel abonné\"],\"other\":[\"nouveaux abonnés\"]}]],\"email.digest.count.likes\":[[\"n\",\"plural\",{\"one\":[\"j'aime sur vos commentaires\"],\"other\":[\"j'aime sur vos commentaires\"]}]],\"email.digest.count.mentions\":[[\"n\",\"plural\",{\"one\":[\"mention\"],\"other\":[\"mentions\"]}]],\"email.digest.count.playlistFollows\":[[\"n\",\"plural\",{\"one\":[\"abonnement à vos collections\"],\"other\":[\"abonnements à vos collections\"]}]],\"email.digest.count.upvotes\":[[\"n\",\"plural\",{\"one\":[\"vote\"],\"other\":[\"votes\"]}]],\"email.digest.forYou\":[\"Pour vous\"],\"email.digest.hl.comment\":[[\"who\"],\" a commenté votre reco \",[\"target\"]],\"email.digest.hl.commentLike\":[[\"who\"],\" a aimé votre commentaire sur \",[\"target\"]],\"email.digest.hl.mentionComment\":[[\"who\"],\" vous a mentionné dans un commentaire sur \",[\"target\"]],\"email.digest.hl.mentionIn\":[[\"who\"],\" vous a mentionné dans \",[\"target\"]],\"email.digest.hl.mentionPlaylist\":[[\"who\"],\" vous a mentionné dans la collection \",[\"target\"]],\"email.digest.hl.playlistFollow\":[[\"who\"],\" a suivi votre collection \",[\"target\"]],\"email.digest.hl.userFollow\":[[\"who\"],\" vous suit désormais\"],\"email.digest.inChat\":[\"Dans la tribune\"],\"email.digest.invitedBy\":[\"sur invitation de @\",[\"username\"]],\"email.digest.invitedByYou\":[\"sur votre invitation\"],\"email.digest.monthly.reason\":[\"Vous recevez ce récap mensuel car il est activé dans vos paramètres.\"],\"email.digest.monthly.subject\":[\"Le mois sur \",[\"site\"],\", \",[\"range\"]],\"email.digest.monthly.title\":[\"Le mois sur \",[\"site\"]],\"email.digest.more\":[[\"n\"],\" de plus dans votre fil Suivi\"],\"email.digest.network\":[\"De ceux que vous suivez\"],\"email.digest.newMembers\":[\"Nouveaux membres\"],\"email.digest.newPlaylists\":[\"Nouvelles collections publiques\"],\"email.digest.openChat\":[\"Ouvrir la tribune\"],\"email.digest.playlistMeta\":[\"par @\",[\"username\"],\" · \",[\"n\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}]],\"email.digest.pre.members\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouveau membre\"],\"other\":[\"#\",\" nouveaux membres\"]}]],\"email.digest.pre.network\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouvelle reco de ceux que vous suivez\"],\"other\":[\"#\",\" nouvelles recos de ceux que vous suivez\"]}]],\"email.digest.pre.playlists\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouvelle collection\"],\"other\":[\"#\",\" nouvelles collections\"]}]],\"email.digest.pre.topMonth\":[[\"n\",\"plural\",{\"one\":[\"la reco la plus votée du mois\"],\"other\":[\"les \",\"#\",\" recos les plus votées du mois\"]}]],\"email.digest.pre.topWeek\":[[\"n\",\"plural\",{\"one\":[\"la reco la plus votée de la semaine\"],\"other\":[\"les \",\"#\",\" recos les plus votées de la semaine\"]}]],\"email.digest.seeAll\":[[\"n\",\"plural\",{\"one\":[\"Voir la notification\"],\"other\":[\"Voir les \",\"#\",\" notifications\"]}]],\"email.digest.stat.chat\":[[\"n\",\"plural\",{\"one\":[\"message dans la tribune\"],\"other\":[\"messages dans la tribune\"]}]],\"email.digest.stat.dumps\":[[\"n\",\"plural\",{\"one\":[\"reco\"],\"other\":[\"recos\"]}]],\"email.digest.theChat\":[\"la tribune\"],\"email.digest.topMonth\":[\"Les plus votées du mois\"],\"email.digest.topWeek\":[\"Les plus votées de la semaine\"],\"email.digest.totals\":[[\"dumps\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}],\", \",[\"upvotes\",\"plural\",{\"one\":[\"#\",\" vote\"],\"other\":[\"#\",\" votes\"]}],\" et \",[\"comments\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}],\".\"],\"email.digest.upload\":[\"fichier\"],\"email.digest.weekly.reason\":[\"Vous recevez ce résumé hebdomadaire car il est activé dans vos paramètres.\"],\"email.digest.weekly.subject\":[\"Votre semaine sur \",[\"site\"],\", \",[\"range\"]],\"email.digest.weekly.title\":[\"Votre semaine, \",[\"username\"]],\"email.digest.yours\":[\"à vous\"],\"email.reset.body\":[\"Quelqu'un a demandé à réinitialiser le mot de passe de votre compte \",[\"site\"],\". Le bouton ci-dessous vous permet d'en choisir un nouveau. Il ne fonctionne qu'une fois, pendant \",[\"hours\",\"plural\",{\"one\":[\"#\",\" heure\"],\"other\":[\"#\",\" heures\"]}],\".\"],\"email.reset.button\":[\"Choisir un nouveau mot de passe\"],\"email.reset.fallback\":[\"Si le bouton ne fonctionne pas, collez ce lien dans votre navigateur :\"],\"email.reset.hi\":[\"Bonjour \",[\"username\"],\",\"],\"email.reset.ignore\":[\"Si vous n'êtes pas à l'origine de cette demande, ignorez cet e-mail. Votre mot de passe ne change pas.\"],\"email.reset.preheader\":[\"Le lien ne fonctionne qu'une fois, pendant \",[\"hours\",\"plural\",{\"one\":[\"#\",\" heure\"],\"other\":[\"#\",\" heures\"]}],\".\"],\"email.reset.reason\":[\"Vous recevez cet e-mail car une réinitialisation de mot de passe a été demandée pour cette adresse.\"],\"email.reset.subject\":[\"Réinitialisez votre mot de passe \",[\"site\"]],\"email.reset.title\":[\"Réinitialiser votre mot de passe\"],\"email.settings\":[\"Paramètres\"],\"email.welcome.body\":[\"# Bienvenue sur \",[\"site\"],\" !\\n\\nBonjour **\",[\"username\"],\"**,\\n\\nVotre compte a bien été créé. Bienvenue à bord !\"],\"email.welcome.button\":[\"Ouvrir \",[\"site\"]],\"email.welcome.digests\":[\"Chaque semaine, vous recevrez un résumé de ce qui s'est passé ici, et chaque mois un récap des recos les plus votées. Vous pouvez désactiver l'un ou l'autre dans vos paramètres.\"],\"email.welcome.preheader\":[\"Votre compte est prêt.\"],\"email.welcome.reason\":[\"Vous recevez cet e-mail car un compte \",[\"site\"],\" a été créé avec cette adresse.\"],\"email.welcome.subject\":[\"Bienvenue sur \",[\"site\"]],\"en9o7K\":[\"Impossible de publier le commentaire\"],\"etFQQS\":[\"Pourquoi on en voudrait ?\"],\"f3cEDO\":[\"Changer l'image\"],\"fC6mXb\":[\"Voter\"],\"fI-mNw\":[\"Collections\"],\"fgLNSM\":[\"S'inscrire\"],\"gANddk\":[\"Envoi…\"],\"gGx5tM\":[\"Modification\"],\"gIQQwD\":[\"Chargement échoué\"],\"gIpLuy\":[\"Il faut un lien\"],\"gL9KKi\":[\"Qui peut la voir\"],\"gLfZlz\":[\"Ajouter à la collection\"],\"gjJ-sb\":[\"Impossible de se connecter au serveur de mises à jour en direct. Les votes et les notifications pourraient ne pas se synchroniser avant la reconnexion.\"],\"goWEhR\":[\"Piste précédente\"],\"hBuUKa\":[\"Changer le mot de passe…\"],\"hD7w09\":[\"Vous avez tout lu, tout vu, tout bu.\"],\"hJSliC\":[\"<0>\",[\"0\"],\" a publié <1>\",[\"1\"],\"\"],\"hT8hgO\":[\"Rétablir le titre suggéré\"],\"hYgDIe\":[\"Créer\"],\"he3ygx\":[\"Copier\"],\"hpCc_Z\":[\"Fermer quand même\"],\"i7K_Te\":[\"Qui suis-je ?\"],\"iDNBZe\":[\"Notifications\"],\"iWpEwy\":[\"Accueil\"],\"ipYn7W\":[\"Si cette adresse est enregistrée, vous recevrez un lien de réinitialisation sous peu.\"],\"isRobC\":[\"Nouveau\"],\"jTGs8A\":[\"Ce site n'envoie pas d'e-mails, aucun résumé ne part donc.\"],\"jbernk\":[\"Chargement du profil…\"],\"jnrIbz\":[\"Voté (\",[\"0\"],\")\"],\"joEmfT\":[\"Serveur inaccessible\"],\"jrZTZl\":[\"Pas encore de recos. Soyez le premier !\"],\"k112DD\":[\"Charger les messages plus anciens\"],\"k36iJ-\":[\"Échec de l’enregistrement de la catégorie\"],\"k4b5_X\":[\"modifié\"],\"kLttbL\":[\"Inscription échouée\"],\"lUanmi\":[\"Vous serez notifié lorsque quelqu'un suit vos collections, vote pour vos recos ou publie du nouveau contenu.\"],\"lY5h1V\":[[\"0\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}]],\"lcfvr_\":[\"Supprimer ce commentaire ?\"],\"lpIMne\":[\"Les mots de passe ne correspondent pas\"],\"mDJo8W\":[\"Recos (\",[\"0\"],\")\"],\"mb_Wju\":[\"Déposez un fichier ici, ou collez-en un\"],\"mt6O6E\":[\"C'est un mirage.\"],\"nbm5sI\":[\"Aucune reco ne correspond à « \",[\"q\"],\" ».\"],\"nrjqON\":[\"Vérification de l'invitation…\"],\"nwtY4N\":[\"Une erreur est survenue\"],\"nx4kaN\":[\"Sauvegarde impossible\"],\"oTYvvN\":[[\"0\",\"plural\",{\"one\":[\"et \",\"#\",\" reco plus ancienne\"],\"other\":[\"et \",\"#\",\" recos plus anciennes\"]}]],\"ogtYkT\":[\"Mot de passe mis à jour\"],\"pCpd9p\":[\"<0>\",[\"0\"],\" vous a mentionné dans <1>\",[\"where\"],\"\"],\"pSheLH\":[\"Aucun invité pour le moment.\"],\"pd3FCq\":[\"Ce fichier dépasse \",[\"limit\"],\".\"],\"pvnfJD\":[\"Sombre\"],\"qIMfNQ\":[\"Supprimer la collection\"],\"qItrqH\":[\"Vous ne recevrez plus le récap mensuel. Vous pouvez le réactiver dans vos paramètres.\"],\"qbDAcy\":[\"Recommander\"],\"qgx_78\":[\"Suivez des collections publiques pour voir leurs recos ici.\"],\"qnUJBI\":[\"Le nouvel ordre n'a pas pu être enregistré. Réessayez.\"],\"qsqt0w\":[\"Lien d'invitation\"],\"qvFa8r\":[\"public\"],\"qvz_Pp\":[\"Reco\"],\"rCbqPX\":[\"Ce lien d'invitation est manquant, expiré ou déjà utilisé.\"],\"rDMzDU\":[\"Écrivez un message…\"],\"rg9pXu\":[\"Recherche échouée\"],\"rn6SBY\":[\"Rétablir le son\"],\"sEXJPi\":[\"Lien ou fichier\"],\"sGeXL3\":[\"Miniature\"],\"sQia9P\":[\"Se connecter\"],\"sTiqbm\":[\"invité par\"],\"sdP5Aa\":[\"[supprimé]\"],\"sgX7B-\":[\"Usage unique, valable 7 jours.\"],\"shHs8T\":[\"Saisissez une recherche.\"],\"smeBfS\":[\"Invitation invalide\"],\"teaneZ\":[\"image\"],\"tfDRzk\":[\"Enregistrer\"],\"tvmuQ0\":[\"Thème de couleur\"],\"u1lDX2\":[\"Récupération de l'aperçu…\"],\"uD0qXQ\":[\"Déposez un fichier ici\"],\"uISnsY\":[\"Suppression impossible\"],\"uMGUnV\":[\"Pas encore de collections.\"],\"ub1EEL\":[\"modifié \",[\"0\"]],\"uomLas\":[\"Ajouter une catégorie\"],\"v-Wp--\":[\"Ouvrir la source\"],\"vJBF1r\":[\"Publication…\"],\"vLhLLO\":[\"Notifications (\",[\"unreadNotificationCount\"],\" non lues)\"],\"vQMkHu\":[\"Retirer le vote\"],\"vuosjb\":[\"Menu utilisateur\"],\"wckWOP\":[\"Administration\"],\"wixIgH\":[\"Vous avez déjà un compte ? <0>Se connecter\"],\"wtxFp4\":[\"Fichier trop volumineux (max \",[\"max\"],\").\"],\"x6tjuK\":[\"Onglet par défaut\"],\"xEWkgZ\":[\"← Retour à toutes les recos\"],\"xPHtx0\":[\"Lancer la recherche\"],\"xVuNgt\":[\"+ Nouvelle collection\"],\"xc9O_u\":[\"Supprimer la reco\"],\"y1G_DI\":[\"Renommage impossible\"],\"y6sq5j\":[\"Abonné\"],\"y7oaHj\":[\"Titre de la collection\"],\"yA_6BX\":[\"Tout voir →\"],\"yBBtRm\":[\"Suivez des utilisateurs pour voir leurs recos ici.\"],\"yQ2kGp\":[\"Charger plus\"],\"y_0uwd\":[\"Hier\"],\"yxlcv5\":[\"Ne plus recevoir le récap mensuel ?\"],\"yz7wBu\":[\"Fermer\"],\"yzF66j\":[\"Lien\"],\"z0ROB3\":[\"Retirer le j'aime\"],\"z1uNN0\":[\"Aucun emoji trouvé.\"],\"zVuxvN\":[\"Actualisation…\"],\"zwBp5t\":[\"Privé\"]}"); \ No newline at end of file +/*eslint-disable*/export const messages=JSON.parse("{\"-K9EZb\":[\"Ajouter un e-mail…\"],\"-Ya-b9\":[\"Enregistrement échoué\"],\"-iDCtG\":[\"Vos paramètres\"],\"-siMqD\":[\"Journal\"],\"1CalO6\":[\"Style\"],\"1HfJWf\":[\"Rechercher des recos, utilisateurs, collections…\"],\"1QfxQT\":[\"Fermer\"],\"1YKhJ3\":[\"1 nouveau message\"],\"1njn7W\":[\"Clair\"],\"1utXA6\":[\"Recos\"],\"26iNma\":[\"Publier le commentaire\"],\"2DoBvq\":[\"Flux\"],\"2Hlmdt\":[\"Écrire une réponse…\"],\"2_DHgA\":[\"Identifiant d’URL\"],\"2nrsEn\":[\"message supprimé\"],\"2wxgft\":[\"Renommer\"],\"2ygf_L\":[\"← Retour\"],\"3KKSM4\":[\"privé\"],\"3QuNUr\":[\"Plusieurs personnes écrivent…\"],\"3SEwY1\":[\"Supprimer la catégorie \\\"\",[\"0\"],\"\\\" ? Cette action est irréversible.\"],\"3qkggm\":[\"Plein écran\"],\"3yfh3D\":[\"<0>\",[\"0\"],\" a suivi votre collection <1>\",[\"1\"],\"\"],\"48e2R8\":[\"Modifier l'e-mail\"],\"49voTZ\":[[\"label\"],\" (\",[\"count\"],\")\"],\"4B6w_o\":[\"Recommandé !\"],\"4GKuCs\":[\"Connexion échouée\"],\"4HH9iB\":[\"Aucun abonnement pour le moment.\"],\"4OjqAQ\":[\"Continuer l'édition\"],\"4RtQ1k\":[\"Ne plus suivre \",[\"targetUsername\"]],\"4c-qBx\":[\"Votre mot de passe a été modifié. Vous pouvez maintenant vous connecter.\"],\"4yj9xV\":[\"Les mises à jour en direct sont temporairement interrompues. Tentative de reconnexion…\"],\"5KHDCi\":[[\"0\"],\" et \",[\"1\"],\" sont en train d'écrire…\"],\"5NZ3tg\":[\"Désactivation…\"],\"5TviPn\":[\"Annuler la recherche\"],\"5cC8f2\":[\"Modifié le \",[\"0\"]],\"5oD9f_\":[\"Plus tôt\"],\"6Qly-0\":[\"un commentaire\"],\"6YE0P6\":[\"Édition de \",[\"0\"]],\"6YtxFj\":[\"Nom\"],\"6_dCYd\":[\"Aperçu\"],\"6gRgw8\":[\"Réessayer\"],\"7PHCIN\":[\"Annuler la suppression\"],\"7PzzBU\":[\"Utilisateur\"],\"7d1a0d\":[\"Public\"],\"7sNhEz\":[\"Nom d'utilisateur\"],\"8-8abB\":[\"Déconnecté — reconnexion…\"],\"8F1i42\":[\"Page introuvable\"],\"8JplJJ\":[\"Désabonné\"],\"8Ug9jB\":[\"Connexe\"],\"8ZsakT\":[\"Mot de passe\"],\"92o1-U\":[\"Suppression échouée\"],\"9BruTc\":[\"Pourquoi ?\"],\"9l4qcT\":[\"Déposez un fichier de remplacement ici\"],\"9uI_rE\":[\"Annuler\"],\"A0y396\":[\"+ Inviter quelqu'un\"],\"A1taO8\":[\"Rechercher\"],\"AEfu-D\":[\"Dans les collections\"],\"AHZflp\":[\"Tribune\"],\"AQbgNR\":[\"Suivre \",[\"targetUsername\"]],\"AS7WoE\":[\"Repartir de zéro\"],\"ATGYL1\":[\"Adresse e-mail\"],\"AZctoV\":[[\"0\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}]],\"Ade-6d\":[\"Mises à jour en direct indisponibles.\"],\"AeXO77\":[\"Compte\"],\"AhNP40\":[\"Position\"],\"AnRu_j\":[\"Annuler la réponse\"],\"B4LeYP\":[\"Échec de la suppression (\",[\"0\"],\")\"],\"BjitBo\":[\"Télécharger \",[\"0\"]],\"BlpJ2G\":[\"Reprise de votre brouillon.\"],\"CI50ct\":[\"Voté\"],\"C_qQE4\":[\"Déjà recommandé par \",[\"0\"],\" \",[\"1\"]],\"Cf_gxi\":[\"Ne plus recevoir le résumé hebdomadaire ?\"],\"Cj24wt\":[\"Inscription…\"],\"CnqayG\":[\"La mise à jour a échoué. Réessayez.\"],\"D8DRYV\":[\"Supprimer ce message ?\"],\"DI3NBO\":[\"Ouvrir sur \",[\"0\"]],\"DPfwMq\":[\"Terminé\"],\"DdeHXH\":[\"Supprimer cette reco ? Cette action est irréversible.\"],\"Dp1JhP\":[\"<0>\",[\"0\"],\" a voté pour <1>\",[\"1\"],\"\"],\"E417RG\":[\"Vous ne recevrez plus le résumé hebdomadaire. Vous pouvez le réactiver dans vos paramètres.\"],\"EBU6O6\":[\"Connexion perdue — ce message n'a peut-être pas été envoyé\"],\"ECiS12\":[\"Voir la reco →\"],\"ED8n0z\":[\"la tribune\"],\"EarrCe\":[\"Rechercher un emoji\"],\"Evcm2Z\":[\"Fermer sans publier ? Le fichier joint sera perdu — ce que vous avez écrit sera conservé.\"],\"EwVrWF\":[\"Le dump n'a pas pu être retiré. Réessayez.\"],\"ExR0Fr\":[\"L'URL est obligatoire.\"],\"F5Js1v\":[\"Ne plus suivre la collection\"],\"FdTlEG\":[\"Piste \",[\"0\"]],\"FgAxTj\":[\"Se déconnecter\"],\"Fxf4jq\":[\"Description (facultatif)\"],\"GCWCCo\":[\"Fermer le lecteur\"],\"GDvlUT\":[\"Rôle\"],\"GLukaH\":[\"Modérateur\"],\"GNSsCc\":[\"Impossible de créer la collection\"],\"GQ38dl\":[[\"unreadChatCount\"],\" messages non lus\"],\"GY6uSc\":[\"Suivant →\"],\"GbqhrN\":[[\"0\"],\"–\",[\"1\"],\" caractères : lettres, chiffres ou tirets bas\"],\"GptGxg\":[\"Changer le mot de passe\"],\"Gv5nA5\":[\"Format de réponse inattendu\"],\"H4MGkI\":[\"Agrandir le lecteur\"],\"H4o4sk\":[\"Personne pour le moment.\"],\"H8pzW-\":[\"Impossible de mettre à jour l'avatar\"],\"HTLDA4\":[\"Chargement…\"],\"H_PnT8\":[\"Insérer un emoji\"],\"I-x669\":[\"Invités\"],\"IO9KL_\":[\"Piste suivante\"],\"IZX7TO\":[\"Impossible de générer une invitation\"],\"IagCbF\":[\"URL\"],\"IgrLD_\":[\"Pause\"],\"IhK90M\":[\"Lettres, chiffres et tirets bas seulement\"],\"ImOQa9\":[\"Répondre\"],\"JJ-Bhk\":[\"Votre adresse e-mail\"],\"JLasL-\":[\"Désabonnement impossible\"],\"JRQitQ\":[\"Confirmer le nouveau mot de passe\"],\"JXr41k\":[\"<0>\",[\"0\"],\" a commenté sur <1>\",[\"1\"],\"\"],\"Jd58Fo\":[\"Tendances\"],\"Jf0PuK\":[\"Aller à la connexion\"],\"JlFcis\":[\"Envoyer\"],\"JzARG2\":[\"Les liens vers l’ancienne adresse marchent toujours.\"],\"K7tIrx\":[\"Catégorie\"],\"KDGWg5\":[\"Retirer de la collection\"],\"KDw4GX\":[\"Réessayer\"],\"KKZxBU\":[\"Envoi \",[\"0\"],\" %\"],\"K_F6pa\":[\"Enregistrement…\"],\"KtYF-t\":[\"Sons\"],\"L-rMC9\":[\"Réinitialiser par défaut\"],\"L7_rsx\":[\"Envoi de l'image…\"],\"MHrjPM\":[\"Titre\"],\"MKEPCY\":[\"Suivre\"],\"MTb2Ot\":[\"Aucun message pour l'instant. Dites bonjour !\"],\"Mj6oCJ\":[[\"0\"],\" est en train d'écrire…\"],\"N40H-G\":[\"Tout\"],\"N6j2JH\":[\"Modifier \",[\"0\"]],\"NUrY9o\":[\"Catégories\"],\"O32mmL\":[\"Modifier la description\"],\"O3oNi5\":[\"E-mail\"],\"Oprv1v\":[\"Mot de passe (min. \",[\"0\"],\" caractères)\"],\"Oz0N9s\":[\"nouveau\"],\"P5MuiB\":[\"Ajouter une image\"],\"PN3pBl\":[\"Pas de réponse du serveur — ce message n'a peut-être pas été envoyé\"],\"PUe8Yk\":[\"Réduire le lecteur\"],\"PiH3UR\":[\"Copié !\"],\"Pn2B7_\":[\"Mot de passe actuel\"],\"Pwqkdw\":[\"Chargement…\"],\"Q4WUjR\":[\"Rechercher un emoji…\"],\"Q6n4F4\":[\"Actualiser les métadonnées\"],\"QGaQRo\":[\"Erreur lors de la mise à jour du rôle\"],\"QKsaQr\":[\"ou <0>parcourir les fichiers\"],\"QLtPBd\":[\"Aucune reco dans cette collection pour l'instant.\"],\"QctOgc\":[\"Impossible de joindre le serveur. Vérifiez votre connexion et réessayez.\"],\"R7SsBE\":[\"Couper le son\"],\"R9Khdg\":[\"Auto\"],\"RCcPrX\":[\"Supprimer cette collection ? Cette action est irréversible.\"],\"RTksSy\":[\"<0>\",[\"0\"],\" a commencé à vous suivre\"],\"RaKjrM\":[\"Impossible d'enregistrer la modification\"],\"RcUHRT\":[\"Suivi\"],\"RfwZxd\":[\"Réinitialiser le mot de passe\"],\"Rrp6-J\":[\"Se connecter pour aimer\"],\"RvVJBH\":[\"Actualisation du flux…\"],\"S1xV5B\":[[\"maxLength\"],\" caractères au maximum.\"],\"SBTElJ\":[\"Recherche…\"],\"SMaFdc\":[\"Se désabonner\"],\"Sad2tK\":[\"Envoi…\"],\"So7gCD\":[\"Collez un lien…\"],\"StovX6\":[\"Rien à voir, circulez.\"],\"Sxm8rQ\":[\"Utilisateurs\"],\"T8ejtZ\":[\"Ce commentaire n'a pas pu être supprimé. Réessayez.\"],\"T91vKp\":[\"Lecture\"],\"T9bjWt\":[\"<0>\",[\"0\"],\" a été ajouté à <1>\",[\"1\"],\"\"],\"TAtbho\":[\"Chargement des messages…\"],\"TN382O\":[\"Lien invalide\"],\"Tv9vbB\":[\"Suivre la collection\"],\"Tz0i8g\":[\"Paramètres\"],\"U3pytU\":[\"Administrateur\"],\"UNMVei\":[\"Mot de passe oublié ?\"],\"UOZith\":[\"Publication échouée\"],\"URAieT\":[\"Se connecter pour voter\"],\"V38P77\":[\"Seuls son auteur et les modérateurs peuvent modifier ce dump.\"],\"VCoEm-\":[\"Retour à la connexion\"],\"VKsaTi\":[\"En réponse à\"],\"VLAd8f\":[\"Max \",[\"limit\"]],\"V_e7nf\":[\"Définir un nouveau mot de passe\"],\"VnNJbN\":[\"De collections\"],\"VskHIx\":[\"Supprimer la catégorie\"],\"VyTYmS\":[\"Changer l'avatar\"],\"W5A0Ly\":[\"Une erreur inattendue s'est produite.\"],\"W9FRBT\":[\"Aimer\"],\"WAcnnR\":[\"L'image n'a pas pu être envoyée.\"],\"WE_r9K\":[\"Échec de la création de la catégorie\"],\"WfffrI\":[\"identifiant\"],\"WhimMi\":[\"Échec de la réinitialisation\"],\"WpXcBJ\":[\"Rien ici pour l'instant.\"],\"XHqhoS\":[\"Impossible de charger le chat.\"],\"XJVYo8\":[\"Pourquoi & où\"],\"XJy2oN\":[\"Connexion…\"],\"Xan6QP\":[\"Nouvelle reco\"],\"XgRtUf\":[\"Impossible de changer le mot de passe\"],\"XnL-Eu\":[\"Aucun utilisateur ne correspond à « \",[\"q\"],\" ».\"],\"Xs2Lez\":[\"Ce lien de réinitialisation est absent ou malformé.\"],\"YK1Dhc\":[\"une publication\"],\"YWnSuq\":[\"Ce lien de désabonnement est absent ou mal formé.\"],\"YZxXK8\":[\"Fermer le sélecteur d'emoji\"],\"YcjhhX\":[\"Gestion utilisateur\"],\"Ye9RMF\":[\"<0>\",[\"0\"],\" a aimé votre commentaire sur <1>\",[\"1\"],\"\"],\"YpkCca\":[\"Pas encore de collections suivies.\"],\"Z5HWHd\":[\"Activés\"],\"ZBdbv9\":[\"Modifier le titre\"],\"ZCpU0u\":[\"Aucune collection ne correspond à « \",[\"q\"],\" ».\"],\"ZWxjuD\":[\"Certaines images n'ont pas pu être envoyées.\"],\"ZmD2o6\":[\"Créer et ajouter\"],\"_3O5R_\":[\"Échec de la demande\"],\"_DwR-n\":[\"Création…\"],\"_I1_HN\":[\"Minuscules, chiffres et tirets simples seulement\"],\"_R_sGB\":[\"Mot de passe modifié avec succès.\"],\"_aept4\":[\"Publier la réponse\"],\"_cF7Rs\":[\"Volume\"],\"_karOe\":[\"Hors ligne — message non envoyé\"],\"_nT6AE\":[\"Nouveau mot de passe\"],\"_t4W-i\":[\"De personnes\"],\"aAIQg2\":[\"Apparence\"],\"aDvLhk\":[\"Ajouter un commentaire…\"],\"az8lvo\":[\"Désactivés\"],\"azhsgG\":[\"Son ancien nom mène toujours à son profil, et personne d’autre ne peut le prendre.\"],\"b3Thhd\":[\"Envoi échoué\"],\"b8XMJ8\":[[\"visibleCount\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}]],\"bQhwn-\":[\"Chargement de la collection…\"],\"b_1P1D\":[\"Renommage…\"],\"bzSI52\":[\"Abandonner\"],\"cILfnJ\":[\"Supprimer le fichier\"],\"cNu4my\":[[\"0\"],\" de plus en ligne : \",[\"restNames\"]],\"cYP9Sb\":[\"+ Collection\"],\"cbeBbZ\":[\"Au moins \",[\"0\"],\" caractères\"],\"cnGeoo\":[\"Supprimer\"],\"dAs22m\":[\"Remplacer le fichier\"],\"dEgA5A\":[\"Annuler\"],\"dMizp8\":[\"Nouvelle collection\"],\"dau1Pl\":[[\"unseenCount\"],\" nouveaux messages\"],\"digest.monthly\":[\"Récap mensuel\"],\"digest.monthly.off\":[\"Ne plus recevoir le récap mensuel\"],\"digest.weekly\":[\"Résumé hebdomadaire\"],\"digest.weekly.off\":[\"Ne plus recevoir le résumé hebdomadaire\"],\"dqwCfR\":[\"Impossible de charger un aperçu pour ce lien — vérifiez-le, ou publiez-le tel quel et actualisez l'aperçu plus tard.\"],\"dznB-Y\":[\"Seules les images peuvent être déposées ici.\"],\"eFSqvc\":[\"Impossible de publier la réponse\"],\"eIEHHV\":[\"Changer la couleur de peau\"],\"eOfXq3\":[\"Un titre est requis.\"],\"ePK91l\":[\"Modifier\"],\"eaUTwS\":[\"Envoyer le lien de réinitialisation\"],\"ecUA8p\":[\"Aujourd'hui\"],\"ef9nPf\":[\"Chargement de la reco…\"],\"eloqXQ\":[\"Vous pouvez la publier quand même.\"],\"email.digest.addedTo\":[\"ajoutée à \",[\"playlist\"]],\"email.digest.andMore\":[\"et \",[\"n\"],\" de plus\"],\"email.digest.by\":[\"par @\",[\"username\"]],\"email.digest.chatWeek\":[[\"n\",\"plural\",{\"one\":[\"#\",\" message cette semaine\"],\"other\":[\"#\",\" messages cette semaine\"]}]],\"email.digest.community\":[\"La communauté\"],\"email.digest.count.comments\":[[\"n\",\"plural\",{\"one\":[\"commentaire\"],\"other\":[\"commentaires\"]}]],\"email.digest.count.followers\":[[\"n\",\"plural\",{\"one\":[\"nouvel abonné\"],\"other\":[\"nouveaux abonnés\"]}]],\"email.digest.count.likes\":[[\"n\",\"plural\",{\"one\":[\"j'aime sur vos commentaires\"],\"other\":[\"j'aime sur vos commentaires\"]}]],\"email.digest.count.mentions\":[[\"n\",\"plural\",{\"one\":[\"mention\"],\"other\":[\"mentions\"]}]],\"email.digest.count.playlistFollows\":[[\"n\",\"plural\",{\"one\":[\"abonnement à vos collections\"],\"other\":[\"abonnements à vos collections\"]}]],\"email.digest.count.upvotes\":[[\"n\",\"plural\",{\"one\":[\"vote\"],\"other\":[\"votes\"]}]],\"email.digest.forYou\":[\"Pour vous\"],\"email.digest.hl.comment\":[[\"who\"],\" a commenté votre reco \",[\"target\"]],\"email.digest.hl.commentLike\":[[\"who\"],\" a aimé votre commentaire sur \",[\"target\"]],\"email.digest.hl.mentionComment\":[[\"who\"],\" vous a mentionné dans un commentaire sur \",[\"target\"]],\"email.digest.hl.mentionIn\":[[\"who\"],\" vous a mentionné dans \",[\"target\"]],\"email.digest.hl.mentionPlaylist\":[[\"who\"],\" vous a mentionné dans la collection \",[\"target\"]],\"email.digest.hl.playlistFollow\":[[\"who\"],\" a suivi votre collection \",[\"target\"]],\"email.digest.hl.userFollow\":[[\"who\"],\" vous suit désormais\"],\"email.digest.inChat\":[\"Dans la tribune\"],\"email.digest.invitedBy\":[\"sur invitation de @\",[\"username\"]],\"email.digest.invitedByYou\":[\"sur votre invitation\"],\"email.digest.monthly.reason\":[\"Vous recevez ce récap mensuel car il est activé dans vos paramètres.\"],\"email.digest.monthly.subject\":[\"Le mois sur \",[\"site\"],\", \",[\"range\"]],\"email.digest.monthly.title\":[\"Le mois sur \",[\"site\"]],\"email.digest.more\":[[\"n\"],\" de plus dans votre fil Suivi\"],\"email.digest.network\":[\"De ceux que vous suivez\"],\"email.digest.newMembers\":[\"Nouveaux membres\"],\"email.digest.newPlaylists\":[\"Nouvelles collections publiques\"],\"email.digest.openChat\":[\"Ouvrir la tribune\"],\"email.digest.playlistMeta\":[\"par @\",[\"username\"],\" · \",[\"n\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}]],\"email.digest.pre.members\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouveau membre\"],\"other\":[\"#\",\" nouveaux membres\"]}]],\"email.digest.pre.network\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouvelle reco de ceux que vous suivez\"],\"other\":[\"#\",\" nouvelles recos de ceux que vous suivez\"]}]],\"email.digest.pre.playlists\":[[\"n\",\"plural\",{\"one\":[\"#\",\" nouvelle collection\"],\"other\":[\"#\",\" nouvelles collections\"]}]],\"email.digest.pre.topMonth\":[[\"n\",\"plural\",{\"one\":[\"la reco la plus votée du mois\"],\"other\":[\"les \",\"#\",\" recos les plus votées du mois\"]}]],\"email.digest.pre.topWeek\":[[\"n\",\"plural\",{\"one\":[\"la reco la plus votée de la semaine\"],\"other\":[\"les \",\"#\",\" recos les plus votées de la semaine\"]}]],\"email.digest.seeAll\":[[\"n\",\"plural\",{\"one\":[\"Voir la notification\"],\"other\":[\"Voir les \",\"#\",\" notifications\"]}]],\"email.digest.stat.chat\":[[\"n\",\"plural\",{\"one\":[\"message dans la tribune\"],\"other\":[\"messages dans la tribune\"]}]],\"email.digest.stat.dumps\":[[\"n\",\"plural\",{\"one\":[\"reco\"],\"other\":[\"recos\"]}]],\"email.digest.theChat\":[\"la tribune\"],\"email.digest.topMonth\":[\"Les plus votées du mois\"],\"email.digest.topWeek\":[\"Les plus votées de la semaine\"],\"email.digest.totals\":[[\"dumps\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}],\", \",[\"upvotes\",\"plural\",{\"one\":[\"#\",\" vote\"],\"other\":[\"#\",\" votes\"]}],\" et \",[\"comments\",\"plural\",{\"one\":[\"#\",\" commentaire\"],\"other\":[\"#\",\" commentaires\"]}],\".\"],\"email.digest.upload\":[\"fichier\"],\"email.digest.weekly.reason\":[\"Vous recevez ce résumé hebdomadaire car il est activé dans vos paramètres.\"],\"email.digest.weekly.subject\":[\"Votre semaine sur \",[\"site\"],\", \",[\"range\"]],\"email.digest.weekly.title\":[\"Votre semaine, \",[\"username\"]],\"email.digest.yours\":[\"à vous\"],\"email.reset.body\":[\"Quelqu'un a demandé à réinitialiser le mot de passe de votre compte \",[\"site\"],\". Le bouton ci-dessous vous permet d'en choisir un nouveau. Il ne fonctionne qu'une fois, pendant \",[\"hours\",\"plural\",{\"one\":[\"#\",\" heure\"],\"other\":[\"#\",\" heures\"]}],\".\"],\"email.reset.button\":[\"Choisir un nouveau mot de passe\"],\"email.reset.fallback\":[\"Si le bouton ne fonctionne pas, collez ce lien dans votre navigateur :\"],\"email.reset.hi\":[\"Bonjour \",[\"username\"],\",\"],\"email.reset.ignore\":[\"Si vous n'êtes pas à l'origine de cette demande, ignorez cet e-mail. Votre mot de passe ne change pas.\"],\"email.reset.preheader\":[\"Le lien ne fonctionne qu'une fois, pendant \",[\"hours\",\"plural\",{\"one\":[\"#\",\" heure\"],\"other\":[\"#\",\" heures\"]}],\".\"],\"email.reset.reason\":[\"Vous recevez cet e-mail car une réinitialisation de mot de passe a été demandée pour cette adresse.\"],\"email.reset.subject\":[\"Réinitialisez votre mot de passe \",[\"site\"]],\"email.reset.title\":[\"Réinitialiser votre mot de passe\"],\"email.settings\":[\"Paramètres\"],\"email.welcome.body\":[\"# Bienvenue sur \",[\"site\"],\" !\\n\\nBonjour **\",[\"username\"],\"**,\\n\\nVotre compte a bien été créé. Bienvenue à bord !\"],\"email.welcome.button\":[\"Ouvrir \",[\"site\"]],\"email.welcome.digests\":[\"Chaque semaine, vous recevrez un résumé de ce qui s'est passé ici, et chaque mois un récap des recos les plus votées. Vous pouvez désactiver l'un ou l'autre dans vos paramètres.\"],\"email.welcome.preheader\":[\"Votre compte est prêt.\"],\"email.welcome.reason\":[\"Vous recevez cet e-mail car un compte \",[\"site\"],\" a été créé avec cette adresse.\"],\"email.welcome.subject\":[\"Bienvenue sur \",[\"site\"]],\"en9o7K\":[\"Impossible de publier le commentaire\"],\"etFQQS\":[\"Pourquoi on en voudrait ?\"],\"f3cEDO\":[\"Changer l'image\"],\"fC6mXb\":[\"Voter\"],\"fI-mNw\":[\"Collections\"],\"fgLNSM\":[\"S'inscrire\"],\"gANddk\":[\"Envoi…\"],\"gGx5tM\":[\"Modification\"],\"gIQQwD\":[\"Chargement échoué\"],\"gIpLuy\":[\"Il faut un lien\"],\"gL9KKi\":[\"Qui peut la voir\"],\"gLfZlz\":[\"Ajouter à la collection\"],\"gjJ-sb\":[\"Impossible de se connecter au serveur de mises à jour en direct. Les votes et les notifications pourraient ne pas se synchroniser avant la reconnexion.\"],\"goWEhR\":[\"Piste précédente\"],\"hBuUKa\":[\"Changer le mot de passe…\"],\"hD7w09\":[\"Vous avez tout lu, tout vu, tout bu.\"],\"hJSliC\":[\"<0>\",[\"0\"],\" a publié <1>\",[\"1\"],\"\"],\"hT8hgO\":[\"Rétablir le titre suggéré\"],\"hYgDIe\":[\"Créer\"],\"he3ygx\":[\"Copier\"],\"hpCc_Z\":[\"Fermer quand même\"],\"i7K_Te\":[\"Qui suis-je ?\"],\"iDNBZe\":[\"Notifications\"],\"iWpEwy\":[\"Accueil\"],\"ipYn7W\":[\"Si cette adresse est enregistrée, vous recevrez un lien de réinitialisation sous peu.\"],\"isRobC\":[\"Nouveau\"],\"jTGs8A\":[\"Ce site n'envoie pas d'e-mails, aucun résumé ne part donc.\"],\"jbernk\":[\"Chargement du profil…\"],\"jnrIbz\":[\"Voté (\",[\"0\"],\")\"],\"joEmfT\":[\"Serveur inaccessible\"],\"jrZTZl\":[\"Pas encore de recos. Soyez le premier !\"],\"k112DD\":[\"Charger les messages plus anciens\"],\"k36iJ-\":[\"Échec de l’enregistrement de la catégorie\"],\"k4b5_X\":[\"modifié\"],\"kLttbL\":[\"Inscription échouée\"],\"lUanmi\":[\"Vous serez notifié lorsque quelqu'un suit vos collections, vote pour vos recos ou publie du nouveau contenu.\"],\"lY5h1V\":[[\"0\",\"plural\",{\"one\":[\"#\",\" reco\"],\"other\":[\"#\",\" recos\"]}]],\"lcfvr_\":[\"Supprimer ce commentaire ?\"],\"lpIMne\":[\"Les mots de passe ne correspondent pas\"],\"mDJo8W\":[\"Recos (\",[\"0\"],\")\"],\"mb_Wju\":[\"Déposez un fichier ici, ou collez-en un\"],\"mt6O6E\":[\"C'est un mirage.\"],\"nbm5sI\":[\"Aucune reco ne correspond à « \",[\"q\"],\" ».\"],\"nrjqON\":[\"Vérification de l'invitation…\"],\"nwtY4N\":[\"Une erreur est survenue\"],\"nx4kaN\":[\"Sauvegarde impossible\"],\"oTYvvN\":[[\"0\",\"plural\",{\"one\":[\"et \",\"#\",\" reco plus ancienne\"],\"other\":[\"et \",\"#\",\" recos plus anciennes\"]}]],\"ogtYkT\":[\"Mot de passe mis à jour\"],\"pCpd9p\":[\"<0>\",[\"0\"],\" vous a mentionné dans <1>\",[\"where\"],\"\"],\"pSheLH\":[\"Aucun invité pour le moment.\"],\"pd3FCq\":[\"Ce fichier dépasse \",[\"limit\"],\".\"],\"pvnfJD\":[\"Sombre\"],\"qIMfNQ\":[\"Supprimer la collection\"],\"qItrqH\":[\"Vous ne recevrez plus le récap mensuel. Vous pouvez le réactiver dans vos paramètres.\"],\"qbDAcy\":[\"Recommander\"],\"qgx_78\":[\"Suivez des collections publiques pour voir leurs recos ici.\"],\"qnUJBI\":[\"Le nouvel ordre n'a pas pu être enregistré. Réessayez.\"],\"qsqt0w\":[\"Lien d'invitation\"],\"qvFa8r\":[\"public\"],\"qvz_Pp\":[\"Reco\"],\"rCbqPX\":[\"Ce lien d'invitation est manquant, expiré ou déjà utilisé.\"],\"rDMzDU\":[\"Écrivez un message…\"],\"rg9pXu\":[\"Recherche échouée\"],\"rn6SBY\":[\"Rétablir le son\"],\"sEXJPi\":[\"Lien ou fichier\"],\"sGeXL3\":[\"Miniature\"],\"sQia9P\":[\"Se connecter\"],\"sTiqbm\":[\"invité par\"],\"sdP5Aa\":[\"[supprimé]\"],\"sgX7B-\":[\"Usage unique, valable 7 jours.\"],\"shHs8T\":[\"Saisissez une recherche.\"],\"smeBfS\":[\"Invitation invalide\"],\"teaneZ\":[\"image\"],\"tfDRzk\":[\"Enregistrer\"],\"tvmuQ0\":[\"Thème de couleur\"],\"u1lDX2\":[\"Récupération de l'aperçu…\"],\"uD0qXQ\":[\"Déposez un fichier ici\"],\"uISnsY\":[\"Suppression impossible\"],\"uMGUnV\":[\"Pas encore de collections.\"],\"ub1EEL\":[\"modifié \",[\"0\"]],\"uomLas\":[\"Ajouter une catégorie\"],\"v-Wp--\":[\"Ouvrir la source\"],\"vJBF1r\":[\"Publication…\"],\"vLhLLO\":[\"Notifications (\",[\"unreadNotificationCount\"],\" non lues)\"],\"vQMkHu\":[\"Retirer le vote\"],\"vuosjb\":[\"Menu utilisateur\"],\"wckWOP\":[\"Administration\"],\"wixIgH\":[\"Vous avez déjà un compte ? <0>Se connecter\"],\"wtxFp4\":[\"Fichier trop volumineux (max \",[\"max\"],\").\"],\"x6tjuK\":[\"Onglet par défaut\"],\"xEWkgZ\":[\"← Retour à toutes les recos\"],\"xPHtx0\":[\"Lancer la recherche\"],\"xVuNgt\":[\"+ Nouvelle collection\"],\"xc9O_u\":[\"Supprimer la reco\"],\"y1G_DI\":[\"Renommage impossible\"],\"y6sq5j\":[\"Abonné\"],\"y7oaHj\":[\"Titre de la collection\"],\"yA_6BX\":[\"Tout voir →\"],\"yBBtRm\":[\"Suivez des utilisateurs pour voir leurs recos ici.\"],\"yQ2kGp\":[\"Charger plus\"],\"y_0uwd\":[\"Hier\"],\"yxlcv5\":[\"Ne plus recevoir le récap mensuel ?\"],\"yz7wBu\":[\"Fermer\"],\"yzF66j\":[\"Lien\"],\"z0ROB3\":[\"Retirer le j'aime\"],\"z1uNN0\":[\"Aucun emoji trouvé.\"],\"zVuxvN\":[\"Actualisation…\"],\"zwBp5t\":[\"Privé\"]}"); \ No newline at end of file diff --git a/locales/fr.po b/locales/fr.po index 5f4647cc..209b34c0 100644 --- a/locales/fr.po +++ b/locales/fr.po @@ -120,13 +120,13 @@ msgstr "à vous" #. js-lingui-explicit-id #: api/mail/digest.ts:221 -#: src/pages/Settings.tsx:190 +#: src/pages/Settings.tsx:157 msgid "digest.monthly" msgstr "Récap mensuel" #. js-lingui-explicit-id #: api/mail/digest.ts:221 -#: src/pages/Settings.tsx:181 +#: src/pages/Settings.tsx:148 msgid "digest.weekly" msgstr "Résumé hebdomadaire" @@ -501,7 +501,7 @@ msgstr "Il faut un lien" msgid "a post" msgstr "une publication" -#: src/pages/Settings.tsx:108 +#: src/pages/Settings.tsx:86 msgid "Account" msgstr "Compte" @@ -550,7 +550,7 @@ msgstr "Vous avez déjà un compte ? <0>Se connecter" msgid "An unexpected error occurred." msgstr "Une erreur inattendue s'est produite." -#: src/pages/Settings.tsx:135 +#: src/pages/Settings.tsx:102 msgid "Appearance" msgstr "Apparence" @@ -565,7 +565,7 @@ msgstr "Au moins {0} caractères" msgid "At most {maxLength} characters." msgstr "{maxLength} caractères au maximum." -#: src/pages/Settings.tsx:51 +#: src/pages/Settings.tsx:44 msgid "Auto" msgstr "Auto" @@ -629,7 +629,7 @@ msgstr "Changer l'image" msgid "Change password" msgstr "Changer le mot de passe" -#: src/pages/Settings.tsx:130 +#: src/pages/Settings.tsx:97 msgid "Change password…" msgstr "Changer le mot de passe…" @@ -678,7 +678,7 @@ msgstr "Fermer sans publier ? Le fichier joint sera perdu — ce que vous avez msgid "Collapse player" msgstr "Réduire le lecteur" -#: src/pages/Settings.tsx:144 +#: src/pages/Settings.tsx:111 msgid "Color scheme" msgstr "Thème de couleur" @@ -752,11 +752,11 @@ msgstr "Création…" msgid "Current password" msgstr "Mot de passe actuel" -#: src/pages/Settings.tsx:53 +#: src/pages/Settings.tsx:46 msgid "Dark" msgstr "Sombre" -#: src/pages/Settings.tsx:155 +#: src/pages/Settings.tsx:122 msgid "Default tab" msgstr "Onglet par défaut" @@ -944,7 +944,7 @@ msgstr "Modification" msgid "Editing {0}" msgstr "Édition de {0}" -#: src/pages/Settings.tsx:179 +#: src/pages/Settings.tsx:146 msgid "Email" msgstr "E-mail" @@ -1017,7 +1017,7 @@ msgstr "Impossible de mettre à jour l'avatar" msgid "Failed to update role" msgstr "Erreur lors de la mise à jour du rôle" -#: src/pages/Settings.tsx:153 +#: src/pages/Settings.tsx:120 msgid "Feeds" msgstr "Flux" @@ -1052,7 +1052,7 @@ msgstr "Suivez des utilisateurs pour voir leurs recos ici." #: src/components/feed/FeedTabBar.tsx:43 #: src/components/profile/ProfileFollowTab.tsx:163 -#: src/pages/Settings.tsx:60 +#: src/pages/Settings.tsx:53 #: src/pages/UserPublicProfile.tsx:157 msgid "Followed" msgstr "Suivi" @@ -1087,7 +1087,7 @@ msgid "Go to login" msgstr "Aller à la connexion" #: src/components/feed/FeedTabBar.tsx:39 -#: src/pages/Settings.tsx:57 +#: src/pages/Settings.tsx:50 msgid "Hot" msgstr "Tendances" @@ -1132,7 +1132,7 @@ msgid "Invitees" msgstr "Invités" #: src/components/feed/FeedTabBar.tsx:41 -#: src/pages/Settings.tsx:59 +#: src/pages/Settings.tsx:52 msgid "Journal" msgstr "Journal" @@ -1144,7 +1144,7 @@ msgstr "Continuer l'édition" msgid "Letters, numbers and underscores only" msgstr "Lettres, chiffres et tirets bas seulement" -#: src/pages/Settings.tsx:52 +#: src/pages/Settings.tsx:45 msgid "Light" msgstr "Clair" @@ -1276,7 +1276,7 @@ msgstr "nouveau" #: src/components/chat/ChatModal.tsx:669 #: src/components/feed/FeedTabBar.tsx:40 -#: src/pages/Settings.tsx:58 +#: src/pages/Settings.tsx:51 msgid "New" msgstr "Nouveau" @@ -1368,7 +1368,7 @@ msgstr "Rien ici pour l'instant." #: src/components/layout/NotificationBell.tsx:54 #: src/pages/Notifications.tsx:308 #: src/pages/Notifications.tsx:364 -#: src/pages/Settings.tsx:164 +#: src/pages/Settings.tsx:131 msgid "Notifications" msgstr "Notifications" @@ -1376,11 +1376,11 @@ msgstr "Notifications" msgid "Notifications ({unreadNotificationCount} unread)" msgstr "Notifications ({unreadNotificationCount} non lues)" -#: src/pages/Settings.tsx:65 +#: src/pages/Settings.tsx:58 msgid "Off" msgstr "Désactivés" -#: src/pages/Settings.tsx:64 +#: src/pages/Settings.tsx:57 msgid "On" msgstr "Activés" @@ -1413,7 +1413,7 @@ msgstr "Aperçu" msgid "Page not found" msgstr "Page introuvable" -#: src/pages/Settings.tsx:124 +#: src/pages/Settings.tsx:91 #: src/pages/UserLogin.tsx:82 msgid "Password" msgstr "Mot de passe" @@ -1690,8 +1690,8 @@ msgstr "Définir un nouveau mot de passe" #: src/components/layout/UserMenu.tsx:89 #: src/components/profile/ProfileHeader.tsx:177 -#: src/pages/Settings.tsx:77 -#: src/pages/Settings.tsx:105 +#: src/pages/Settings.tsx:69 +#: src/pages/Settings.tsx:83 msgid "Settings" msgstr "Paramètres" @@ -1718,7 +1718,7 @@ msgstr "Certaines images n'ont pas pu être envoyées." msgid "Something went wrong" msgstr "Une erreur est survenue" -#: src/pages/Settings.tsx:166 +#: src/pages/Settings.tsx:133 msgid "Sounds" msgstr "Sons" @@ -1726,7 +1726,7 @@ msgstr "Sons" msgid "Start fresh" msgstr "Repartir de zéro" -#: src/pages/Settings.tsx:137 +#: src/pages/Settings.tsx:104 msgid "Style" msgstr "Style" @@ -1778,7 +1778,7 @@ msgstr "Rien à voir, circulez." msgid "This reset link is missing or malformed." msgstr "Ce lien de réinitialisation est absent ou malformé." -#: src/pages/Settings.tsx:202 +#: src/pages/Settings.tsx:169 msgid "This site doesn't send email, so no digest goes out." msgstr "Ce site n'envoie pas d'e-mails, aucun résumé ne part donc." @@ -1915,7 +1915,7 @@ msgstr "Menu utilisateur" #: src/components/profile/ProfileManageTab.tsx:61 #: src/components/profile/UsernameField.tsx:55 -#: src/pages/Settings.tsx:110 +#: src/pages/Settings.tsx:88 #: src/pages/UserLogin.tsx:75 #: src/pages/UserRegister.tsx:137 msgid "Username" @@ -1970,10 +1970,6 @@ msgstr "Écrire une réponse…" msgid "Yesterday" msgstr "Hier" -#: src/pages/Settings.tsx:116 -msgid "You can change it once every {renameDays} days. Links to your old name keep working, and nobody else can take it." -msgstr "Vous pouvez le changer une fois tous les {renameDays} jours. Les liens vers votre ancien nom marchent toujours, et personne d’autre ne peut le prendre." - #: src/components/dump/create/DuplicateNotice.tsx:61 msgid "You can post it anyway." msgstr "Vous pouvez la publier quand même." diff --git a/shared/user.ts b/shared/user.ts index fb0ad416..7484c686 100644 --- a/shared/user.ts +++ b/shared/user.ts @@ -102,12 +102,11 @@ export interface RegisterUserRequest { /** * Passed to `updateUser` by the users routes and the password reset, and sent - * by a client to `PATCH /api/me`, which takes only `description`, `email` and - * `username`; the role and the password change through their own endpoints, - * and the username through `renameUser`. + * by a client to `PATCH /api/me`, which takes only `description` and `email`; + * the role and the password change through their own endpoints, and the + * username through `renameUser`, by a moderator only. */ export interface UpdateUserRequest { - username?: string; password?: string; role?: Role; /** An empty string clears it. Null, like undefined, keeps it. */ diff --git a/shared/validation.ts b/shared/validation.ts index 7cab8663..a4057db3 100644 --- a/shared/validation.ts +++ b/shared/validation.ts @@ -38,14 +38,12 @@ export const USERNAME_RE: RegExp = new RegExp( ); /** - * How often a slug or a username may change, counted from the aliases the - * changes left: a dump's or a playlist's slug 3 times a day, a username once - * a month. Moderators are exempt. The services refuse a change past it with a - * 429, and the forms say what the limit is. + * How often a dump's or a playlist's slug may change, counted from the aliases + * the changes left: 3 times a day. Moderators are exempt. slug-service refuses + * a change past it with a 429, and the edit forms say what the limit is. */ export const RENAME_LIMITS = { SLUG: { count: 3, windowMs: 24 * 3_600_000 }, - USERNAME: { count: 1, windowMs: 30 * 24 * 3_600_000 }, } as const; /** diff --git a/src/App.css b/src/App.css index 99572989..c04541db 100644 --- a/src/App.css +++ b/src/App.css @@ -793,8 +793,11 @@ } /* ── Mode toggle — segmented control ── */ +/* Too wide for its container, it wraps onto more rows, each filled. */ .visibility-toggle { display: flex; + flex-wrap: wrap; + max-width: 100%; background: var(--color-bg); border: 1.5px solid var(--color-border); border-radius: var(--radius-lg); @@ -816,6 +819,14 @@ white-space: nowrap; } +/* On a phone, many options make two even rows; Segmented sets the columns. */ +@media (max-width: 512px) { + .visibility-toggle--many { + display: grid; + grid-template-columns: repeat(var(--segmented-columns), minmax(0, 1fr)); + } +} + .visibility-toggle button.active { background: var(--color-accent); color: var(--color-on-accent); @@ -2646,17 +2657,32 @@ body.has-player .chat-fab { border: 1.5px solid var(--color-border); border-radius: var(--radius-md); background: transparent; - color: var(--color-text-muted); + color: var(--color-text); font-size: var(--text-sm); + /* A diff --git a/src/components/profile/UsernameField.tsx b/src/components/profile/UsernameField.tsx index 497a988c..0bb97cf4 100644 --- a/src/components/profile/UsernameField.tsx +++ b/src/components/profile/UsernameField.tsx @@ -1,6 +1,6 @@ /** - * Renders the field that renames a user, on the settings page for one's own - * name and on a profile's "manage" tab for a moderator. + * Renders the field that renames a user, on a profile's "manage" tab, which + * those with `user:manage` see: users can't rename themselves. * * @module */ @@ -14,16 +14,16 @@ import { useApiForm } from "../../hooks/form/useApiForm.ts"; import { FormError } from "../form/FormError.tsx"; import { SubmitButton } from "../form/SubmitButton.tsx"; -/** Passed to {@link UsernameField} by `Settings` and `ProfileManageTab`. */ +/** Passed to {@link UsernameField} by `ProfileManageTab`. */ export interface UsernameFieldProps { username: string; /** - * Sends the new name, to `PATCH /api/me` or - * `PATCH /api/users/:key/username`, and resolves to the name the server - * kept. A refusal rejects with its message, which the field shows. + * Sends the new name to `PATCH /api/users/:key/username`, and resolves to + * the name the server kept. A refusal rejects with its message, which the + * field shows. */ save: (username: string) => Promise; - /** Shown under the field, such as how often the name may change. */ + /** Shown under the field, such as what becomes of the old name. */ hint?: ReactNode; } diff --git a/src/components/shared/Segmented.tsx b/src/components/shared/Segmented.tsx index 28b0cf75..b17d57a8 100644 --- a/src/components/shared/Segmented.tsx +++ b/src/components/shared/Segmented.tsx @@ -29,12 +29,28 @@ export interface SegmentedProps { className?: string; } +/** + * Past this many options, a phone lays them out in two even rows rather than + * one row that would wrap unevenly (`.visibility-toggle--many`). + */ +const MAX_ONE_ROW = 4; + /** Renders the options as buttons, the one equal to `value` marked active. */ export function Segmented( { options, value, onChange, disabled, className }: SegmentedProps, ): React.JSX.Element { + const many = options.length > MAX_ONE_ROW; return ( -
+
{options.map((opt) => ( {me && ( - )} diff --git a/src/test/pages/Settings.test.tsx b/src/test/pages/Settings.test.tsx index e26580c5..75a3722d 100644 --- a/src/test/pages/Settings.test.tsx +++ b/src/test/pages/Settings.test.tsx @@ -4,7 +4,7 @@ import { userEvent } from "@testing-library/user-event"; import { Settings } from "../../pages/Settings.tsx"; import { DEFAULT_SETTINGS } from "../../../shared/settings.ts"; import { calls, fail, route } from "../fetch.ts"; -import { makeSession, makeToken, wire } from "../fixtures.ts"; +import { makeSession, makeToken } from "../fixtures.ts"; import { renderApp } from "../render.tsx"; import { AUTH_STORAGE_KEY } from "../../utils/auth/storage.ts"; @@ -95,45 +95,12 @@ describe("Settings", () => { expect(weeklyOff).toHaveClass("active"); }); - test("a new username goes to the account and the session", async () => { - route( - "PATCH /api/me", - (req) => wire({ ...annSession.user, ...(req.body as object) }), - ); - const user = open(); - const name = await screen.findByRole("textbox", { name: "Username" }); - expect(screen.getByText(/once every 30 days/)).toBeInTheDocument(); - const rename = screen.getByRole("button", { name: "Rename" }); - expect(rename).toBeDisabled(); - await user.clear(name); - await user.type(name, "annie"); - await user.click(rename); - await waitFor(() => - expect(JSON.parse(localStorage.getItem(AUTH_STORAGE_KEY)!).user.username) - .toBe("annie") - ); - expect(calls("PATCH /api/me")[0].body).toEqual({ username: "annie" }); - expect(screen.getByRole("textbox", { name: "Username" })).toHaveValue( - "annie", - ); - }); - - test("a refused username says why, and a malformed one isn't sent", async () => { - route( - "PATCH /api/me", - () => fail("A username can only be changed once every 30 days", 429), - ); - const user = open(); - const name = await screen.findByRole("textbox", { name: "Username" }); - await user.type(name, " x{Enter}"); - expect(await screen.findByText(/Letters, numbers and underscores/)) - .toBeInTheDocument(); - expect(calls("PATCH /api/me")).toHaveLength(0); - await user.clear(name); - await user.type(name, "annie{Enter}"); - expect(await screen.findByRole("alert")).toHaveTextContent( - "once every 30 days", - ); + test("the username is shown, and can't be changed here", async () => { + open(); + await screen.findByText("Username"); + expect(row("Username")).toHaveTextContent(annSession.user.username); + expect(screen.queryByRole("textbox", { name: "Username" })).toBeNull(); + expect(screen.queryByRole("button", { name: "Rename" })).toBeNull(); }); test("change password keeps you signed in with the fresh tokens", async () => { diff --git a/src/test/pages/UserPublicProfile.test.tsx b/src/test/pages/UserPublicProfile.test.tsx index d3d82e35..e61be40d 100644 --- a/src/test/pages/UserPublicProfile.test.tsx +++ b/src/test/pages/UserPublicProfile.test.tsx @@ -505,7 +505,7 @@ describe("UserPublicProfile", () => { }); }); - test("a refused rename says why", async () => { + test("a refused rename says why, and a malformed one isn't sent", async () => { route( "PATCH /api/users/:id/username", () => fail("Username already exists", 409), @@ -515,7 +515,12 @@ describe("UserPublicProfile", () => { url: "/@ann/manage", }); const name = await screen.findByRole("textbox", { name: "Username" }); - await user.type(name, "x{Enter}"); + await user.type(name, " x{Enter}"); + expect(await screen.findByText(/Letters, numbers and underscores/)) + .toBeInTheDocument(); + expect(calls("PATCH /api/users/:id/username")).toHaveLength(0); + await user.clear(name); + await user.type(name, "annx{Enter}"); expect(await screen.findByRole("alert")).toHaveTextContent( "Username already exists", );