i18n Support and Task improvements (#80)

* fix: release workflow

* feat: move mostly to internal tasks system

* feat: migrate object clean to new task system

* fix: release not  getting good base version

* chore: set version v0.3.0

* chore: style

* feat: basic task concurrency

* feat: temp pages to fill in page links

* feat: inital i18n support

* feat: localize store page

* chore: style

* fix: weblate doesn't like multifile thing

* fix: update nuxt

* feat: improved error logging

* fix: using old task api

* feat: basic translation docs

* feat: add i18n eslint plugin

* feat: translate store and auth pages

* feat: more translation progress

* feat: admin dash i18n progress

* feat: enable update check by default in prod

* fix: using wrong i18n keys

* fix: crash in library sources page

* feat: finish i18n work

* fix: missing i18n translations

* feat: use twemoji for emojis

* feat: sanatize object ids

* fix: EmojiText's alt text

* fix: UserWidget not using links

* feat: cache and auth for emoji api

* fix: add more missing translations
This commit is contained in:
Husky
2025-06-04 19:53:30 -04:00
committed by GitHub
parent 05af43b622
commit 2233aec23f
86 changed files with 5175 additions and 2816 deletions
@@ -6,7 +6,7 @@
>
<div class="ml-4 mt-2">
<h3 class="text-base font-semibold text-zinc-100 text-sm">
Unread notifications
{{ $t("account.notifications.unread") }}
</h3>
</div>
<div class="ml-4 mt-2 shrink-0">
@@ -15,7 +15,15 @@
type="button"
class="text-sm text-zinc-400"
>
View all &rarr;
<i18n-t
keypath="account.notifications.all"
tag="span"
scope="global"
>
<template #arrow>
<span aria-hidden="true">{{ $t("chars.arrow") }}</span>
</template>
</i18n-t>
</NuxtLink>
</div>
</div>
@@ -32,7 +40,7 @@
v-if="props.notifications.length == 0"
class="text-sm text-zinc-400 p-3 text-center w-full"
>
No notifications
{{ $t("account.notifications.none") }}
</div>
</PanelWidget>
</template>
@@ -0,0 +1,78 @@
<script setup lang="ts">
import { Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/vue";
import { ChevronDownIcon } from "@heroicons/vue/16/solid";
const { locales, locale: currLocale, setLocale } = useI18n();
function localToEmoji(local: string): string {
switch (local) {
case "en":
case "en-gb":
case "en-ca":
case "en-au":
case "en-us": {
return "🇺🇸";
}
case "en-pirate": {
return "🏴‍☠️";
}
default: {
return "❓";
}
}
}
</script>
<template>
<Menu as="div" class="relative inline-block">
<MenuButton>
<UserHeaderWidget>
<div
class="inline-flex items-center text-zinc-300 hover:text-white h-5"
>
<EmojiText :emoji="localToEmoji(currLocale)" />
<!-- <span class="ml-2 text-sm font-bold">{{ locale }}</span> -->
<ChevronDownIcon class="ml-3 h-4" />
</div>
</UserHeaderWidget>
</MenuButton>
<transition
enter-active-class="transition ease-out duration-100"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<MenuItems
class="absolute right-0 top-10 z-50 w-56 origin-top-right focus:outline-none shadow-md"
>
<PanelWidget class="flex-col gap-y-2">
<div class="flex flex-col">
<MenuItem
v-for="locale in locales"
:key="locale.code"
hydrate-on-visible
as="div"
>
<button @click="setLocale(locale.code)">
<EmojiText :emoji="localToEmoji(locale.code)" />
{{ locale.name }}
</button>
</MenuItem>
</div>
</PanelWidget>
</MenuItems>
</transition>
</Menu>
</template>
<style scoped>
img.emoji {
height: 1em;
width: 1em;
margin: 0 0.05em 0 0.1em;
vertical-align: -0.1em;
}
</style>
+13 -13
View File
@@ -46,29 +46,28 @@
hydrate-on-visible
as="div"
>
<!-- TODO: think this would work better as a NuxtLink instead of a button -->
<button
:href="nav.route"
<NuxtLink
:to="nav.route"
:class="[
active ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400',
'w-full text-left transition block px-4 py-2 text-sm',
]"
@click="() => navigateTo(nav.route, close)"
@click="close"
>
{{ nav.label }}
</button>
</NuxtLink>
</MenuItem>
<MenuItem v-slot="{ active }" hydrate-on-visible as="div">
<!-- TODO: think this would work better as a NuxtLink instead of a button -->
<a
<MenuItem v-slot="{ active, close }" hydrate-on-visible as="div">
<NuxtLink
to="/auth/signout"
:class="[
active ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400',
'w-full text-left transition block px-4 py-2 text-sm',
]"
href="/auth/signout"
@click="close"
>
Signout
</a>
{{ $t("auth.signout") }}
</NuxtLink>
</MenuItem>
</div>
</PanelWidget>
@@ -84,17 +83,18 @@ import { useObject } from "~/composables/objects";
import type { NavigationItem } from "~/composables/types";
const user = useUser();
const { t } = useI18n();
const navigation: NavigationItem[] = [
user.value?.admin
? {
label: "Admin Dashboard",
label: t("userHeader.profile.admin"),
route: "/admin",
prefix: "",
}
: undefined,
{
label: "Account settings",
label: t("userHeader.profile.settings"),
route: "/account",
prefix: "",
},