mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
feat: ✨ Implement internationalization
This commit is contained in:
parent
02d9869737
commit
8c3ddc2a28
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["inlang.vs-code-extension"]
|
||||||
|
}
|
||||||
|
|
@ -75,6 +75,12 @@
|
||||||
"indentWidth": 4
|
"indentWidth": 4
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"ignore": ["node_modules/**/*", "dist/**/*", ".output", ".nuxt"]
|
"ignore": [
|
||||||
|
"node_modules/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
".output",
|
||||||
|
".nuxt",
|
||||||
|
"paraglide"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import { cn } from "@/lib/utils";
|
||||||
import type { Attachment, Emoji, Status } from "@versia/client/types";
|
import type { Attachment, Emoji, Status } from "@versia/client/types";
|
||||||
import { TriangleAlert } from "lucide-vue-next";
|
import { TriangleAlert } from "lucide-vue-next";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
|
import { languageTag } from "~/paraglide/runtime";
|
||||||
import { type BooleanSetting, SettingIds } from "~/settings";
|
import { type BooleanSetting, SettingIds } from "~/settings";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
||||||
import Attachments from "./attachments.vue";
|
import Attachments from "./attachments.vue";
|
||||||
|
|
@ -75,7 +76,7 @@ const isOverflowing = computed(() => {
|
||||||
|
|
||||||
const characterCount = plainContent?.length;
|
const characterCount = plainContent?.length;
|
||||||
const formattedCharacterCount = characterCount
|
const formattedCharacterCount = characterCount
|
||||||
? new Intl.NumberFormat("en-us").format(characterCount)
|
? new Intl.NumberFormat(languageTag()).format(characterCount)
|
||||||
: undefined;
|
: undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import type {
|
||||||
UseTimeAgoUnitNamesDefault,
|
UseTimeAgoUnitNamesDefault,
|
||||||
} from "@vueuse/core";
|
} from "@vueuse/core";
|
||||||
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
||||||
|
import { languageTag } from "~/paraglide/runtime";
|
||||||
import { SettingIds } from "~/settings";
|
import { SettingIds } from "~/settings";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
import SmallCard from "../profiles/small-card.vue";
|
import SmallCard from "../profiles/small-card.vue";
|
||||||
|
|
@ -89,7 +90,7 @@ const timeAgo = useTimeAgo(createdAt, {
|
||||||
invalid: "",
|
invalid: "",
|
||||||
} as UseTimeAgoMessages<UseTimeAgoUnitNamesDefault>,
|
} as UseTimeAgoMessages<UseTimeAgoUnitNamesDefault>,
|
||||||
});
|
});
|
||||||
const fullTime = new Intl.DateTimeFormat("en-US", {
|
const fullTime = new Intl.DateTimeFormat(languageTag(), {
|
||||||
dateStyle: "medium",
|
dateStyle: "medium",
|
||||||
timeStyle: "short",
|
timeStyle: "short",
|
||||||
}).format(createdAt);
|
}).format(createdAt);
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ const _delete = async () => {
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<slot />
|
<slot />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="w-56">
|
<DropdownMenuContent class="min-w-56">
|
||||||
<DropdownMenuLabel>Note Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>Note Actions</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@
|
||||||
<Repeat class="size-4 text-primary" />
|
<Repeat class="size-4 text-primary" />
|
||||||
<Avatar class="size-6 border" :src="avatar" :name="displayName" />
|
<Avatar class="size-6 border" :src="avatar" :name="displayName" />
|
||||||
<span class="font-semibold" v-render-emojis="emojis">{{ displayName }}</span>
|
<span class="font-semibold" v-render-emojis="emojis">{{ displayName }}</span>
|
||||||
reblogged
|
{{ m.large_vivid_horse_catch() }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Emoji } from "@versia/client/types";
|
import type { Emoji } from "@versia/client/types";
|
||||||
import { Repeat } from "lucide-vue-next";
|
import { Repeat } from "lucide-vue-next";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
|
|
||||||
const { url } = defineProps<{
|
const { url } = defineProps<{
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@
|
||||||
<div v-else class="grid grid-cols-2 p-2 gap-2">
|
<div v-else class="grid grid-cols-2 p-2 gap-2">
|
||||||
<Button variant="outline" size="sm" @click="accept">
|
<Button variant="outline" size="sm" @click="accept">
|
||||||
<Check />
|
<Check />
|
||||||
Accept
|
{{ m.slow_these_kestrel_sail() }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" @click="reject">
|
<Button variant="ghost" size="sm" @click="reject">
|
||||||
<X />
|
<X />
|
||||||
Reject
|
{{ m.weary_steep_yak_embrace() }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -42,6 +42,7 @@ import { Check, Loader, X } from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import CopyableText from "~/components/notes/copyable-text.vue";
|
import CopyableText from "~/components/notes/copyable-text.vue";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
|
|
||||||
const { follower } = defineProps<{
|
const { follower } = defineProps<{
|
||||||
|
|
@ -61,25 +62,25 @@ watch(relationship, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const accept = async () => {
|
const accept = async () => {
|
||||||
const id = toast.loading("Accepting follow request...");
|
const id = toast.loading(m.cool_slimy_coyote_affirm());
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const { data } = await client.value.acceptFollowRequest(follower.id);
|
const { data } = await client.value.acceptFollowRequest(follower.id);
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.success("Follow request accepted.");
|
toast.success(m.busy_awful_mouse_jump());
|
||||||
relationship.value = data;
|
relationship.value = data;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const reject = async () => {
|
const reject = async () => {
|
||||||
const id = toast.loading("Rejecting follow request...");
|
const id = toast.loading(m.front_sunny_penguin_flip());
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const { data } = await client.value.rejectFollowRequest(follower.id);
|
const { data } = await client.value.rejectFollowRequest(follower.id);
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.success("Follow request rejected.");
|
toast.success(m.green_flat_mayfly_trust());
|
||||||
relationship.value = data;
|
relationship.value = data;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { useForm } from "vee-validate";
|
||||||
import * as z from "zod";
|
import * as z from "zod";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Input } from "~/components/ui/input";
|
import { Input } from "~/components/ui/input";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
const { instance } = defineProps<{
|
const { instance } = defineProps<{
|
||||||
instance: Instance;
|
instance: Instance;
|
||||||
|
|
@ -26,15 +27,15 @@ const formSchema = toTypedSchema(
|
||||||
identifier: z
|
identifier: z
|
||||||
.string()
|
.string()
|
||||||
.min(3, {
|
.min(3, {
|
||||||
message: "Must be at least 3 characters long",
|
message: m.aware_house_dolphin_win(),
|
||||||
})
|
})
|
||||||
.or(
|
.or(
|
||||||
z.string().email({
|
z.string().email({
|
||||||
message: "Must be a valid email address",
|
message: m.weary_fresh_dragonfly_bless(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
password: z.string().min(3, {
|
password: z.string().min(3, {
|
||||||
message: "Must be at least 3 characters long",
|
message: m.aware_house_dolphin_win(),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -86,7 +87,7 @@ const issuerRedirectUrl = (issuerId: string) => {
|
||||||
<FormField v-slot="{ componentField }" name="identifier">
|
<FormField v-slot="{ componentField }" name="identifier">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Email (or username)
|
{{ m.fluffy_soft_wolf_cook() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="petergriffin" type="text" auto-capitalize="none"
|
<Input placeholder="petergriffin" type="text" auto-capitalize="none"
|
||||||
|
|
@ -99,7 +100,7 @@ const issuerRedirectUrl = (issuerId: string) => {
|
||||||
<FormField v-slot="{ componentField }" name="password">
|
<FormField v-slot="{ componentField }" name="password">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Password
|
{{ m.livid_bright_wallaby_quiz() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="hunter2" type="password" auto-capitalize="none"
|
<Input placeholder="hunter2" type="password" auto-capitalize="none"
|
||||||
|
|
@ -111,7 +112,7 @@ const issuerRedirectUrl = (issuerId: string) => {
|
||||||
</FormField>
|
</FormField>
|
||||||
<Button :disabled="isLoading" type="submit">
|
<Button :disabled="isLoading" type="submit">
|
||||||
<Loader v-if="isLoading" class="mr-2 h-4 w-4 animate-spin" />
|
<Loader v-if="isLoading" class="mr-2 h-4 w-4 animate-spin" />
|
||||||
Sign In
|
{{ m.fuzzy_sea_moth_absorb() }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -121,7 +122,7 @@ const issuerRedirectUrl = (issuerId: string) => {
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex justify-center text-xs uppercase">
|
<div class="relative flex justify-center text-xs uppercase">
|
||||||
<span class="bg-background px-2 text-muted-foreground">
|
<span class="bg-background px-2 text-muted-foreground">
|
||||||
Or continue with
|
{{ m.tidy_tidy_cow_cut() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
{{ emoji.shortcode }}
|
{{ emoji.shortcode }}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{{ emoji.global ? "Global" : "Uploaded by you" }}
|
{{ emoji.global ? m.lime_day_squid_pout() : m.witty_heroic_trout_cry() }}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardFooter class="p-0" v-if="canEdit">
|
<CardFooter class="p-0" v-if="canEdit">
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
<DropdownMenuItem @click="editName">
|
<DropdownMenuItem @click="editName">
|
||||||
<TextCursorInput class="mr-2 h-4 w-4" />
|
<TextCursorInput class="mr-2 h-4 w-4" />
|
||||||
<span>Rename</span>
|
{{ m.cuddly_such_swallow_hush() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<!-- <DropdownMenuItem @click="editCaption">
|
<!-- <DropdownMenuItem @click="editCaption">
|
||||||
<Captions class="mr-2 h-4 w-4" />
|
<Captions class="mr-2 h-4 w-4" />
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
<DropdownMenuSeparator /> -->
|
<DropdownMenuSeparator /> -->
|
||||||
<DropdownMenuItem @click="_delete">
|
<DropdownMenuItem @click="_delete">
|
||||||
<Delete class="mr-2 h-4 w-4" />
|
<Delete class="mr-2 h-4 w-4" />
|
||||||
<span>Delete</span>
|
{{ m.tense_quick_cod_favor() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
@ -64,6 +64,7 @@ import {
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "~/components/ui/dropdown-menu";
|
} from "~/components/ui/dropdown-menu";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
const { emoji } = defineProps<{
|
const { emoji } = defineProps<{
|
||||||
emoji: Emoji;
|
emoji: Emoji;
|
||||||
|
|
@ -75,21 +76,21 @@ const canEdit =
|
||||||
|
|
||||||
const editName = async () => {
|
const editName = async () => {
|
||||||
const result = await confirmModalService.confirm({
|
const result = await confirmModalService.confirm({
|
||||||
title: "Enter a new shortcode",
|
title: m.slimy_awful_florian_sail(),
|
||||||
defaultValue: emoji.shortcode,
|
defaultValue: emoji.shortcode,
|
||||||
confirmText: "Edit",
|
confirmText: m.teary_antsy_panda_aid(),
|
||||||
inputType: "text",
|
inputType: "text",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.confirmed) {
|
if (result.confirmed) {
|
||||||
const id = toast.loading("Updating shortcode...");
|
const id = toast.loading(m.teary_tame_gull_bless());
|
||||||
try {
|
try {
|
||||||
await client.value.updateEmoji(emoji.id, {
|
await client.value.updateEmoji(emoji.id, {
|
||||||
shortcode: result.value,
|
shortcode: result.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.success("Shortcode updated.");
|
toast.success(m.gaudy_lime_bison_adore());
|
||||||
} catch {
|
} catch {
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
}
|
}
|
||||||
|
|
@ -97,11 +98,11 @@ const editName = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const _delete = async () => {
|
const _delete = async () => {
|
||||||
const id = toast.loading("Deleting emoji...");
|
const id = toast.loading(m.weary_away_liger_zip());
|
||||||
try {
|
try {
|
||||||
await client.value.deleteEmoji(emoji.id);
|
await client.value.deleteEmoji(emoji.id);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.success("Emoji deleted.");
|
toast.success(m.crisp_whole_canary_tear());
|
||||||
} catch {
|
} catch {
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
<FormField v-slot="{ handleChange, handleBlur }" name="banner">
|
<FormField v-slot="{ handleChange, handleBlur }" name="banner">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Banner
|
{{ m.bright_late_osprey_renew() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Recommended size: over 1500x500px
|
{{ m.great_level_lamb_sway() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -19,13 +19,13 @@
|
||||||
<FormField v-slot="{ handleChange, handleBlur }" name="avatar">
|
<FormField v-slot="{ handleChange, handleBlur }" name="avatar">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Avatar
|
{{ m.safe_icy_bulldog_quell() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Recommended size: 400x400px
|
{{ m.aware_quiet_opossum_catch() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -34,13 +34,13 @@
|
||||||
<FormField v-slot="{ componentField }" name="name">
|
<FormField v-slot="{ componentField }" name="name">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Display Name
|
{{ m.mild_known_mallard_jolt() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" />
|
<Input v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Custom emojis can be used here.
|
{{ m.lime_dry_skunk_loop() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -49,13 +49,13 @@
|
||||||
<FormField v-slot="{ componentField }" name="username">
|
<FormField v-slot="{ componentField }" name="username">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Username
|
{{ m.neat_silly_dog_prosper() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input v-bind="componentField" />
|
<Input v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Changing this will break all links to your profile.
|
{{ m.petty_plane_tadpole_earn() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -64,13 +64,13 @@
|
||||||
<FormField v-slot="{ componentField }" name="bio">
|
<FormField v-slot="{ componentField }" name="bio">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Bio
|
{{ m.next_caring_ladybug_hack() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea rows="10" v-bind="componentField" />
|
<Textarea rows="10" v-bind="componentField" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Markdown and custom emojis are supported.
|
{{ m.stale_just_anaconda_earn() }}
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -79,12 +79,14 @@
|
||||||
<FormField v-slot="{ value, handleChange }" name="fields">
|
<FormField v-slot="{ value, handleChange }" name="fields">
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
Custom Fields
|
{{ m.aqua_mealy_toucan_pride() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div class="grid gap-4">
|
<div class="grid gap-4">
|
||||||
<div v-for="(field, index) in value" :key="index" class="grid items-center grid-cols-[auto,repeat(3,minmax(0,1fr))] gap-2">
|
<div v-for="(field, index) in value" :key="index"
|
||||||
<Button variant="destructive" size="icon" @click="handleChange([...value.slice(0, index), ...value.slice(index + 1)])">
|
class="grid items-center grid-cols-[auto,repeat(3,minmax(0,1fr))] gap-2">
|
||||||
|
<Button variant="destructive" size="icon"
|
||||||
|
@click="handleChange([...value.slice(0, index), ...value.slice(index + 1)])">
|
||||||
<Trash />
|
<Trash />
|
||||||
</Button>
|
</Button>
|
||||||
<Input v-model="field.name" placeholder="Name" @update:model-value="e => {
|
<Input v-model="field.name" placeholder="Name" @update:model-value="e => {
|
||||||
|
|
@ -94,8 +96,9 @@
|
||||||
handleChange([...value.slice(0, index), { name: field.name, value: e }, ...value.slice(index + 1)]);
|
handleChange([...value.slice(0, index), { name: field.name, value: e }, ...value.slice(index + 1)]);
|
||||||
}" />
|
}" />
|
||||||
</div>
|
</div>
|
||||||
<Button type="button" variant="secondary" @click="handleChange([...value, { name: '', value: '' }])">
|
<Button type="button" variant="secondary"
|
||||||
Add Field
|
@click="handleChange([...value, { name: '', value: '' }])">
|
||||||
|
{{ m.front_north_eel_gulp() }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
@ -107,10 +110,10 @@
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
Mark account as bot
|
{{ m.gaudy_each_opossum_play() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Is this account sending automated messages?
|
{{ m.grassy_acidic_gadfly_cure() }}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|
@ -124,10 +127,10 @@
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
Enable follow requests
|
{{ m.dirty_moving_shark_emerge() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Will require approval for new followers.
|
{{ m.bright_fun_mouse_boil() }}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|
@ -141,10 +144,10 @@
|
||||||
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
<FormItem class="grid grid-cols-[1fr,auto] items-center p-6 gap-2">
|
||||||
<CardHeader class="space-y-0.5 p-0">
|
<CardHeader class="space-y-0.5 p-0">
|
||||||
<FormLabel :as="CardTitle">
|
<FormLabel :as="CardTitle">
|
||||||
Allow account discovery
|
{{ m.red_vivid_cuckoo_spark() }}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Allow your account to be found in search results.
|
{{ m.plain_zany_donkey_dart() }}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
|
|
@ -179,6 +182,7 @@ import {
|
||||||
import { Input } from "~/components/ui/input";
|
import { Input } from "~/components/ui/input";
|
||||||
import { Switch } from "~/components/ui/switch";
|
import { Switch } from "~/components/ui/switch";
|
||||||
import { Textarea } from "~/components/ui/textarea";
|
import { Textarea } from "~/components/ui/textarea";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
if (!identity.value) {
|
if (!identity.value) {
|
||||||
throw new Error("Identity not found.");
|
throw new Error("Identity not found.");
|
||||||
|
|
@ -195,7 +199,10 @@ const formSchema = toTypedSchema(
|
||||||
v.size <=
|
v.size <=
|
||||||
(identity.value?.instance.configuration.accounts
|
(identity.value?.instance.configuration.accounts
|
||||||
.header_size_limit ?? 0),
|
.header_size_limit ?? 0),
|
||||||
`Banner must be less than ${identity.value?.instance.configuration.accounts.header_size_limit} bytes`,
|
m.civil_icy_ant_mend({
|
||||||
|
size: identity.value?.instance.configuration.accounts
|
||||||
|
.header_size_limit,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
avatar: z
|
avatar: z
|
||||||
|
|
@ -205,7 +212,10 @@ const formSchema = toTypedSchema(
|
||||||
v.size <=
|
v.size <=
|
||||||
(identity.value?.instance.configuration.accounts
|
(identity.value?.instance.configuration.accounts
|
||||||
.avatar_size_limit ?? 0),
|
.avatar_size_limit ?? 0),
|
||||||
`Avatar must be less than ${identity.value?.instance.configuration.accounts.avatar_size_limit} bytes`,
|
m.zippy_caring_raven_edit({
|
||||||
|
size: identity.value?.instance.configuration.accounts
|
||||||
|
.avatar_size_limit,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
name: z
|
name: z
|
||||||
|
|
@ -216,10 +226,7 @@ const formSchema = toTypedSchema(
|
||||||
),
|
),
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string()
|
||||||
.regex(
|
.regex(/^[a-z0-9_-]+$/, m.still_upper_otter_dine())
|
||||||
/^[a-z0-9_-]+$/,
|
|
||||||
"Username can only contain lowercase letters, numbers, underscores and hyphens",
|
|
||||||
)
|
|
||||||
.max(
|
.max(
|
||||||
identity.value.instance.configuration.accounts
|
identity.value.instance.configuration.accounts
|
||||||
.max_username_characters,
|
.max_username_characters,
|
||||||
|
|
@ -251,7 +258,7 @@ const form = useForm({
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
const id = toast.loading("Updating profile...");
|
const id = toast.loading(m.jolly_noble_sloth_breathe());
|
||||||
|
|
||||||
const changedData = {
|
const changedData = {
|
||||||
display_name:
|
display_name:
|
||||||
|
|
@ -287,7 +294,7 @@ const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
Object.values(changedData).filter((v) => v !== undefined).length === 0
|
Object.values(changedData).filter((v) => v !== undefined).length === 0
|
||||||
) {
|
) {
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.error("No changes");
|
toast.error(m.tough_alive_niklas_promise());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,7 +306,7 @@ const handleSubmit = form.handleSubmit(async (values) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
toast.success("Profile updated");
|
toast.success(m.spry_honest_kestrel_arrive());
|
||||||
|
|
||||||
if (identity.value) {
|
if (identity.value) {
|
||||||
identity.value.account = data;
|
identity.value.account = data;
|
||||||
|
|
|
||||||
|
|
@ -3,61 +3,61 @@
|
||||||
<DropdownMenuTrigger as-child>
|
<DropdownMenuTrigger as-child>
|
||||||
<slot />
|
<slot />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent class="w-56">
|
<DropdownMenuContent class="min-w-56">
|
||||||
<DropdownMenuLabel>Profile Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>{{ m.spicy_loved_giraffe_empower() }}</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.username)">
|
<DropdownMenuItem as="button" @click="copyText(account.username)">
|
||||||
<AtSign class="mr-2 size-4" />
|
<AtSign class="mr-2 size-4" />
|
||||||
<span>Copy username</span>
|
{{ m.cool_dark_tapir_belong() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(JSON.stringify(account, null, 4))">
|
<DropdownMenuItem as="button" @click="copyText(JSON.stringify(account, null, 4))">
|
||||||
<Code class="mr-2 size-4" />
|
<Code class="mr-2 size-4" />
|
||||||
<span>Copy API data</span>
|
{{ m.yummy_moving_scallop_sail() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.id)">
|
<DropdownMenuItem as="button" @click="copyText(account.id)">
|
||||||
<Hash class="mr-2 size-4" />
|
<Hash class="mr-2 size-4" />
|
||||||
<span>Copy ID</span>
|
{{ m.sunny_zany_jellyfish_pop() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<DropdownMenuItem as="button" @click="copyText(url)">
|
<DropdownMenuItem as="button" @click="copyText(url)">
|
||||||
<Link class="mr-2 size-4" />
|
<Link class="mr-2 size-4" />
|
||||||
<span>Copy link</span>
|
{{ m.ago_new_pelican_drip() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="copyText(account.url)">
|
<DropdownMenuItem as="button" @click="copyText(account.url)">
|
||||||
<Link class="mr-2 size-4" />
|
<Link class="mr-2 size-4" />
|
||||||
<span>Copy link (origin)</span>
|
{{ m.solid_witty_zebra_walk() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="account.url">
|
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="account.url">
|
||||||
<ExternalLink class="mr-2 size-4" />
|
<ExternalLink class="mr-2 size-4" />
|
||||||
<span>Open on remote</span>
|
{{ m.active_trite_lark_inspire() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="isLoggedIn && !isMe" />
|
<DropdownMenuSeparator v-if="isLoggedIn && !isMe" />
|
||||||
<DropdownMenuGroup v-if="isLoggedIn && !isMe">
|
<DropdownMenuGroup v-if="isLoggedIn && !isMe">
|
||||||
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
<DropdownMenuItem as="button" @click="muteUser(account.id)">
|
||||||
<VolumeX class="mr-2 size-4" />
|
<VolumeX class="mr-2 size-4" />
|
||||||
<span>Mute</span>
|
{{ m.spare_wild_mole_intend() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem as="button" @click="blockUser(account.id)">
|
<DropdownMenuItem as="button" @click="blockUser(account.id)">
|
||||||
<Ban class="mr-2 size-4" />
|
<Ban class="mr-2 size-4" />
|
||||||
<span>Block</span>
|
{{ m.misty_soft_sparrow_vent() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="isRemote" />
|
<DropdownMenuSeparator v-if="isRemote" />
|
||||||
<DropdownMenuGroup v-if="isRemote">
|
<DropdownMenuGroup v-if="isRemote">
|
||||||
<DropdownMenuItem as="button" @click="refresh">
|
<DropdownMenuItem as="button" @click="refresh">
|
||||||
<RefreshCw class="mr-2 size-4" />
|
<RefreshCw class="mr-2 size-4" />
|
||||||
<span>Refresh</span>
|
{{ m.slow_chunky_chipmunk_hush() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator v-if="isLoggedIn && !isMe" />
|
<DropdownMenuSeparator v-if="isLoggedIn && !isMe" />
|
||||||
<DropdownMenuGroup v-if="isLoggedIn && !isMe">
|
<DropdownMenuGroup v-if="isLoggedIn && !isMe">
|
||||||
<DropdownMenuItem as="button" :disabled="true">
|
<DropdownMenuItem as="button" :disabled="true">
|
||||||
<Flag class="mr-2 size-4" />
|
<Flag class="mr-2 size-4" />
|
||||||
<span>Report</span>
|
{{ m.great_few_jaguar_rise() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|
@ -87,6 +87,7 @@ import {
|
||||||
VolumeX,
|
VolumeX,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
|
||||||
const { account } = defineProps<{
|
const { account } = defineProps<{
|
||||||
account: Account;
|
account: Account;
|
||||||
|
|
@ -98,14 +99,14 @@ const isLoggedIn = !!identity.value;
|
||||||
const { copy } = useClipboard();
|
const { copy } = useClipboard();
|
||||||
const copyText = (text: string) => {
|
const copyText = (text: string) => {
|
||||||
copy(text);
|
copy(text);
|
||||||
toast.success("Copied to clipboard");
|
toast.success(m.flat_nice_worm_dream());
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = wrapUrl(`/@${account.acct}`);
|
const url = wrapUrl(`/@${account.acct}`);
|
||||||
const isRemote = account.acct.includes("@");
|
const isRemote = account.acct.includes("@");
|
||||||
|
|
||||||
const muteUser = async (userId: string) => {
|
const muteUser = async (userId: string) => {
|
||||||
const id = toast.loading("Muting user...");
|
const id = toast.loading(m.ornate_tidy_coyote_grow());
|
||||||
await client.value.muteAccount(userId);
|
await client.value.muteAccount(userId);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
|
|
@ -113,7 +114,7 @@ const muteUser = async (userId: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const blockUser = async (userId: string) => {
|
const blockUser = async (userId: string) => {
|
||||||
const id = toast.loading("Blocking user...");
|
const id = toast.loading(m.empty_smug_raven_bloom());
|
||||||
await client.value.blockAccount(userId);
|
await client.value.blockAccount(userId);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
|
|
@ -121,10 +122,10 @@ const blockUser = async (userId: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
const id = toast.loading("Requesting refresh...");
|
const id = toast.loading(m.real_every_macaw_wish());
|
||||||
await client.value.refetchAccount(account.id);
|
await client.value.refetchAccount(account.id);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
toast.success("Account refreshed");
|
toast.success(m.many_cool_fox_love());
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -3,20 +3,20 @@
|
||||||
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
||||||
<div>
|
<div>
|
||||||
<CalendarDays class="size-4" />
|
<CalendarDays class="size-4" />
|
||||||
Joined <span class="text-primary font-semibold">{{ formattedCreationDate }}</span>
|
{{ m.gross_fancy_platypus_seek() }} <span class="text-primary font-semibold">{{ formattedCreationDate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
||||||
<div>
|
<div>
|
||||||
<span class="text-primary font-semibold">{{ noteCount }}</span> Notes
|
<span class="text-primary font-semibold">{{ noteCount }}</span> {{ m.real_gray_stork_seek() }}
|
||||||
</div>
|
</div>
|
||||||
·
|
·
|
||||||
<div>
|
<div>
|
||||||
<span class="text-primary font-semibold">{{ followerCount }}</span> Followers
|
<span class="text-primary font-semibold">{{ followerCount }}</span> {{ m.teal_helpful_parakeet_hike() }}
|
||||||
</div>
|
</div>
|
||||||
·
|
·
|
||||||
<div>
|
<div>
|
||||||
<span class="text-primary font-semibold">{{ followingCount }}</span> Following
|
<span class="text-primary font-semibold">{{ followingCount }}</span> {{ m.aloof_royal_samuel_startle() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { CalendarDays } from "lucide-vue-next";
|
import { CalendarDays } from "lucide-vue-next";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
import { languageTag } from "~/paraglide/runtime";
|
||||||
|
|
||||||
const { creationDate } = defineProps<{
|
const { creationDate } = defineProps<{
|
||||||
creationDate: Date;
|
creationDate: Date;
|
||||||
|
|
@ -32,7 +34,7 @@ const { creationDate } = defineProps<{
|
||||||
followingCount: number;
|
followingCount: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const formattedCreationDate = new Intl.DateTimeFormat("en-US", {
|
const formattedCreationDate = new Intl.DateTimeFormat(languageTag(), {
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
}).format(creationDate);
|
}).format(creationDate);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
@click="relationship?.following ? unfollow() : follow()">
|
@click="relationship?.following ? unfollow() : follow()">
|
||||||
<Loader v-if="isLoading" class="animate-spin" />
|
<Loader v-if="isLoading" class="animate-spin" />
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ relationship?.following ? "Unfollow" : relationship?.requested ? "Requested" : "Follow" }}
|
{{ relationship?.following ? m.brief_upper_otter_cuddle() : relationship?.requested ? m.weak_bright_larva_grasp() : m.lazy_major_loris_grasp() }}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<ProfileActions :account="account">
|
<ProfileActions :account="account">
|
||||||
|
|
@ -29,10 +29,10 @@
|
||||||
</CopyableText>
|
</CopyableText>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row flex-wrap gap-2 -mx-2" v-if="isDeveloper || account.bot || roles.length > 0">
|
<div class="flex flex-row flex-wrap gap-2 -mx-2" v-if="isDeveloper || account.bot || roles.length > 0">
|
||||||
<ProfileBadge v-if="isDeveloper" name="Versia Developer" description="This user is a Versia developer."
|
<ProfileBadge v-if="isDeveloper" :name="m.nice_bad_grizzly_coax()" :description="m.honest_jolly_shell_blend()"
|
||||||
:verified="true" />
|
:verified="true" />
|
||||||
<ProfileBadge v-if="account.bot" name="Automated"
|
<ProfileBadge v-if="account.bot" :name="m.merry_red_shrimp_bump()"
|
||||||
description="This account is not operated as living entity." />
|
:description="m.sweet_mad_jannes_create()" />
|
||||||
<ProfileBadge v-for="role in roles" :key="role.id" :name="role.name" :description="role.description"
|
<ProfileBadge v-for="role in roles" :key="role.id" :name="role.name" :description="role.description"
|
||||||
:icon="role.icon" />
|
:icon="role.icon" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,6 +55,7 @@ import CopyableText from "~/components/notes/copyable-text.vue";
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card, CardContent, CardFooter, CardTitle } from "~/components/ui/card";
|
import { Card, CardContent, CardFooter, CardTitle } from "~/components/ui/card";
|
||||||
import { Separator } from "~/components/ui/separator";
|
import { Separator } from "~/components/ui/separator";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
import { SettingIds } from "~/settings";
|
import { SettingIds } from "~/settings";
|
||||||
import { confirmModalService } from "../modals/composable";
|
import { confirmModalService } from "../modals/composable";
|
||||||
import ProfileActions from "./profile-actions.vue";
|
import ProfileActions from "./profile-actions.vue";
|
||||||
|
|
@ -84,44 +85,48 @@ const confirmFollows = useSetting(SettingIds.ConfirmFollow);
|
||||||
const follow = async () => {
|
const follow = async () => {
|
||||||
if (confirmFollows.value.value) {
|
if (confirmFollows.value.value) {
|
||||||
const confirmation = await confirmModalService.confirm({
|
const confirmation = await confirmModalService.confirm({
|
||||||
title: "Follow user",
|
title: m.many_fair_capybara_imagine(),
|
||||||
message: `Are you sure you want to follow @${account.acct}?`,
|
message: m.mellow_yummy_jannes_cuddle({
|
||||||
confirmText: "Follow",
|
acct: `@${account.acct}`,
|
||||||
cancelText: "Cancel",
|
}),
|
||||||
|
confirmText: m.cuddly_even_tern_loop(),
|
||||||
|
cancelText: m.soft_bold_ant_attend(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmation) {
|
if (!confirmation.confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = toast.loading("Following user...");
|
const id = toast.loading(m.quick_basic_peacock_bubble());
|
||||||
const { data } = await client.value.followAccount(account.id);
|
const { data } = await client.value.followAccount(account.id);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
relationship.value = data;
|
relationship.value = data;
|
||||||
toast.success("User followed");
|
toast.success(m.awake_quick_cuckoo_smile());
|
||||||
};
|
};
|
||||||
|
|
||||||
const unfollow = async () => {
|
const unfollow = async () => {
|
||||||
if (confirmFollows.value.value) {
|
if (confirmFollows.value.value) {
|
||||||
const confirmation = await confirmModalService.confirm({
|
const confirmation = await confirmModalService.confirm({
|
||||||
title: "Unfollow user",
|
title: m.funny_aloof_swan_loop(),
|
||||||
message: `Are you sure you want to unfollow @${account.acct}?`,
|
message: m.white_best_dolphin_catch({
|
||||||
confirmText: "Unfollow",
|
acct: `@${account.acct}`,
|
||||||
cancelText: "Cancel",
|
}),
|
||||||
|
confirmText: m.cute_polite_oryx_blend(),
|
||||||
|
cancelText: m.soft_bold_ant_attend(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmation) {
|
if (!confirmation.confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = toast.loading("Unfollowing user...");
|
const id = toast.loading(m.big_safe_guppy_mix());
|
||||||
const { data } = await client.value.unfollowAccount(account.id);
|
const { data } = await client.value.unfollowAccount(account.id);
|
||||||
toast.dismiss(id);
|
toast.dismiss(id);
|
||||||
|
|
||||||
relationship.value = data;
|
relationship.value = data;
|
||||||
toast.success("User unfollowed");
|
toast.success(m.misty_level_stingray_expand());
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -30,24 +30,24 @@
|
||||||
</Button>
|
</Button>
|
||||||
<DropdownMenuItem @click="signInAction">
|
<DropdownMenuItem @click="signInAction">
|
||||||
<UserPlus />
|
<UserPlus />
|
||||||
Add account
|
{{ m.sunny_pink_hyena_walk() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator v-if="identity" />
|
<DropdownMenuSeparator v-if="identity" />
|
||||||
<DropdownMenuGroup v-if="identity">
|
<DropdownMenuGroup v-if="identity">
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem :as="NuxtLink" :href="`/@${identity.account.username}`">
|
||||||
<BadgeCheck />
|
<BadgeCheck />
|
||||||
Account
|
{{ m.factual_awful_hare_drip() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem @click="signOut()" v-if="identity">
|
<DropdownMenuItem @click="signOut()" v-if="identity">
|
||||||
<LogOut />
|
<LogOut />
|
||||||
Log out
|
{{ m.sharp_big_mallard_reap() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem :as="NuxtLink" href="/register" v-else>
|
<DropdownMenuItem :as="NuxtLink" href="/register" v-else>
|
||||||
<LogIn />
|
<LogIn />
|
||||||
Register
|
{{ m.honest_few_baboon_pop() }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
@ -62,6 +62,7 @@ import {
|
||||||
UserPlus,
|
UserPlus,
|
||||||
} from "lucide-vue-next";
|
} from "lucide-vue-next";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
import { NuxtLink } from "#components";
|
import { NuxtLink } from "#components";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
'https://cdn.versia.pub/branding/icon.svg'
|
'https://cdn.versia.pub/branding/icon.svg'
|
||||||
" :name="instance?.title" />
|
" :name="instance?.title" />
|
||||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||||
<span class="truncate font-semibold">{{ instance?.title ?? 'Versia Server' }}</span>
|
<span class="truncate font-semibold">{{ instance?.title ?? m.short_zippy_felix_kick() }}</span>
|
||||||
<span class="truncate text-xs">{{ "A Versia Server instance" }}</span>
|
<span class="truncate text-xs">{{ m.top_active_ocelot_cure() }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- <ChevronsUpDown class="ml-auto" /> -->
|
<!-- <ChevronsUpDown class="ml-auto" /> -->
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
|
<SidebarGroupLabel>{{ m.trite_real_sawfish_drum() }}</SidebarGroupLabel>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<SidebarMenuItem v-for="item in data.other.filter(
|
<SidebarMenuItem v-for="item in data.other.filter(
|
||||||
i => i.requiresLogin ? !!identity : true,
|
i => i.requiresLogin ? !!identity : true,
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
<SidebarGroup v-if="identity" class="mt-auto">
|
<SidebarGroup v-if="identity" class="mt-auto">
|
||||||
<SidebarGroupLabel>More</SidebarGroupLabel>
|
<SidebarGroupLabel>{{ m.close_short_kitten_coax() }}</SidebarGroupLabel>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<Collapsible v-for="item in data.navMain" :key="item.title" as-child class="group/collapsible">
|
<Collapsible v-for="item in data.navMain" :key="item.title" as-child class="group/collapsible">
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
|
|
@ -73,12 +73,12 @@
|
||||||
<Button variant="default" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
<Button variant="default" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
||||||
v-if="identity" @click="useEvent('composer:open')">
|
v-if="identity" @click="useEvent('composer:open')">
|
||||||
<Pen />
|
<Pen />
|
||||||
<span class="group-data-[collapsible=icon]:hidden">Compose</span>
|
<span class="group-data-[collapsible=icon]:hidden">{{ m.salty_aloof_turkey_nudge() }}</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="destructive" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
<Button variant="destructive" size="lg" class="w-full group-data-[collapsible=icon]:px-4"
|
||||||
v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)">
|
v-if="$pwa?.needRefresh" @click="$pwa?.updateServiceWorker(true)">
|
||||||
<DownloadCloud />
|
<DownloadCloud />
|
||||||
<span class="group-data-[collapsible=icon]:hidden">Update</span>
|
<span class="group-data-[collapsible=icon]:hidden">{{ m.quaint_low_felix_pave() }}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
|
|
@ -119,6 +119,8 @@ import {
|
||||||
SidebarMenuSubItem,
|
SidebarMenuSubItem,
|
||||||
SidebarRail,
|
SidebarRail,
|
||||||
} from "~/components/ui/sidebar";
|
} from "~/components/ui/sidebar";
|
||||||
|
import * as m from "~/paraglide/messages.js";
|
||||||
|
import { setLanguageTag } from "~/paraglide/runtime";
|
||||||
import { type EnumSetting, SettingIds } from "~/settings";
|
import { type EnumSetting, SettingIds } from "~/settings";
|
||||||
import Avatar from "../profiles/avatar.vue";
|
import Avatar from "../profiles/avatar.vue";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
@ -126,31 +128,33 @@ import AccountSwitcher from "./account-switcher.vue";
|
||||||
|
|
||||||
const sidebarStyle = useSetting(SettingIds.SidebarStyle) as Ref<EnumSetting>;
|
const sidebarStyle = useSetting(SettingIds.SidebarStyle) as Ref<EnumSetting>;
|
||||||
|
|
||||||
|
setLanguageTag("fr");
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
title: "Preferences",
|
title: m.patchy_seemly_hound_grace(),
|
||||||
url: "/preferences",
|
url: "/preferences",
|
||||||
icon: Settings2,
|
icon: Settings2,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "Account",
|
title: m.factual_arable_jurgen_endure(),
|
||||||
url: "/preferences/account",
|
url: "/preferences/account",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Appearance",
|
title: m.tough_clean_wolf_gleam(),
|
||||||
url: "/preferences/appearance",
|
url: "/preferences/appearance",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Behaviour",
|
title: m.legal_best_tadpole_rise(),
|
||||||
url: "/preferences/behaviour",
|
url: "/preferences/behaviour",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Emojis",
|
title: m.novel_trite_sloth_adapt(),
|
||||||
url: "/preferences/emojis",
|
url: "/preferences/emojis",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Roles",
|
title: m.safe_green_mink_cook(),
|
||||||
url: "/preferences/roles",
|
url: "/preferences/roles",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -158,31 +162,31 @@ const data = {
|
||||||
],
|
],
|
||||||
other: [
|
other: [
|
||||||
{
|
{
|
||||||
name: "Home",
|
name: m.bland_chunky_sparrow_propel(),
|
||||||
url: "/home",
|
url: "/home",
|
||||||
icon: House,
|
icon: House,
|
||||||
requiresLogin: true,
|
requiresLogin: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Public",
|
name: m.lost_trick_dog_grace(),
|
||||||
url: "/public",
|
url: "/public",
|
||||||
icon: MapIcon,
|
icon: MapIcon,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Local",
|
name: m.crazy_game_parrot_pave(),
|
||||||
url: "/local",
|
url: "/local",
|
||||||
icon: BedSingle,
|
icon: BedSingle,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Global",
|
name: m.real_tame_moose_greet(),
|
||||||
url: "/global",
|
url: "/global",
|
||||||
icon: Globe,
|
icon: Globe,
|
||||||
requiresLogin: false,
|
requiresLogin: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Notifications",
|
name: m.that_patchy_mare_snip(),
|
||||||
url: "/notifications",
|
url: "/notifications",
|
||||||
icon: Bell,
|
icon: Bell,
|
||||||
requiresLogin: true,
|
requiresLogin: true,
|
||||||
|
|
|
||||||
112
messages/en.json
Normal file
112
messages/en.json
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
|
"bland_chunky_sparrow_propel": "Home",
|
||||||
|
"lost_trick_dog_grace": "Public",
|
||||||
|
"crazy_game_parrot_pave": "Local",
|
||||||
|
"real_tame_moose_greet": "Global",
|
||||||
|
"that_patchy_mare_snip": "Notifications",
|
||||||
|
"patchy_seemly_hound_grace": "Preferences",
|
||||||
|
"factual_arable_jurgen_endure": "Account",
|
||||||
|
"tough_clean_wolf_gleam": "Appearance",
|
||||||
|
"legal_best_tadpole_rise": "Behaviour",
|
||||||
|
"novel_trite_sloth_adapt": "Emojis",
|
||||||
|
"safe_green_mink_cook": "Roles",
|
||||||
|
"short_zippy_felix_kick": "Versia Server",
|
||||||
|
"top_active_ocelot_cure": "A Versia Server instance",
|
||||||
|
"trite_real_sawfish_drum": "Navigation",
|
||||||
|
"close_short_kitten_coax": "More",
|
||||||
|
"salty_aloof_turkey_nudge": "Compose",
|
||||||
|
"quaint_low_felix_pave": "Update",
|
||||||
|
"sunny_pink_hyena_walk": "Add account",
|
||||||
|
"factual_awful_hare_drip": "Account",
|
||||||
|
"sharp_big_mallard_reap": "Log out",
|
||||||
|
"honest_few_baboon_pop": "Register",
|
||||||
|
"spicy_loved_giraffe_empower": "Profile Actions",
|
||||||
|
"cool_dark_tapir_belong": "Copy username",
|
||||||
|
"yummy_moving_scallop_sail": "Copy API data",
|
||||||
|
"sunny_zany_jellyfish_pop": "Copy ID",
|
||||||
|
"ago_new_pelican_drip": "Copy link",
|
||||||
|
"solid_witty_zebra_walk": "Copy link (origin)",
|
||||||
|
"active_trite_lark_inspire": "Open on remote",
|
||||||
|
"spare_wild_mole_intend": "Mute",
|
||||||
|
"misty_soft_sparrow_vent": "Block",
|
||||||
|
"slow_chunky_chipmunk_hush": "Refresh",
|
||||||
|
"great_few_jaguar_rise": "Report",
|
||||||
|
"flat_nice_worm_dream": "Copied to clipboard",
|
||||||
|
"ornate_tidy_coyote_grow": "Muting user...",
|
||||||
|
"empty_smug_raven_bloom": "Blocking user...",
|
||||||
|
"real_every_macaw_wish": "Requesting refresh...",
|
||||||
|
"many_cool_fox_love": "Account refreshed",
|
||||||
|
"gross_fancy_platypus_seek": "Joined",
|
||||||
|
"real_gray_stork_seek": "Notes",
|
||||||
|
"teal_helpful_parakeet_hike": "Followers",
|
||||||
|
"aloof_royal_samuel_startle": "Following",
|
||||||
|
"brief_upper_otter_cuddle": "Unfollow",
|
||||||
|
"weak_bright_larva_grasp": "Requested",
|
||||||
|
"lazy_major_loris_grasp": "Follow",
|
||||||
|
"honest_jolly_shell_blend": "This user is a Versia developer.",
|
||||||
|
"nice_bad_grizzly_coax": "Versia Developer",
|
||||||
|
"merry_red_shrimp_bump": "Automated",
|
||||||
|
"sweet_mad_jannes_create": "This account is not operated as living entity.",
|
||||||
|
"many_fair_capybara_imagine": "Follow user",
|
||||||
|
"vivid_each_warthog_edit": "Are you sure you want to follow @${account.acct}?",
|
||||||
|
"cuddly_even_tern_loop": "Follow",
|
||||||
|
"soft_bold_ant_attend": "Cancel",
|
||||||
|
"quick_basic_peacock_bubble": "Following user...",
|
||||||
|
"awake_quick_cuckoo_smile": "User followed",
|
||||||
|
"funny_aloof_swan_loop": "Unfollow user",
|
||||||
|
"cute_polite_oryx_blend": "Unfollow",
|
||||||
|
"dirty_inclusive_meerkat_nudge": "Cancel",
|
||||||
|
"big_safe_guppy_mix": "Unfollowing user...",
|
||||||
|
"misty_level_stingray_expand": "User unfollowed",
|
||||||
|
"lime_day_squid_pout": "Global",
|
||||||
|
"witty_heroic_trout_cry": "Uploaded by you",
|
||||||
|
"cuddly_such_swallow_hush": "Rename",
|
||||||
|
"tense_quick_cod_favor": "Delete",
|
||||||
|
"slimy_awful_florian_sail": "Enter a new shortcode",
|
||||||
|
"teary_antsy_panda_aid": "Edit",
|
||||||
|
"teary_tame_gull_bless": "Updating shortcode...",
|
||||||
|
"gaudy_lime_bison_adore": "Shortcode updated.",
|
||||||
|
"weary_away_liger_zip": "Deleting emoji...",
|
||||||
|
"crisp_whole_canary_tear": "Emoji deleted.",
|
||||||
|
"mellow_yummy_jannes_cuddle": "Are you sure you want to follow {acct}?",
|
||||||
|
"white_best_dolphin_catch": "Are you sure you want to unfollow {acct}?",
|
||||||
|
"bright_late_osprey_renew": "Banner",
|
||||||
|
"great_level_lamb_sway": "Recommended size: over 1500x500px",
|
||||||
|
"safe_icy_bulldog_quell": "Avatar",
|
||||||
|
"aware_quiet_opossum_catch": "Recommended size: 400x400px",
|
||||||
|
"mild_known_mallard_jolt": "Display Name",
|
||||||
|
"lime_dry_skunk_loop": "Custom emojis can be used here.",
|
||||||
|
"neat_silly_dog_prosper": "Username",
|
||||||
|
"petty_plane_tadpole_earn": "Changing this will break all links to your profile.",
|
||||||
|
"next_caring_ladybug_hack": "Bio",
|
||||||
|
"stale_just_anaconda_earn": "Markdown and custom emojis are supported.",
|
||||||
|
"aqua_mealy_toucan_pride": "Custom Fields",
|
||||||
|
"front_north_eel_gulp": "Add field",
|
||||||
|
"gaudy_each_opossum_play": "Mark account as bot",
|
||||||
|
"grassy_acidic_gadfly_cure": "Is this account sending automated messages?",
|
||||||
|
"dirty_moving_shark_emerge": "Enable follow requests",
|
||||||
|
"bright_fun_mouse_boil": "Will require approval for new followers.",
|
||||||
|
"red_vivid_cuckoo_spark": "Allow account discovery",
|
||||||
|
"plain_zany_donkey_dart": "Allow your account to be found in search results.",
|
||||||
|
"jolly_noble_sloth_breathe": "Updating profile...",
|
||||||
|
"tough_alive_niklas_promise": "No changes",
|
||||||
|
"spry_honest_kestrel_arrive": "Profile updated",
|
||||||
|
"civil_icy_ant_mend": "Banner must be less than {size} bytes",
|
||||||
|
"zippy_caring_raven_edit": "Avatar must be less than {size} bytes",
|
||||||
|
"still_upper_otter_dine": "Username can only contain lowercase letters, numbers, underscores and hyphens",
|
||||||
|
"aware_house_dolphin_win": "Must be at least 3 characters long",
|
||||||
|
"weary_fresh_dragonfly_bless": "Must be a valid email address",
|
||||||
|
"sunny_novel_otter_glow": "Must be at least 3 characters long",
|
||||||
|
"fluffy_soft_wolf_cook": "Email (or username)",
|
||||||
|
"livid_bright_wallaby_quiz": "Password",
|
||||||
|
"fuzzy_sea_moth_absorb": "Sign In",
|
||||||
|
"tidy_tidy_cow_cut": "Or continue with",
|
||||||
|
"slow_these_kestrel_sail": "Accept",
|
||||||
|
"weary_steep_yak_embrace": "Reject",
|
||||||
|
"cool_slimy_coyote_affirm": "Accepting follow request...",
|
||||||
|
"busy_awful_mouse_jump": "Follow request accepted.",
|
||||||
|
"front_sunny_penguin_flip": "Rejecting follow request...",
|
||||||
|
"green_flat_mayfly_trust": "Follow request rejected.",
|
||||||
|
"large_vivid_horse_catch": "reblogged"
|
||||||
|
}
|
||||||
109
messages/fr.json
Normal file
109
messages/fr.json
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
|
"bland_chunky_sparrow_propel": "Acceuil",
|
||||||
|
"lost_trick_dog_grace": "Public",
|
||||||
|
"crazy_game_parrot_pave": "Local",
|
||||||
|
"real_tame_moose_greet": "Global",
|
||||||
|
"that_patchy_mare_snip": "Notifications",
|
||||||
|
"patchy_seemly_hound_grace": "Préférences",
|
||||||
|
"factual_arable_jurgen_endure": "Compte",
|
||||||
|
"tough_clean_wolf_gleam": "Apparence",
|
||||||
|
"legal_best_tadpole_rise": "Comportement",
|
||||||
|
"novel_trite_sloth_adapt": "Émojis",
|
||||||
|
"safe_green_mink_cook": "Rôles",
|
||||||
|
"short_zippy_felix_kick": "Versia Server",
|
||||||
|
"top_active_ocelot_cure": "Une instance de Versia Server",
|
||||||
|
"trite_real_sawfish_drum": "Navigation",
|
||||||
|
"close_short_kitten_coax": "Plus",
|
||||||
|
"salty_aloof_turkey_nudge": "Composer",
|
||||||
|
"quaint_low_felix_pave": "Mettre à jour",
|
||||||
|
"sunny_pink_hyena_walk": "Ajouter un compte",
|
||||||
|
"factual_awful_hare_drip": "Compte",
|
||||||
|
"sharp_big_mallard_reap": "Se déconnecter",
|
||||||
|
"honest_few_baboon_pop": "Créer un compte",
|
||||||
|
"spicy_loved_giraffe_empower": "Actions du profil",
|
||||||
|
"cool_dark_tapir_belong": "Copier le nom d'utilisateur",
|
||||||
|
"yummy_moving_scallop_sail": "Copier les données de l'API",
|
||||||
|
"sunny_zany_jellyfish_pop": "Copier l'ID",
|
||||||
|
"ago_new_pelican_drip": "Copier le lien",
|
||||||
|
"solid_witty_zebra_walk": "Copier le lien (origine)",
|
||||||
|
"active_trite_lark_inspire": "Ouvrir l'origine",
|
||||||
|
"spare_wild_mole_intend": "Muter",
|
||||||
|
"misty_soft_sparrow_vent": "Bloquer",
|
||||||
|
"slow_chunky_chipmunk_hush": "Rafraîchir",
|
||||||
|
"great_few_jaguar_rise": "Signaler",
|
||||||
|
"flat_nice_worm_dream": "Copié dans le presse-papiers",
|
||||||
|
"ornate_tidy_coyote_grow": "Mutage de l'utilisateur...",
|
||||||
|
"empty_smug_raven_bloom": "Blocage de l'utilisateur...",
|
||||||
|
"real_every_macaw_wish": "Demande d'actualisation...",
|
||||||
|
"many_cool_fox_love": "Compte actualisé",
|
||||||
|
"gross_fancy_platypus_seek": "Inscrit en",
|
||||||
|
"real_gray_stork_seek": "Notes",
|
||||||
|
"teal_helpful_parakeet_hike": "Abonné•e•s",
|
||||||
|
"aloof_royal_samuel_startle": "Abonnements",
|
||||||
|
"brief_upper_otter_cuddle": "Se désabonner",
|
||||||
|
"weak_bright_larva_grasp": "Demandé",
|
||||||
|
"lazy_major_loris_grasp": "Suivre",
|
||||||
|
"honest_jolly_shell_blend": "Cet utilisateur est un développeur Versia.",
|
||||||
|
"nice_bad_grizzly_coax": "Développeur Versia",
|
||||||
|
"merry_red_shrimp_bump": "Automatisé",
|
||||||
|
"sweet_mad_jannes_create": "Ce compte n'est pas utilisé par une entité vivante.",
|
||||||
|
"many_fair_capybara_imagine": "Suivre",
|
||||||
|
"cuddly_even_tern_loop": "Suivre",
|
||||||
|
"soft_bold_ant_attend": "Annuler",
|
||||||
|
"quick_basic_peacock_bubble": "Abonnement en cours...",
|
||||||
|
"awake_quick_cuckoo_smile": "Utilisateur suivi",
|
||||||
|
"funny_aloof_swan_loop": "Se désabonner",
|
||||||
|
"cute_polite_oryx_blend": "Se désabonner",
|
||||||
|
"dirty_inclusive_meerkat_nudge": "Annuler",
|
||||||
|
"big_safe_guppy_mix": "Désabonnement...",
|
||||||
|
"misty_level_stingray_expand": "Utilisateur désabonné",
|
||||||
|
"lime_day_squid_pout": "Global",
|
||||||
|
"witty_heroic_trout_cry": "Ajouté par vous",
|
||||||
|
"cuddly_such_swallow_hush": "Renommer",
|
||||||
|
"tense_quick_cod_favor": "Supprimer",
|
||||||
|
"slimy_awful_florian_sail": "Entrez un nouveau nom",
|
||||||
|
"teary_antsy_panda_aid": "Modifier",
|
||||||
|
"teary_tame_gull_bless": "Mise à jour du nom...",
|
||||||
|
"gaudy_lime_bison_adore": "Nom mis à jour.",
|
||||||
|
"weary_away_liger_zip": "Suppression de l'emoji...",
|
||||||
|
"crisp_whole_canary_tear": "Emoji supprimé.",
|
||||||
|
"mellow_yummy_jannes_cuddle": "Êtes-vous sûr de vouloir suivre {acct} ?",
|
||||||
|
"white_best_dolphin_catch": "Etes-vous sûr de vouloir vous désabonner de {acct} ?",
|
||||||
|
"bright_late_osprey_renew": "Bannière",
|
||||||
|
"great_level_lamb_sway": "Taille recommandée : plus de 1500x500px",
|
||||||
|
"safe_icy_bulldog_quell": "Avatar",
|
||||||
|
"aware_quiet_opossum_catch": "Taille recommandée : 400x400px",
|
||||||
|
"mild_known_mallard_jolt": "Nom d'affichage",
|
||||||
|
"lime_dry_skunk_loop": "Des émojis personnalisés peuvent être utilisés ici.",
|
||||||
|
"neat_silly_dog_prosper": "Nom d'utilisateur",
|
||||||
|
"petty_plane_tadpole_earn": "Changer ce nom brisera tous les liens vers votre profil.",
|
||||||
|
"next_caring_ladybug_hack": "Bio",
|
||||||
|
"stale_just_anaconda_earn": "Le Markdown et les émojis personnalisés sont utilisables.",
|
||||||
|
"aqua_mealy_toucan_pride": "Champs personnalisés",
|
||||||
|
"front_north_eel_gulp": "Ajouter un champ",
|
||||||
|
"dirty_moving_shark_emerge": "Activer les demandes de suivi",
|
||||||
|
"bright_fun_mouse_boil": "Une approbation sera nécessaire pour les nouveaux abonnés.",
|
||||||
|
"red_vivid_cuckoo_spark": "Autoriser la découverte de compte",
|
||||||
|
"plain_zany_donkey_dart": "Permettez à votre compte d'être trouvé dans les résultats de recherche.",
|
||||||
|
"jolly_noble_sloth_breathe": "Mise à jour du profil...",
|
||||||
|
"tough_alive_niklas_promise": "Aucun changement",
|
||||||
|
"spry_honest_kestrel_arrive": "Profil mis à jour",
|
||||||
|
"civil_icy_ant_mend": "La bannière doit être inférieure à {size} octets",
|
||||||
|
"zippy_caring_raven_edit": "L'avatar doit être inférieur à {size} octets",
|
||||||
|
"still_upper_otter_dine": "Le nom d'utilisateur ne peut contenir que des lettres minuscules, des chiffres, des traits de soulignement et des tirets",
|
||||||
|
"aware_house_dolphin_win": "Doit comporter au moins 3 caractères",
|
||||||
|
"weary_fresh_dragonfly_bless": "Doit être une adresse e-mail valide",
|
||||||
|
"sunny_novel_otter_glow": "Doit comporter au moins 3 caractères",
|
||||||
|
"fluffy_soft_wolf_cook": "Email (ou nom d'utilisateur)",
|
||||||
|
"livid_bright_wallaby_quiz": "Mot de passe",
|
||||||
|
"fuzzy_sea_moth_absorb": "Se connecter",
|
||||||
|
"tidy_tidy_cow_cut": "Ou continuer avec",
|
||||||
|
"slow_these_kestrel_sail": "Accepter",
|
||||||
|
"weary_steep_yak_embrace": "Rejeter",
|
||||||
|
"cool_slimy_coyote_affirm": "Acceptation de la demande de suivi...",
|
||||||
|
"busy_awful_mouse_jump": "Demande de suivi acceptée.",
|
||||||
|
"front_sunny_penguin_flip": "Rejet de la demande de suivi...",
|
||||||
|
"green_flat_mayfly_trust": "Demande de suivi rejetée.",
|
||||||
|
"large_vivid_horse_catch": "a reblogué•e"
|
||||||
|
}
|
||||||
|
|
@ -20,11 +20,12 @@
|
||||||
"url": "git+https://github.com/versia-pub/frontend.git"
|
"url": "git+https://github.com/versia-pub/frontend.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build",
|
"build": "paraglide-js compile --project ./project.inlang --outdir ./paraglide && nuxt build",
|
||||||
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 bun --bun nuxt dev --https --https.cert config/versia-fe.localhost.pem --https.key config/versia-fe.localhost-key.pem --host versia-fe.localhost",
|
"dev": "NODE_TLS_REJECT_UNAUTHORIZED=0 bun --bun nuxt dev --https --https.cert config/versia-fe.localhost.pem --https.key config/versia-fe.localhost-key.pem --host versia-fe.localhost",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"emojis:generate": "bun run utils/emojis.ts",
|
"emojis:generate": "bun run utils/emojis.ts",
|
||||||
"postinstall": "nuxt prepare",
|
"postinstall": "paraglide-js compile --project ./project.inlang --outdir ./paraglide && nuxt prepare",
|
||||||
|
"rebuild-i18n": "paraglide-js compile --project ./project.inlang --outdir ./paraglide",
|
||||||
"lint": "bunx @biomejs/biome check .",
|
"lint": "bunx @biomejs/biome check .",
|
||||||
"check": "bunx tsc -p ."
|
"check": "bunx tsc -p ."
|
||||||
},
|
},
|
||||||
|
|
@ -69,7 +70,8 @@
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
"@types/html-to-text": "^9.0.4",
|
"@types/html-to-text": "^9.0.4",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vue-tsc": "^2.1.10"
|
"vue-tsc": "^2.1.10",
|
||||||
|
"@inlang/paraglide-js": "1.11.3"
|
||||||
},
|
},
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"@biomejs/biome",
|
"@biomejs/biome",
|
||||||
|
|
|
||||||
1
project.inlang/.gitignore
vendored
Normal file
1
project.inlang/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
cache
|
||||||
1
project.inlang/project_id
Normal file
1
project.inlang/project_id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
9d8c2839bac9b12c091cd75e30f73bf4570b61f052e0bae7af076988269f44ed
|
||||||
15
project.inlang/settings.json
Normal file
15
project.inlang/settings.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/project-settings",
|
||||||
|
"sourceLanguageTag": "en",
|
||||||
|
"languageTags": ["en", "fr"],
|
||||||
|
"modules": [
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
|
||||||
|
],
|
||||||
|
"plugin.inlang.messageFormat": {
|
||||||
|
"pathPattern": "./messages/{languageTag}.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"allowImportingTsExtensions": true
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowJs": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue