feat: working on i18n

This commit is contained in:
Paulo Castellano 2026-01-21 19:35:49 -03:00
parent f0ffe83f83
commit 38774aaaa6
16 changed files with 355 additions and 89 deletions

View file

@ -4,6 +4,7 @@
use App\Models\Language;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
@ -38,6 +39,10 @@ public function share(Request $request): array
{
$user = $request->user();
if ($user) {
App::setLocale($user->language->code);
}
$currentWorkspace = $user?->currentWorkspace;
$currentRole = null;

42
lang/en/hashtags.php Normal file
View file

@ -0,0 +1,42 @@
<?php
return [
'title' => 'Hashtags',
'description' => 'Create hashtag groups to quickly add to your posts',
'new_group' => 'New Group',
'no_groups_yet' => 'No hashtag groups yet',
'no_groups_description' => 'Create hashtag groups to quickly add popular hashtags to your posts',
'create_first_group' => 'Create your first group',
'hashtags_count' => ':count hashtags',
'create' => [
'title' => 'Create Hashtag Group',
'description' => 'Give your group a name and add hashtags separated by spaces or commas',
'name' => 'Group Name',
'name_placeholder' => 'e.g. Marketing, Travel, Food',
'hashtags' => 'Hashtags',
'hashtags_placeholder' => '#marketing #socialmedia #business #growth',
'hashtags_hint' => 'Enter hashtags separated by spaces or commas. Include the # symbol.',
'submit' => 'Create Group',
'submitting' => 'Creating...',
],
'edit' => [
'title' => 'Edit Hashtag Group',
'description' => 'Update the name and hashtags for this group',
'name' => 'Group Name',
'name_placeholder' => 'e.g. Marketing, Travel, Food',
'hashtags' => 'Hashtags',
'hashtags_placeholder' => '#marketing #socialmedia #business #growth',
'hashtags_hint' => 'Enter hashtags separated by spaces or commas. Include the # symbol.',
'submit' => 'Save Changes',
'submitting' => 'Saving...',
],
'delete' => [
'title' => 'Delete Hashtag Group',
'description' => 'Are you sure you want to delete this hashtag group? This action cannot be undone.',
'confirm' => 'Delete',
'cancel' => 'Cancel',
],
];

36
lang/en/labels.php Normal file
View file

@ -0,0 +1,36 @@
<?php
return [
'title' => 'Labels',
'description' => 'Create labels to organize and categorize your posts',
'new_label' => 'New Label',
'no_labels_yet' => 'No labels yet',
'create_first_label' => 'Create your first label',
'create' => [
'title' => 'Create Label',
'description' => 'Give your label a name and pick a color',
'name' => 'Name',
'name_placeholder' => 'Enter label name...',
'color' => 'Color',
'submit' => 'Create Label',
'submitting' => 'Creating...',
],
'edit' => [
'title' => 'Edit Label',
'description' => 'Update the name and color for this label',
'name' => 'Name',
'name_placeholder' => 'Enter label name...',
'color' => 'Color',
'submit' => 'Save Changes',
'submitting' => 'Saving...',
],
'delete' => [
'title' => 'Delete Label',
'description' => 'Are you sure you want to delete this label? This action cannot be undone.',
'confirm' => 'Delete',
'cancel' => 'Cancel',
],
];

48
lang/en/sidebar.php Normal file
View file

@ -0,0 +1,48 @@
<?php
return [
'select_workspace' => 'Select workspace',
'create_workspace' => 'Create workspace',
'create_post' => 'Create post',
'profile' => 'Profile',
'log_out' => 'Log out',
'workspace' => 'Workspace: :name',
'workspace_select' => 'Workspace: Select',
'theme' => 'Theme: :name',
'theme_light' => 'Light',
'theme_dark' => 'Dark',
'theme_system' => 'System',
'language' => 'Language: :name',
'language_select' => 'Language: Select',
'groups' => [
'posts' => 'Posts',
'configuration' => 'Configurations',
'support' => 'Support',
],
'posts' => [
'calendar' => 'Calendar',
'all' => 'All',
'scheduled' => 'Scheduled',
'posted' => 'Posted',
'drafts' => 'Drafts',
],
'config' => [
'connections' => 'Connections',
'hashtags' => 'Hashtags',
'labels' => 'Labels',
'settings' => 'Settings',
],
'support' => [
'discord' => 'Discord',
'share_feedback' => 'Share feedback',
'last_updates' => 'Last Updates',
'docs' => 'Docs',
],
];

1
lang/php_en.json Normal file

File diff suppressed because one or more lines are too long

1
lang/php_pt-BR.json Normal file

File diff suppressed because one or more lines are too long

42
lang/pt-BR/hashtags.php Normal file
View file

@ -0,0 +1,42 @@
<?php
return [
'title' => 'Hashtags',
'description' => 'Crie grupos de hashtags para adicionar rapidamente aos seus posts',
'new_group' => 'Novo Grupo',
'no_groups_yet' => 'Nenhum grupo de hashtags ainda',
'no_groups_description' => 'Crie grupos de hashtags para adicionar rapidamente hashtags populares aos seus posts',
'create_first_group' => 'Crie seu primeiro grupo',
'hashtags_count' => ':count hashtags',
'create' => [
'title' => 'Criar Grupo de Hashtags',
'description' => 'Dê um nome ao grupo e adicione hashtags separadas por espaços ou vírgulas',
'name' => 'Nome do Grupo',
'name_placeholder' => 'ex: Marketing, Viagem, Comida',
'hashtags' => 'Hashtags',
'hashtags_placeholder' => '#marketing #redessociais #negocios #crescimento',
'hashtags_hint' => 'Digite as hashtags separadas por espaços ou vírgulas. Inclua o símbolo #.',
'submit' => 'Criar Grupo',
'submitting' => 'Criando...',
],
'edit' => [
'title' => 'Editar Grupo de Hashtags',
'description' => 'Atualize o nome e as hashtags deste grupo',
'name' => 'Nome do Grupo',
'name_placeholder' => 'ex: Marketing, Viagem, Comida',
'hashtags' => 'Hashtags',
'hashtags_placeholder' => '#marketing #redessociais #negocios #crescimento',
'hashtags_hint' => 'Digite as hashtags separadas por espaços ou vírgulas. Inclua o símbolo #.',
'submit' => 'Salvar Alterações',
'submitting' => 'Salvando...',
],
'delete' => [
'title' => 'Excluir Grupo de Hashtags',
'description' => 'Tem certeza que deseja excluir este grupo de hashtags? Esta ação não pode ser desfeita.',
'confirm' => 'Excluir',
'cancel' => 'Cancelar',
],
];

36
lang/pt-BR/labels.php Normal file
View file

@ -0,0 +1,36 @@
<?php
return [
'title' => 'Etiquetas',
'description' => 'Crie etiquetas para organizar e categorizar seus posts',
'new_label' => 'Nova Etiqueta',
'no_labels_yet' => 'Nenhuma etiqueta ainda',
'create_first_label' => 'Crie sua primeira etiqueta',
'create' => [
'title' => 'Criar Etiqueta',
'description' => 'Dê um nome e escolha uma cor para sua etiqueta',
'name' => 'Nome',
'name_placeholder' => 'Digite o nome da etiqueta...',
'color' => 'Cor',
'submit' => 'Criar Etiqueta',
'submitting' => 'Criando...',
],
'edit' => [
'title' => 'Editar Etiqueta',
'description' => 'Atualize o nome e a cor desta etiqueta',
'name' => 'Nome',
'name_placeholder' => 'Digite o nome da etiqueta...',
'color' => 'Cor',
'submit' => 'Salvar Alterações',
'submitting' => 'Salvando...',
],
'delete' => [
'title' => 'Excluir Etiqueta',
'description' => 'Tem certeza que deseja excluir esta etiqueta? Esta ação não pode ser desfeita.',
'confirm' => 'Excluir',
'cancel' => 'Cancelar',
],
];

48
lang/pt-BR/sidebar.php Normal file
View file

@ -0,0 +1,48 @@
<?php
return [
'select_workspace' => 'Selecionar workspace',
'create_workspace' => 'Criar workspace',
'create_post' => 'Criar post',
'profile' => 'Perfil',
'log_out' => 'Sair',
'workspace' => 'Workspace: :name',
'workspace_select' => 'Workspace: Selecionar',
'theme' => 'Tema: :name',
'theme_light' => 'Claro',
'theme_dark' => 'Escuro',
'theme_system' => 'Sistema',
'language' => 'Idioma: :name',
'language_select' => 'Idioma: Selecionar',
'groups' => [
'posts' => 'Posts',
'configuration' => 'Configurações',
'support' => 'Suporte',
],
'posts' => [
'calendar' => 'Calendário',
'all' => 'Todos',
'scheduled' => 'Agendados',
'posted' => 'Publicados',
'drafts' => 'Rascunhos',
],
'config' => [
'connections' => 'Conexões',
'hashtags' => 'Hashtags',
'labels' => 'Etiquetas',
'settings' => 'Configurações',
],
'support' => [
'discord' => 'Discord',
'share_feedback' => 'Enviar feedback',
'last_updates' => 'Últimas Atualizações',
'docs' => 'Documentação',
],
];

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { Link, router, usePage } from '@inertiajs/vue3';
import { IconBrandDiscord, IconCalendar, IconCheck, IconSelector, IconFileText, IconHash, IconLogout, IconPlus, IconSettings, IconAffiliate, IconPencil, IconFileCheck, IconTag, IconUser, IconClock, IconMessageCircle, IconBell, IconBook, IconSun, IconMoon, IconDeviceDesktop, IconLanguage } from '@tabler/icons-vue';
import { trans } from 'laravel-vue-i18n';
import { Button } from '@/components/ui/button';
import { create as createPost } from '@/actions/App/Http/Controllers/PostController';
import { updateLanguage } from '@/actions/App/Http/Controllers/Settings/ProfileController';
@ -72,56 +73,56 @@ const { getInitials } = useInitials();
const { appearance, updateAppearance } = useAppearance();
const { state: sidebarState } = useSidebar();
const themeLabels: Record<string, string> = {
light: 'Light',
dark: 'Dark',
system: 'System',
};
const themeLabels = computed(() => ({
light: trans('sidebar.theme_light'),
dark: trans('sidebar.theme_dark'),
system: trans('sidebar.theme_system'),
}));
const postsNavItems: NavItem[] = [
const postsNavItems = computed<NavItem[]>(() => [
{
title: 'Calendar',
title: trans('sidebar.posts.calendar'),
href: calendar.url(),
icon: IconCalendar,
},
{
title: 'All',
title: trans('sidebar.posts.all'),
href: postsIndex.url(),
icon: IconFileText,
},
{
title: 'Scheduled',
title: trans('sidebar.posts.scheduled'),
href: postsIndex.url('scheduled'),
icon: IconClock,
},
{
title: 'Posted',
title: trans('sidebar.posts.posted'),
href: postsIndex.url('published'),
icon: IconFileCheck,
},
{
title: 'Drafts',
title: trans('sidebar.posts.drafts'),
href: postsIndex.url('draft'),
icon: IconPencil,
},
];
]);
const canManageWorkspace = computed(() => auth.value.role !== 'member');
const configNavItems = computed(() => {
const items: NavItem[] = [
{
title: 'Connections',
title: trans('sidebar.config.connections'),
href: accounts.url(),
icon: IconAffiliate,
},
{
title: 'Hashtags',
title: trans('sidebar.config.hashtags'),
href: hashtags.url(),
icon: IconHash,
},
{
title: 'Labels',
title: trans('sidebar.config.labels'),
href: labels.url(),
icon: IconTag,
},
@ -129,7 +130,7 @@ const configNavItems = computed(() => {
if (canManageWorkspace.value) {
items.push({
title: 'Settings',
title: trans('sidebar.config.settings'),
href: workspaceSettings.url(),
icon: IconSettings,
});
@ -138,28 +139,28 @@ const configNavItems = computed(() => {
return items;
});
const supportNavItems: NavItem[] = [
const supportNavItems = computed<NavItem[]>(() => [
{
title: 'Discord',
title: trans('sidebar.support.discord'),
href: 'https://trypost.it/discord',
icon: IconBrandDiscord,
},
{
title: 'Share feedback',
title: trans('sidebar.support.share_feedback'),
href: 'https://github.com/trypost-it/trypost/discussions',
icon: IconMessageCircle,
},
{
title: 'Last Updates',
title: trans('sidebar.support.last_updates'),
href: 'https://github.com/trypost-it/trypost/releases',
icon: IconBell,
},
{
title: 'Docs',
title: trans('sidebar.support.docs'),
href: 'https://docs.trypost.it',
icon: IconBook,
},
];
]);
function switchWorkspace(workspace: Workspace) {
router.post(switchMethod.url(workspace.id), {}, {
@ -206,7 +207,7 @@ function handleLogout() {
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{{ auth.user.name }}</span>
<span class="truncate text-xs text-muted-foreground">{{ currentWorkspace?.name ||
'Select workspace' }}</span>
$t('sidebar.select_workspace') }}</span>
</div>
<IconSelector class="ml-auto size-4" />
</SidebarMenuButton>
@ -237,7 +238,7 @@ function handleLogout() {
<DropdownMenuSubTrigger>
<img v-if="currentWorkspace?.logo.url" :src="currentWorkspace.logo.url"
:alt="currentWorkspace.name" class="mr-2 size-4 rounded-full object-cover" />
<span>Workspace: {{ currentWorkspace?.name || 'Select' }}</span>
<span>{{ currentWorkspace ? $t('sidebar.workspace', { name: currentWorkspace.name }) : $t('sidebar.workspace_select') }}</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
@ -253,7 +254,7 @@ function handleLogout() {
<DropdownMenuSeparator />
<DropdownMenuItem class="cursor-pointer gap-2" @click="createWorkspace">
<IconPlus class="size-4" />
<span>Create workspace</span>
<span>{{ $t('sidebar.create_workspace') }}</span>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
@ -267,7 +268,7 @@ function handleLogout() {
<IconSun v-if="appearance === 'light'" class="mr-2 size-4" />
<IconMoon v-else-if="appearance === 'dark'" class="mr-2 size-4" />
<IconDeviceDesktop v-else class="mr-2 size-4" />
<span>Theme: {{ themeLabels[appearance] }}</span>
<span>{{ $t('sidebar.theme', { name: themeLabels[appearance] }) }}</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
@ -275,15 +276,15 @@ function handleLogout() {
@update:model-value="updateAppearance">
<DropdownMenuRadioItem value="light">
<IconSun class="mr-2 size-4" />
<span>Light</span>
<span>{{ $t('sidebar.theme_light') }}</span>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark">
<IconMoon class="mr-2 size-4" />
<span>Dark</span>
<span>{{ $t('sidebar.theme_dark') }}</span>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system">
<IconDeviceDesktop class="mr-2 size-4" />
<span>System</span>
<span>{{ $t('sidebar.theme_system') }}</span>
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
@ -296,7 +297,7 @@ function handleLogout() {
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<IconLanguage class="mr-2 size-4" />
<span>Language: {{ currentLanguage?.name || 'Select' }}</span>
<span>{{ currentLanguage ? $t('sidebar.language', { name: currentLanguage.name }) : $t('sidebar.language_select') }}</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
@ -318,7 +319,7 @@ function handleLogout() {
<DropdownMenuItem as-child>
<Link class="cursor-pointer" :href="editProfile()">
<IconUser class="mr-2 size-4" />
Profile
{{ $t('sidebar.profile') }}
</Link>
</DropdownMenuItem>
</DropdownMenuGroup>
@ -329,7 +330,7 @@ function handleLogout() {
<DropdownMenuItem as-child>
<Link class="w-full cursor-pointer" :href="logout()" @click="handleLogout" as="button">
<IconLogout class="mr-2 size-4" />
Log out
{{ $t('sidebar.log_out') }}
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
@ -344,13 +345,13 @@ function handleLogout() {
<Link :href="createPost.url()">
<Button :size="sidebarState === 'collapsed' ? 'icon' : 'default'" class="w-full">
<IconPlus class="size-4" />
<span v-if="sidebarState === 'expanded'">Create post</span>
<span v-if="sidebarState === 'expanded'">{{ $t('sidebar.create_post') }}</span>
</Button>
</Link>
</div>
<SidebarGroup v-if="currentWorkspace">
<SidebarGroupLabel>Posts</SidebarGroupLabel>
<SidebarGroupLabel>{{ $t('sidebar.groups.posts') }}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem v-for="item in postsNavItems" :key="item.title">
@ -367,7 +368,7 @@ function handleLogout() {
</SidebarGroup>
<SidebarGroup v-if="currentWorkspace">
<SidebarGroupLabel>Configuration</SidebarGroupLabel>
<SidebarGroupLabel>{{ $t('sidebar.groups.configuration') }}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem v-for="item in configNavItems" :key="item.title">
@ -384,7 +385,7 @@ function handleLogout() {
</SidebarGroup>
<SidebarGroup v-if="currentWorkspace">
<SidebarGroupLabel>Support</SidebarGroupLabel>
<SidebarGroupLabel>{{ $t('sidebar.groups.support') }}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem v-for="item in supportNavItems" :key="item.title">

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { trans } from 'laravel-vue-i18n';
import { Button } from '@/components/ui/button';
import {
@ -44,18 +45,18 @@ const handleOpenChange = (value: boolean) => {
<Dialog :open="open" @update:open="handleOpenChange">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Create Hashtag Group</DialogTitle>
<DialogTitle>{{ $t('hashtags.create.title') }}</DialogTitle>
<DialogDescription>
Give your group a name and add hashtags separated by spaces or commas
{{ $t('hashtags.create.description') }}
</DialogDescription>
</DialogHeader>
<form @submit.prevent="submit" class="space-y-4">
<div class="space-y-2">
<Label for="create-name">Group Name</Label>
<Label for="create-name">{{ $t('hashtags.create.name') }}</Label>
<Input
id="create-name"
v-model="form.name"
placeholder="e.g. Marketing, Travel, Food"
:placeholder="trans('hashtags.create.name_placeholder')"
:class="{ 'border-destructive': form.errors.name }"
/>
<p v-if="form.errors.name" class="text-sm text-destructive">
@ -64,16 +65,16 @@ const handleOpenChange = (value: boolean) => {
</div>
<div class="space-y-2">
<Label for="create-hashtags">Hashtags</Label>
<Label for="create-hashtags">{{ $t('hashtags.create.hashtags') }}</Label>
<Textarea
id="create-hashtags"
v-model="form.hashtags"
placeholder="#marketing #socialmedia #business #growth"
:placeholder="trans('hashtags.create.hashtags_placeholder')"
rows="4"
:class="{ 'border-destructive': form.errors.hashtags }"
/>
<p class="text-sm text-muted-foreground">
Enter hashtags separated by spaces or commas. Include the # symbol.
{{ $t('hashtags.create.hashtags_hint') }}
</p>
<p v-if="form.errors.hashtags" class="text-sm text-destructive">
{{ form.errors.hashtags }}
@ -82,7 +83,7 @@ const handleOpenChange = (value: boolean) => {
<DialogFooter>
<Button type="submit" :disabled="form.processing">
{{ form.processing ? 'Creating...' : 'Create Group' }}
{{ form.processing ? $t('hashtags.create.submitting') : $t('hashtags.create.submit') }}
</Button>
</DialogFooter>
</form>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { trans } from 'laravel-vue-i18n';
import { watch } from 'vue';
import { Button } from '@/components/ui/button';
@ -55,18 +56,18 @@ const submit = () => {
<Dialog v-model:open="open">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Edit Hashtag Group</DialogTitle>
<DialogTitle>{{ $t('hashtags.edit.title') }}</DialogTitle>
<DialogDescription>
Update the name and hashtags for this group
{{ $t('hashtags.edit.description') }}
</DialogDescription>
</DialogHeader>
<form @submit.prevent="submit" class="space-y-4">
<div class="space-y-2">
<Label for="edit-name">Group Name</Label>
<Label for="edit-name">{{ $t('hashtags.edit.name') }}</Label>
<Input
id="edit-name"
v-model="form.name"
placeholder="e.g. Marketing, Travel, Food"
:placeholder="trans('hashtags.edit.name_placeholder')"
:class="{ 'border-destructive': form.errors.name }"
/>
<p v-if="form.errors.name" class="text-sm text-destructive">
@ -75,16 +76,16 @@ const submit = () => {
</div>
<div class="space-y-2">
<Label for="edit-hashtags">Hashtags</Label>
<Label for="edit-hashtags">{{ $t('hashtags.edit.hashtags') }}</Label>
<Textarea
id="edit-hashtags"
v-model="form.hashtags"
placeholder="#marketing #socialmedia #business #growth"
:placeholder="trans('hashtags.edit.hashtags_placeholder')"
rows="4"
:class="{ 'border-destructive': form.errors.hashtags }"
/>
<p class="text-sm text-muted-foreground">
Enter hashtags separated by spaces or commas. Include the # symbol.
{{ $t('hashtags.edit.hashtags_hint') }}
</p>
<p v-if="form.errors.hashtags" class="text-sm text-destructive">
{{ form.errors.hashtags }}
@ -93,7 +94,7 @@ const submit = () => {
<DialogFooter>
<Button type="submit" :disabled="form.processing">
{{ form.processing ? 'Saving...' : 'Save Changes' }}
{{ form.processing ? $t('hashtags.edit.submitting') : $t('hashtags.edit.submit') }}
</Button>
</DialogFooter>
</form>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { trans } from 'laravel-vue-i18n';
import { Button } from '@/components/ui/button';
import {
@ -56,18 +57,18 @@ const handleOpenChange = (value: boolean) => {
<Dialog :open="open" @update:open="handleOpenChange">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Create Label</DialogTitle>
<DialogTitle>{{ $t('labels.create.title') }}</DialogTitle>
<DialogDescription>
Give your label a name and pick a color
{{ $t('labels.create.description') }}
</DialogDescription>
</DialogHeader>
<form @submit.prevent="submit" class="space-y-4">
<div class="space-y-2">
<Label for="create-name">Name</Label>
<Label for="create-name">{{ $t('labels.create.name') }}</Label>
<Input
id="create-name"
v-model="form.name"
placeholder="Enter label name..."
:placeholder="trans('labels.create.name_placeholder')"
:class="{ 'border-destructive': form.errors.name }"
/>
<p v-if="form.errors.name" class="text-sm text-destructive">
@ -76,7 +77,7 @@ const handleOpenChange = (value: boolean) => {
</div>
<div class="space-y-2">
<Label>Color</Label>
<Label>{{ $t('labels.create.color') }}</Label>
<div class="flex flex-wrap gap-2">
<button
v-for="color in colors"
@ -99,7 +100,7 @@ const handleOpenChange = (value: boolean) => {
<DialogFooter>
<Button type="submit" :disabled="form.processing">
{{ form.processing ? 'Creating...' : 'Create Label' }}
{{ form.processing ? $t('labels.create.submitting') : $t('labels.create.submit') }}
</Button>
</DialogFooter>
</form>

View file

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { trans } from 'laravel-vue-i18n';
import { watch } from 'vue';
import { Button } from '@/components/ui/button';
@ -66,18 +67,18 @@ const submit = () => {
<Dialog v-model:open="open">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>Edit Label</DialogTitle>
<DialogTitle>{{ $t('labels.edit.title') }}</DialogTitle>
<DialogDescription>
Update the name and color for this label
{{ $t('labels.edit.description') }}
</DialogDescription>
</DialogHeader>
<form @submit.prevent="submit" class="space-y-4">
<div class="space-y-2">
<Label for="edit-name">Name</Label>
<Label for="edit-name">{{ $t('labels.edit.name') }}</Label>
<Input
id="edit-name"
v-model="form.name"
placeholder="Enter label name..."
:placeholder="trans('labels.edit.name_placeholder')"
:class="{ 'border-destructive': form.errors.name }"
/>
<p v-if="form.errors.name" class="text-sm text-destructive">
@ -86,7 +87,7 @@ const submit = () => {
</div>
<div class="space-y-2">
<Label>Color</Label>
<Label>{{ $t('labels.edit.color') }}</Label>
<div class="flex flex-wrap gap-2">
<button
v-for="color in colors"
@ -109,7 +110,7 @@ const submit = () => {
<DialogFooter>
<Button type="submit" :disabled="form.processing">
{{ form.processing ? 'Saving...' : 'Save Changes' }}
{{ form.processing ? $t('labels.edit.submitting') : $t('labels.edit.submit') }}
</Button>
</DialogFooter>
</form>

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import { IconPlus, IconHash, IconPencil, IconTrash } from '@tabler/icons-vue';
import { trans } from 'laravel-vue-i18n';
import { ref } from 'vue';
import ConfirmDeleteModal from '@/components/ConfirmDeleteModal.vue';
@ -37,7 +38,7 @@ const isEditDialogOpen = ref(false);
const editingHashtag = ref<Hashtag | null>(null);
const breadcrumbs: BreadcrumbItemType[] = [
{ title: 'Hashtags', href: hashtagsIndex.url() },
{ title: trans('hashtags.title'), href: hashtagsIndex.url() },
];
const openEditDialog = (hashtag: Hashtag) => {
@ -57,20 +58,20 @@ const getHashtagCount = (hashtags: string): number => {
</script>
<template>
<Head title="Hashtags" />
<Head :title="$t('hashtags.title')" />
<AppLayout :breadcrumbs="breadcrumbs">
<div class="flex flex-col gap-6 p-6">
<div v-if="hashtags.length > 0" class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold tracking-tight">Hashtags</h1>
<h1 class="text-2xl font-bold tracking-tight">{{ $t('hashtags.title') }}</h1>
<p class="text-muted-foreground">
Create hashtag groups to quickly add to your posts
{{ $t('hashtags.description') }}
</p>
</div>
<Button @click="isCreateDialogOpen = true">
<IconPlus class="mr-2 h-4 w-4" />
New Group
{{ $t('hashtags.new_group') }}
</Button>
</div>
@ -78,13 +79,13 @@ const getHashtagCount = (hashtags: string): number => {
<div class="h-16 w-16 rounded-full bg-muted flex items-center justify-center mb-4">
<IconHash class="h-8 w-8 text-muted-foreground" />
</div>
<h3 class="text-lg font-semibold mb-2">No hashtag groups yet</h3>
<h3 class="text-lg font-semibold mb-2">{{ $t('hashtags.no_groups_yet') }}</h3>
<p class="text-muted-foreground mb-4 text-center max-w-sm">
Create hashtag groups to quickly add popular hashtags to your posts
{{ $t('hashtags.no_groups_description') }}
</p>
<Button @click="isCreateDialogOpen = true">
<IconPlus class="mr-2 h-4 w-4" />
Create your first group
{{ $t('hashtags.create_first_group') }}
</Button>
</div>
@ -113,7 +114,7 @@ const getHashtagCount = (hashtags: string): number => {
</div>
</div>
<CardDescription>
{{ getHashtagCount(hashtag.hashtags) }} hashtags
{{ $t('hashtags.hashtags_count', { count: getHashtagCount(hashtag.hashtags) }) }}
</CardDescription>
</CardHeader>
<CardContent>
@ -131,9 +132,9 @@ const getHashtagCount = (hashtags: string): number => {
<ConfirmDeleteModal
ref="deleteModal"
title="Delete Hashtag Group"
description="Are you sure you want to delete this hashtag group? This action cannot be undone."
action="Delete"
cancel="Cancel"
:title="$t('hashtags.delete.title')"
:description="$t('hashtags.delete.description')"
:action="$t('hashtags.delete.confirm')"
:cancel="$t('hashtags.delete.cancel')"
/>
</template>

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import { IconPlus, IconTag, IconPencil, IconTrash } from '@tabler/icons-vue';
import { trans } from 'laravel-vue-i18n';
import { ref } from 'vue';
import ConfirmDeleteModal from '@/components/ConfirmDeleteModal.vue';
@ -37,7 +38,7 @@ const isEditDialogOpen = ref(false);
const editingLabel = ref<Label | null>(null);
const breadcrumbs: BreadcrumbItemType[] = [
{ title: 'Labels', href: labelsIndex.url() },
{ title: trans('labels.title'), href: labelsIndex.url() },
];
const openEditDialog = (label: Label) => {
@ -53,20 +54,20 @@ const handleDelete = (labelId: string) => {
</script>
<template>
<Head title="Labels" />
<Head :title="$t('labels.title')" />
<AppLayout :breadcrumbs="breadcrumbs">
<div class="flex flex-col gap-6 p-6">
<div v-if="labels.length > 0" class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold tracking-tight">Labels</h1>
<h1 class="text-2xl font-bold tracking-tight">{{ $t('labels.title') }}</h1>
<p class="text-muted-foreground">
Create labels to organize and categorize your posts
{{ $t('labels.description') }}
</p>
</div>
<Button @click="isCreateDialogOpen = true">
<IconPlus class="mr-2 h-4 w-4" />
New Label
{{ $t('labels.new_label') }}
</Button>
</div>
@ -74,13 +75,13 @@ const handleDelete = (labelId: string) => {
<div class="h-16 w-16 rounded-full bg-muted flex items-center justify-center mb-4">
<IconTag class="h-8 w-8 text-muted-foreground" />
</div>
<h3 class="text-lg font-semibold mb-2">No labels yet</h3>
<h3 class="text-lg font-semibold mb-2">{{ $t('labels.no_labels_yet') }}</h3>
<p class="text-muted-foreground mb-4 text-center max-w-sm">
Create labels to organize and categorize your posts
{{ $t('labels.description') }}
</p>
<Button @click="isCreateDialogOpen = true">
<IconPlus class="mr-2 h-4 w-4" />
Create your first label
{{ $t('labels.create_first_label') }}
</Button>
</div>
@ -125,9 +126,9 @@ const handleDelete = (labelId: string) => {
<ConfirmDeleteModal
ref="deleteModal"
title="Delete Label"
description="Are you sure you want to delete this label? This action cannot be undone."
action="Delete"
cancel="Cancel"
:title="$t('labels.delete.title')"
:description="$t('labels.delete.description')"
:action="$t('labels.delete.confirm')"
:cancel="$t('labels.delete.cancel')"
/>
</template>