feat: translate gallery

This commit is contained in:
tresabhi committed 2025-01-07 13:00:43 -06:00
1 parent 9de41529d1
commit 6b2dda0f9d
8 files changed
+52 -38

No files matched your search

-24
View File
@@ -65,27 +65,3 @@ jobs:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v4
deploy-vercel:
name: Deploy to Vercel
needs: build
runs-on: ubuntu-latest
environment:
name: Preview
steps:
# vercel begs for the .git dir so here it is my guy
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
path: ./dist/website
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: ./dist/website
+6 -1
View File
@@ -778,7 +778,12 @@
"name": "Gallery", "name": "Gallery",
"button": "Browse all", "button": "Browse all",
"description": "Blitz player avatars", "description": "Blitz player avatars",
"seo_description": "All World of Tanks Blitz player avatars" "seo_description": "All World of Tanks Blitz player avatars",
"last_updated": "Last updated on %s1",
"search": {
"hint": "Search avatars...",
"results": "%s1 avatars"
}
}, },
"session": { "session": {
"name": "Session", "name": "Session",
@@ -2,6 +2,7 @@ import { Flex, Text } from '@radix-ui/themes';
import fuzzysort from 'fuzzysort'; import fuzzysort from 'fuzzysort';
import { times } from 'lodash-es'; import { times } from 'lodash-es';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { literals } from '../../core/i18n/literals';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
import { GalleryEphemeral } from '../../stores/galleryEphemeral'; import { GalleryEphemeral } from '../../stores/galleryEphemeral';
import { GalleryCard } from './Card'; import { GalleryCard } from './Card';
@@ -21,7 +22,7 @@ const PREVIEW_COUNT = 28;
export function GalleryList({ avatars }: GalleryListProps) { export function GalleryList({ avatars }: GalleryListProps) {
const search = GalleryEphemeral.use((state) => state.search); const search = GalleryEphemeral.use((state) => state.search);
const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED); const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED);
const { locale } = useLocale(); const { locale, strings } = useLocale();
const filtered = useMemo(() => { const filtered = useMemo(() => {
setLoadedCards(DEFAULT_LOADED); setLoadedCards(DEFAULT_LOADED);
@@ -37,7 +38,9 @@ export function GalleryList({ avatars }: GalleryListProps) {
return ( return (
<> <>
<Text align="center" color="gray"> <Text align="center" color="gray">
{filtered.length.toLocaleString(locale)} avatars {literals(strings.website.tools.gallery.search.results, [
filtered.length.toLocaleString(locale),
])}
</Text> </Text>
<Flex wrap="wrap" gap="4" justify="center"> <Flex wrap="wrap" gap="4" justify="center">
@@ -1,15 +1,17 @@
import { MagnifyingGlassIcon } from '@radix-ui/react-icons'; import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
import { TextField } from '@radix-ui/themes'; import { TextField } from '@radix-ui/themes';
import { useRef } from 'react'; import { useRef } from 'react';
import { useLocale } from '../../hooks/useLocale';
import { GalleryEphemeral } from '../../stores/galleryEphemeral'; import { GalleryEphemeral } from '../../stores/galleryEphemeral';
export function GallerySearch() { export function GallerySearch() {
const input = useRef<HTMLInputElement>(null); const input = useRef<HTMLInputElement>(null);
const mutateGalleryEphemeral = GalleryEphemeral.useMutation(); const mutateGalleryEphemeral = GalleryEphemeral.useMutation();
const { strings } = useLocale();
return ( return (
<TextField.Root <TextField.Root
placeholder="Search avatars..." placeholder={strings.website.tools.gallery.search.hint}
ref={input} ref={input}
onChange={(event) => { onChange={(event) => {
mutateGalleryEphemeral((draft) => { mutateGalleryEphemeral((draft) => {
@@ -1,6 +1,10 @@
import { assertSecret, type BlitzGlossary } from '@blitzkit/core'; import { assertSecret, type BlitzGlossary } from '@blitzkit/core';
export async function fetchGlossary() { export async function fetchGlossary(locale: string) {
const response = await fetch(assertSecret(import.meta.env.WOTB_GLOSSARY)); const response = await fetch(
`${assertSecret(
import.meta.env.WOTB_GLOSSARY_DOMAIN,
)}/${locale}${assertSecret(import.meta.env.WOTB_GLOSSARY_PATH)}`,
);
return (await response.json()) as BlitzGlossary; return (await response.json()) as BlitzGlossary;
} }
@@ -3,14 +3,32 @@ import { Callout, Flex } from '@radix-ui/themes';
import { GalleryList, type Avatar } from '../../../../components/Gallery/List'; import { GalleryList, type Avatar } from '../../../../components/Gallery/List';
import { GallerySearch } from '../../../../components/Gallery/Search'; import { GallerySearch } from '../../../../components/Gallery/Search';
import { PageWrapper } from '../../../../components/PageWrapper'; import { PageWrapper } from '../../../../components/PageWrapper';
import { literals } from '../../../../core/i18n/literals';
import {
LocaleProvider,
useLocale,
type LocaleAcceptorProps,
} from '../../../../hooks/useLocale';
import { GalleryEphemeral } from '../../../../stores/galleryEphemeral'; import { GalleryEphemeral } from '../../../../stores/galleryEphemeral';
interface PageProps { interface ContentProps {
avatars: Avatar[]; avatars: Avatar[];
lastUpdated: string; lastUpdated: string;
} }
export function Page({ avatars, lastUpdated }: PageProps) { type PageProps = ContentProps & LocaleAcceptorProps;
export function Page({ locale, ...props }: PageProps) {
return (
<LocaleProvider locale={locale}>
<Content {...props} />
</LocaleProvider>
);
}
function Content({ avatars, lastUpdated }: ContentProps) {
const { strings } = useLocale();
return ( return (
<PageWrapper maxWidth="80rem" color="gold"> <PageWrapper maxWidth="80rem" color="gold">
<Flex justify="center"> <Flex justify="center">
@@ -18,7 +36,11 @@ export function Page({ avatars, lastUpdated }: PageProps) {
<Callout.Icon> <Callout.Icon>
<CalendarIcon /> <CalendarIcon />
</Callout.Icon> </Callout.Icon>
<Callout.Text>Last updated on {lastUpdated}</Callout.Text> <Callout.Text>
{literals(strings.website.tools.gallery.last_updated, [
lastUpdated,
])}
</Callout.Text>
</Callout.Root> </Callout.Root>
</Flex> </Flex>
@@ -7,7 +7,8 @@ import { Page } from './_index';
export { getStaticPaths } from '../../index.astro'; export { getStaticPaths } from '../../index.astro';
const glossary = await fetchGlossary(); const { locale } = Astro.params;
const glossary = await fetchGlossary(locale);
const avatars = Object.entries(glossary) const avatars = Object.entries(glossary)
.filter( .filter(
([key, value]) => ([key, value]) =>
@@ -20,9 +21,9 @@ const avatars = Object.entries(glossary)
({ id: key.replace('avatar_', ''), name: value.title }) satisfies Avatar, ({ id: key.replace('avatar_', ''), name: value.title }) satisfies Avatar,
) )
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
const lastUpdated = new Date().toLocaleDateString(); const lastUpdated = new Date().toLocaleDateString(locale);
--- ---
<ToolLayout tool={galleryTool}> <ToolLayout tool={galleryTool}>
<Page {avatars} {lastUpdated} client:load /> <Page {locale} {avatars} {lastUpdated} client:load />
</ToolLayout> </ToolLayout>
@@ -1,8 +1,9 @@
import { DEFAULT_LOCALE } from '@blitzkit/core';
import type { APIRoute, GetStaticPaths } from 'astro'; import type { APIRoute, GetStaticPaths } from 'astro';
import { fetchGlossary } from '../../../core/blitz/fetchGlossary'; import { fetchGlossary } from '../../../core/blitz/fetchGlossary';
export const getStaticPaths = (async () => { export const getStaticPaths = (async () => {
const glossary = await fetchGlossary(); const glossary = await fetchGlossary(DEFAULT_LOCALE);
const avatarKeys = Object.keys(glossary) const avatarKeys = Object.keys(glossary)
.filter((key) => key.startsWith('avatar_')) .filter((key) => key.startsWith('avatar_'))
.map((key) => key.replace('avatar_', '')); .map((key) => key.replace('avatar_', ''));
@@ -10,7 +11,7 @@ export const getStaticPaths = (async () => {
return avatarKeys.map((avatar) => ({ params: { avatar } })); return avatarKeys.map((avatar) => ({ params: { avatar } }));
}) satisfies GetStaticPaths; }) satisfies GetStaticPaths;
const glossary = await fetchGlossary(); const glossary = await fetchGlossary(DEFAULT_LOCALE);
export const GET: APIRoute<{}, { avatar: string }> = async ({ params }) => { export const GET: APIRoute<{}, { avatar: string }> = async ({ params }) => {
const image = const image =