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 c7fab132ab
commit 681efe95af
86 changed files with 5175 additions and 2816 deletions
+8 -4
View File
@@ -7,14 +7,14 @@
class="transition text-sm/6 font-semibold text-zinc-400 hover:text-zinc-100 inline-flex gap-x-2 items-center duration-200 hover:scale-105"
>
<ArrowLeftIcon class="h-4 w-4" aria-hidden="true" />
Back to Library
{{ $t("library.back") }}
</NuxtLink>
</div>
<h2 class="text-2xl font-bold font-display text-zinc-100">
{{ collection?.name }}
</h2>
<p class="mt-2 text-zinc-400">
{{ collection?.entries?.length || 0 }} games
{{ $t("library.gameCount", collection?.entries?.length || 0) }}
</p>
</div>
@@ -37,15 +37,19 @@ import { ArrowLeftIcon } from "@heroicons/vue/20/solid";
const route = useRoute();
const collections = await useCollections();
const { t } = useI18n();
const collection = computed(() =>
collections.value.find((e) => e.id == route.params.id),
);
if (collection.value === undefined) {
throw createError({ statusCode: 404, statusMessage: "Collection not found" });
throw createError({
statusCode: 404,
statusMessage: t("library.collection.notFound"),
});
}
useHead({
title: collection.value?.name || "Collection",
title: collection.value?.name || t("library.collection.title"),
});
</script>
+8 -6
View File
@@ -25,7 +25,7 @@
class="transition text-sm/6 font-semibold text-zinc-400 hover:text-zinc-100 inline-flex gap-x-2 items-center duration-200 hover:scale-105"
>
<ArrowLeftIcon class="h-4 w-4" aria-hidden="true" />
Back to Library
{{ $t("library.back") }}
</NuxtLink>
</div>
@@ -44,7 +44,7 @@
type="button"
class="inline-flex items-center justify-center gap-x-2 rounded-md bg-blue-600 px-3.5 py-2.5 text-base font-semibold font-display text-white shadow-sm transition-all duration-200 hover:bg-blue-500 hover:scale-105 hover:shadow-blue-500/25 hover:shadow-lg active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
>
Open in Launcher
{{ $t("library.launcherOpen") }}
<ArrowTopRightOnSquareIcon
class="-mr-0.5 h-5 w-5"
aria-hidden="true"
@@ -57,7 +57,7 @@
:to="`/store/${game.id}`"
class="inline-flex items-center justify-center gap-x-2 rounded-md bg-zinc-800 px-3.5 py-2.5 text-base font-semibold font-display text-white shadow-sm transition-all duration-200 hover:bg-zinc-700 hover:scale-105 hover:shadow-lg active:scale-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600"
>
View in Store
{{ $t("store.view") }}
<ArrowUpRightIcon class="-mr-0.5 h-5 w-5" aria-hidden="true" />
</NuxtLink>
</div>
@@ -69,7 +69,7 @@
<div class="col-start-1 lg:col-start-2 space-y-6">
<div class="bg-zinc-800/50 rounded-xl p-6 backdrop-blur-sm">
<h2 class="text-xl font-display font-semibold text-zinc-100 mb-4">
Game Images
{{ $t("store.images") }}
</h2>
<div class="relative">
<VueCarousel :items-to-show="1">
@@ -86,7 +86,7 @@
<div
class="h-48 lg:h-96 aspect-[1/2] flex items-center justify-center text-zinc-700 font-bold font-display"
>
No images
{{ $t("store.noImages") }}
</div>
</VueSlide>
@@ -121,13 +121,15 @@ import {
} from "@heroicons/vue/20/solid";
import { micromark } from "micromark";
const { t } = useI18n();
const route = useRoute();
const id = route.params.id.toString();
const { game: rawGame } = await $dropFetch(`/api/v1/games/${id}`);
const game = computed(() => {
if (!rawGame) {
throw createError({ statusCode: 404, message: "Game not found" });
throw createError({ statusCode: 404, message: t("library.notFound") });
}
return rawGame;
});
+12 -8
View File
@@ -2,10 +2,11 @@
<div>
<div class="flex flex-col gap-y-8">
<div class="max-w-2xl">
<h2 class="text-2xl font-bold font-display text-zinc-100">Library</h2>
<h2 class="text-2xl font-bold font-display text-zinc-100">
{{ $t("userHeader.links.library") }}
</h2>
<p class="mt-2 text-zinc-400">
Organize your games into collections for easy access, and access all
your games.
{{ $t("library.subheader") }}
</p>
</div>
@@ -29,7 +30,7 @@
{{ collection.name }}
</h3>
<p class="mt-1 text-sm text-zinc-400">
{{ collection.entries.length }} game(s)
{{ $t("library.gameCount", [collection.entries.length]) }}
</p>
</NuxtLink>
@@ -60,11 +61,11 @@
<h3
class="text-lg font-semibold text-zinc-400 group-hover:text-zinc-300"
>
Create Collection
{{ $t("library.collection.create") }}
</h3>
</div>
<p class="mt-1 text-sm text-zinc-500 group-hover:text-zinc-400">
Add a new collection to organize your games
{{ $t("library.collection.subheader") }}
</p>
</div>
</button>
@@ -73,7 +74,9 @@
<!-- game library grid -->
<div>
<h1 class="text-zinc-100 text-xl font-bold font-display">All Games</h1>
<h1 class="text-zinc-100 text-xl font-bold font-display">
{{ $t("library.addGames") }}
</h1>
<div class="mt-4 flex flex-row flex-wrap justify-left gap-4">
<GamePanel
v-for="game in games"
@@ -99,11 +102,12 @@ const collectionCreateOpen = ref(false);
const currentlyDeleting = ref<Collection | undefined>();
const { t } = useI18n();
const library = await useLibrary();
const games = library.value.entries.map((e) => e.game);
useHead({
title: "Home",
title: t("userHeader.links.library"),
});
</script>