forked from BRETT/BRETT
i18n implemented
This commit is contained in:
@@ -25,21 +25,22 @@ interface ListedUser {
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
const TYPE_OPTIONS = [
|
||||
{ value: 'image', label: 'Bilder', icon: 'ph:image-fill' },
|
||||
{ value: 'video', label: 'Videos', icon: 'ph:video-camera-fill' },
|
||||
{ value: 'audio', label: 'Audio', icon: 'ph:music-note-fill' },
|
||||
{ value: 'youtube', label: 'YouTube', icon: 'simple-icons:youtube' },
|
||||
{ value: 'twitch', label: 'Twitch', icon: 'simple-icons:twitch' },
|
||||
{ value: 'link', label: 'Links', icon: 'ph:link-fill' },
|
||||
{ value: 'image', labelKey: 'filters.images', icon: 'ph:image-fill' },
|
||||
{ value: 'video', labelKey: 'filters.videos', icon: 'ph:video-camera-fill' },
|
||||
{ value: 'audio', labelKey: 'filters.audio', icon: 'ph:music-note-fill' },
|
||||
{ value: 'youtube', labelKey: null, icon: 'simple-icons:youtube' },
|
||||
{ value: 'twitch', labelKey: null, icon: 'simple-icons:twitch' },
|
||||
{ value: 'link', labelKey: 'filters.links', icon: 'ph:link-fill' },
|
||||
] as const
|
||||
|
||||
const DAY_OPTIONS = [
|
||||
{ value: '1', label: 'Heute' },
|
||||
{ value: '7', label: '7 Tage' },
|
||||
{ value: '30', label: '30 Tage' },
|
||||
{ value: '365', label: 'Letztes Jahr' },
|
||||
{ value: '1', labelKey: 'filters.today' },
|
||||
{ value: '7', labelKey: 'filters.days7' },
|
||||
{ value: '30', labelKey: 'filters.days30' },
|
||||
{ value: '365', labelKey: 'filters.lastYear' },
|
||||
] as const
|
||||
|
||||
const open = ref(false)
|
||||
@@ -79,7 +80,10 @@ const daysModel = computed<string>({
|
||||
})
|
||||
const daysOption = computed(() => DAY_OPTIONS.find(d => d.value === daysModel.value))
|
||||
|
||||
const typeLabel = (value: string) => TYPE_OPTIONS.find(t => t.value === value)?.label ?? value
|
||||
const typeLabel = (value: string) => {
|
||||
const option = TYPE_OPTIONS.find(o => o.value === value)
|
||||
return option ? (option.labelKey ? t(option.labelKey) : option.value) : value
|
||||
}
|
||||
const typeIcon = (value: string) => TYPE_OPTIONS.find(t => t.value === value)?.icon ?? 'ph:image-fill'
|
||||
|
||||
const activeCount = computed(() =>
|
||||
@@ -111,36 +115,36 @@ function clearFilters() {
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline" size="sm" class="gap-1.5">
|
||||
<Icon name="ph:sliders-horizontal-fill" class="size-4" />
|
||||
Filter
|
||||
{{ $t('filters.filter') }}
|
||||
<Badge v-if="activeCount" variant="secondary" class="ml-0.5 px-1.5 text-xs">{{ activeCount }}</Badge>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-72" align="start">
|
||||
<FieldGroup class="gap-4">
|
||||
<Field>
|
||||
<FieldLabel>Typ</FieldLabel>
|
||||
<FieldLabel>{{ $t('filters.type') }}</FieldLabel>
|
||||
<ToggleGroup v-model="selectedTypes" type="multiple" variant="outline" :spacing="2" class="flex-wrap justify-start">
|
||||
<ToggleGroupItem
|
||||
v-for="t in TYPE_OPTIONS"
|
||||
:key="t.value"
|
||||
:value="t.value"
|
||||
:aria-label="t.label"
|
||||
v-for="o in TYPE_OPTIONS"
|
||||
:key="o.value"
|
||||
:value="o.value"
|
||||
:aria-label="typeLabel(o.value)"
|
||||
class="gap-1.5 rounded-md px-2.5"
|
||||
>
|
||||
<Icon :name="t.icon" class="size-4" />
|
||||
{{ t.label }}
|
||||
<Icon :name="o.icon" class="size-4" />
|
||||
{{ typeLabel(o.value) }}
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>Uploader</FieldLabel>
|
||||
<FieldLabel>{{ $t('filters.uploader') }}</FieldLabel>
|
||||
<Popover v-model:open="uploaderOpen">
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="outline" role="combobox" :aria-expanded="uploaderOpen" class="w-full justify-between font-normal">
|
||||
<span class="flex items-center gap-1.5 truncate">
|
||||
<Icon name="ph:user-fill" class="size-4 shrink-0 text-muted-foreground" />
|
||||
<span :style="selectedUser?.color ? { color: selectedUser.color } : undefined" class="truncate">
|
||||
{{ selectedUser?.name ?? 'Alle Uploader' }}
|
||||
{{ selectedUser?.name ?? $t('filters.allUploaders') }}
|
||||
</span>
|
||||
</span>
|
||||
<Icon name="ph:caret-up-down-fill" class="size-4 shrink-0 text-muted-foreground" />
|
||||
@@ -148,13 +152,13 @@ function clearFilters() {
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-64 p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Nutzer suchen…" />
|
||||
<CommandInput :placeholder="$t('filters.searchUser')" />
|
||||
<CommandList>
|
||||
<CommandEmpty>Kein Nutzer gefunden.</CommandEmpty>
|
||||
<CommandEmpty>{{ $t('filters.noUser') }}</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem value="alle" class="gap-2" @select="selectUser(null)">
|
||||
<Icon name="ph:check-fill" :class="cn('size-4', !uploaderId ? 'opacity-100' : 'opacity-0')" />
|
||||
Alle Uploader
|
||||
{{ $t('filters.allUploaders') }}
|
||||
</CommandItem>
|
||||
<CommandItem
|
||||
v-for="u in uploaders"
|
||||
@@ -187,19 +191,19 @@ function clearFilters() {
|
||||
</Popover>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel>Zeitraum</FieldLabel>
|
||||
<FieldLabel>{{ $t('filters.period') }}</FieldLabel>
|
||||
<Select v-model="daysModel">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue placeholder="Alle" />
|
||||
<SelectValue :placeholder="$t('filters.allTime')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">Gesamter Zeitraum</SelectItem>
|
||||
<SelectItem v-for="d in DAY_OPTIONS" :key="d.value" :value="d.value">{{ d.label }}</SelectItem>
|
||||
<SelectItem value="all">{{ $t('filters.allTime') }}</SelectItem>
|
||||
<SelectItem v-for="d in DAY_OPTIONS" :key="d.value" :value="d.value">{{ $t(d.labelKey) }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
<Button variant="ghost" size="sm" :disabled="!activeCount" class="self-start" @click="clearFilters">
|
||||
Zurücksetzen
|
||||
{{ $t('common.reset') }}
|
||||
</Button>
|
||||
</FieldGroup>
|
||||
</PopoverContent>
|
||||
@@ -211,7 +215,7 @@ function clearFilters() {
|
||||
<button
|
||||
type="button"
|
||||
class="text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label="Tag-Filter entfernen"
|
||||
:aria-label="$t('filters.removeTag')"
|
||||
@click="setQuery({ tag: undefined })"
|
||||
>
|
||||
<Icon name="ph:x-fill" class="size-3.5" />
|
||||
@@ -223,7 +227,7 @@ function clearFilters() {
|
||||
<button
|
||||
type="button"
|
||||
class="text-muted-foreground transition-colors hover:text-foreground"
|
||||
:aria-label="`Typ-Filter ${typeLabel(t)} entfernen`"
|
||||
:aria-label="$t('filters.removeType', { label: typeLabel(t) })"
|
||||
@click="selectedTypes = selectedTypes.filter(v => v !== t)"
|
||||
>
|
||||
<Icon name="ph:x-fill" class="size-3.5" />
|
||||
@@ -235,7 +239,7 @@ function clearFilters() {
|
||||
<button
|
||||
type="button"
|
||||
class="text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label="Uploader-Filter entfernen"
|
||||
:aria-label="$t('filters.removeUploader')"
|
||||
@click="setQuery({ uploader: undefined })"
|
||||
>
|
||||
<Icon name="ph:x-fill" class="size-3.5" />
|
||||
@@ -243,11 +247,11 @@ function clearFilters() {
|
||||
</span>
|
||||
<span v-if="daysOption" class="inline-flex items-center gap-1.5 rounded-sm bg-muted px-2.5 py-1 text-sm">
|
||||
<Icon name="ph:calendar-fill" class="size-3.5" />
|
||||
{{ daysOption.label }}
|
||||
{{ daysOption ? $t(daysOption.labelKey) : '' }}
|
||||
<button
|
||||
type="button"
|
||||
class="text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label="Zeitraum-Filter entfernen"
|
||||
:aria-label="$t('filters.removePeriod')"
|
||||
@click="setQuery({ days: undefined })"
|
||||
>
|
||||
<Icon name="ph:x-fill" class="size-3.5" />
|
||||
|
||||
Reference in New Issue
Block a user