mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: homepage
This commit is contained in:
1 parent
1d44ecab18
commit
d9688f6051
18 files changed
+303
-153
No files matched your search
+138
-1
@@ -1,5 +1,18 @@
|
||||
{
|
||||
"test": {
|
||||
"$": "test",
|
||||
"long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is %s1 because it has the color of %s2. I like it so much that I will say its name 3 times! %s1! %s1! %s1! Once again, it's %s2, not %s3.",
|
||||
"fox": "The quick brown fox jumps over the lazy dog."
|
||||
},
|
||||
|
||||
"common": {
|
||||
"branches": {
|
||||
"local": "dev",
|
||||
"dev": "beta",
|
||||
"opentest": "opentest",
|
||||
"preview": "preview"
|
||||
},
|
||||
|
||||
"locales": {
|
||||
"en": "English",
|
||||
"es": "Spanish",
|
||||
@@ -689,5 +702,129 @@
|
||||
}
|
||||
},
|
||||
|
||||
"website": {}
|
||||
"website": {
|
||||
"home": {
|
||||
"hero": {
|
||||
"credit": "By %s1 ❤️"
|
||||
},
|
||||
"hot_tanks": {
|
||||
"title": "Popular tanks",
|
||||
"subtitle": "Last %s1 days"
|
||||
},
|
||||
"other_projects": {
|
||||
"title": "Other cool projects"
|
||||
}
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"tankopedia": {
|
||||
"name": "Tankopedia",
|
||||
"button": "Find tanks",
|
||||
"description": "Blitz tank encyclopedia",
|
||||
"seo_description": "Statistics, armor, and more for all tanks in World of Tanks Blitz"
|
||||
},
|
||||
"compare": {
|
||||
"name": "Compare",
|
||||
"button": "Compare tanks",
|
||||
"description": "Compare tank statistics",
|
||||
"seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz"
|
||||
},
|
||||
"performance": {
|
||||
"name": "Performance",
|
||||
"button": "View tanks",
|
||||
"description": "Tank performance statistics",
|
||||
"seo_description": "Best tanks and their performances in World of Tanks Blitz"
|
||||
},
|
||||
"gallery": {
|
||||
"name": "Gallery",
|
||||
"button": "Browse all",
|
||||
"description": "Blitz player avatars",
|
||||
"seo_description": "All World of Tanks Blitz player avatars"
|
||||
},
|
||||
"session": {
|
||||
"name": "Session",
|
||||
"button": "Start tracking",
|
||||
"description": "Live session stats",
|
||||
"seo_description": "Track your stats in real time as you play World of Tanks Blitz"
|
||||
},
|
||||
"tier_list": {
|
||||
"name": "Tier list",
|
||||
"button": "Rank tanks",
|
||||
"description": "Rank Blitz tanks",
|
||||
"seo_description": "Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz"
|
||||
},
|
||||
"embed": {
|
||||
"name": "Embed",
|
||||
"button": "Create now",
|
||||
"description": "Embeds for streaming",
|
||||
"seo_description": "Create embeds for streaming World of Tanks Blitz with extreme customization"
|
||||
},
|
||||
"charts": {
|
||||
"name": "Charts",
|
||||
"button": "Nerd out",
|
||||
"description": "Lots of graphs",
|
||||
"seo_description": "Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz"
|
||||
},
|
||||
"rating": {
|
||||
"name": "Rating",
|
||||
"button": "View leaderboard",
|
||||
"description": "Full rating leaderboard",
|
||||
"seo_description": "Live and archived full rating leaderboard for World of Tanks Blitz"
|
||||
},
|
||||
"discord": {
|
||||
"name": "Discord bot",
|
||||
"button": "Install now",
|
||||
"description": "Stats right in Discord",
|
||||
"seo_description": "Get stats right in Discord with BlitzKit's Discord bot"
|
||||
},
|
||||
"more": {
|
||||
"name": "More coming soon",
|
||||
"button": "Join Discord",
|
||||
"description": "What we're making",
|
||||
"seo_description": "What we're making with BlitzKit"
|
||||
},
|
||||
"players": {
|
||||
"name": "Players",
|
||||
"button": "Lookup player",
|
||||
"description": "Periodical player statistics",
|
||||
"seo_description": "Periodical player statistics in World of Tanks Blitz"
|
||||
},
|
||||
"home": {
|
||||
"name": "Home",
|
||||
"button": "Get started",
|
||||
"description": "Get started with BlitzKit",
|
||||
"seo_description": "Get started with BlitzKit"
|
||||
},
|
||||
"blitz_analysis": {
|
||||
"name": "BlitzAnalysis[]",
|
||||
"button": "Analyze now",
|
||||
"description": "Rich historic average stats",
|
||||
"seo_description": "Rich historic average stats in World of Tanks Blitz"
|
||||
},
|
||||
"blitz_insider": {
|
||||
"name": "Blitz Insider",
|
||||
"button": "Discover today",
|
||||
"description": "In-game session stats",
|
||||
"seo_description": "In-game session stats in World of Tanks Blitz"
|
||||
}
|
||||
},
|
||||
|
||||
"footer": {
|
||||
"motto": "Everything World of Tanks Blitz",
|
||||
"copyright": "Unaffiliated with Wargaming or any service",
|
||||
|
||||
"about": {
|
||||
"title": "About",
|
||||
"credits": "Credits",
|
||||
"discord": "Discord",
|
||||
"github": "GitHub",
|
||||
"patches": "Patches"
|
||||
},
|
||||
"legal": {
|
||||
"title": "Legal",
|
||||
"privacy": "Privacy policy",
|
||||
"terms": "Terms of service"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,2 @@
|
||||
export const SUPPORTED_LOCALES = [
|
||||
'en',
|
||||
'es',
|
||||
'hr',
|
||||
'pt',
|
||||
'ru',
|
||||
'uk',
|
||||
'zh',
|
||||
] as const;
|
||||
|
||||
export const SUPPORTED_LOCALES = ['en', 'es', 'hr', 'pt', 'ru', 'uk', 'zh'];
|
||||
export const DEFAULT_LOCALE = SUPPORTED_LOCALES[0];
|
||||
|
||||
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './strings';
|
||||
export * from './translator';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { merge } from 'lodash-es';
|
||||
import type en from '../../lang/en.json';
|
||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '../blitzkit';
|
||||
import { TranslationTree } from './translator';
|
||||
|
||||
const DEBUG_MISSING_I18N = import.meta.env.DEBUG_MISSING_I18N;
|
||||
const files = import.meta.glob('../../lang/*.json', { eager: true });
|
||||
export const localizedStrings: Record<string, typeof en> = {};
|
||||
|
||||
function nuke(object: TranslationTree) {
|
||||
for (const key in object) {
|
||||
const typedKey = key as keyof typeof object;
|
||||
|
||||
if (typeof object[typedKey] === 'string') {
|
||||
const value = object[typedKey];
|
||||
object[typedKey] = redact(value as string);
|
||||
} else nuke(object[typedKey] as TranslationTree);
|
||||
}
|
||||
}
|
||||
|
||||
SUPPORTED_LOCALES.forEach((locale) => {
|
||||
const defaultStrings = files[`../../lang/${DEFAULT_LOCALE}.json`];
|
||||
const strings = files[`../../lang/${locale}.json`];
|
||||
const mergedStrings = merge({}, defaultStrings, strings);
|
||||
|
||||
if (DEBUG_MISSING_I18N === 'true') nuke(mergedStrings);
|
||||
|
||||
localizedStrings[locale] = mergedStrings as typeof en;
|
||||
});
|
||||
|
||||
function redact(string: string) {
|
||||
const matches = [...string.matchAll(/%s\d+/g)];
|
||||
const characters = string
|
||||
.split('')
|
||||
.map((char) => (char === ' ' ? ' ' : '█')) as string[];
|
||||
|
||||
matches.forEach((match) => {
|
||||
const start = match.index;
|
||||
const matchText = match[0];
|
||||
for (let i = 0; i < matchText.length; i++) {
|
||||
characters[start + i] = matchText[i];
|
||||
}
|
||||
});
|
||||
|
||||
return characters.join('');
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import en from '@blitzkit/core/lang/en.json';
|
||||
import ru from '@blitzkit/core/lang/ru.json';
|
||||
export type TranslationTree = { [key: string]: TranslationNode };
|
||||
|
||||
type TranslationTree = { [key: string]: Translation };
|
||||
|
||||
type Translation = string | TranslationTree;
|
||||
export type TranslationNode = string | TranslationTree;
|
||||
|
||||
type NestedKeys<Type> = Type extends object
|
||||
? {
|
||||
@@ -17,7 +14,7 @@ type NestedKeys<Type> = Type extends object
|
||||
}[keyof Type]
|
||||
: never;
|
||||
|
||||
class Prate<
|
||||
export class Translator<
|
||||
Locale extends string,
|
||||
DefaultLocale extends Locale,
|
||||
Strings extends TranslationTree,
|
||||
@@ -26,8 +23,8 @@ class Prate<
|
||||
private readonly defaultStrings: Map<string, string>;
|
||||
|
||||
constructor(
|
||||
public strings: Record<Locale, Strings>,
|
||||
public defaultLocale: DefaultLocale,
|
||||
private strings: Record<Locale, Strings>,
|
||||
private defaultLocale: DefaultLocale,
|
||||
) {
|
||||
this.defaultStrings = this.flatten(strings[defaultLocale]);
|
||||
}
|
||||
@@ -35,7 +32,7 @@ class Prate<
|
||||
private flatten(strings: TranslationTree) {
|
||||
const translations = new Map<string, string>();
|
||||
|
||||
function traverse(parent: string, tree: Record<string, Translation>) {
|
||||
function traverse(parent: string, tree: Record<string, TranslationNode>) {
|
||||
for (const key in tree) {
|
||||
const path = `${parent}${key}`;
|
||||
|
||||
@@ -91,5 +88,3 @@ class Prate<
|
||||
return { translate, t: translate };
|
||||
}
|
||||
}
|
||||
|
||||
const prate = new Prate({ en, ru }, 'en');
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './blitz';
|
||||
export * from './blitzkit';
|
||||
export * from './databases';
|
||||
export * from './i18n';
|
||||
export * from './imgur';
|
||||
export * from './math';
|
||||
export * from './patreon';
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -2,10 +2,12 @@
|
||||
import { assertSecret, fetchGameDefinitions } from '@blitzkit/core';
|
||||
import { Code, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||
import packageJson from '../../../../package.json';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
import { LinkI18n } from './LinkI18n';
|
||||
|
||||
const gameDefinitions = await fetchGameDefinitions();
|
||||
const { locale } = Astro.params;
|
||||
const strings = getStrings(locale);
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -15,7 +17,6 @@ const { locale } = Astro.params;
|
||||
</style>
|
||||
|
||||
<Flex className="footer" gap="6" p="6" direction="column" align="center">
|
||||
{locale}
|
||||
<Flex
|
||||
align="center"
|
||||
justify="between"
|
||||
@@ -34,9 +35,9 @@ const { locale } = Astro.params;
|
||||
}}
|
||||
>
|
||||
<Heading>BlitzKit</Heading>
|
||||
<Text color="gray">Everything World of Tanks Blitz</Text>
|
||||
<Text color="gray">{strings.website.footer.motto}</Text>
|
||||
<Text size="1" color="gray">
|
||||
Unaffiliated with Wargaming or any service
|
||||
{strings.website.footer.copyright}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
@@ -63,7 +64,7 @@ const { locale } = Astro.params;
|
||||
sm: 'auto',
|
||||
}}
|
||||
>
|
||||
<Heading size="3">About</Heading>
|
||||
<Heading size="3">{strings.website.footer.about.title}</Heading>
|
||||
<Flex
|
||||
direction="column"
|
||||
align={{
|
||||
@@ -71,25 +72,26 @@ const { locale } = Astro.params;
|
||||
sm: 'start',
|
||||
}}
|
||||
>
|
||||
<LinkI18n {locale} href="/docs/about/credits" color="gray"
|
||||
>Credits</LinkI18n
|
||||
>
|
||||
<LinkI18n {locale} href="/docs/about/credits" color="gray">
|
||||
{strings.website.footer.about.credits}
|
||||
</LinkI18n>
|
||||
<Link
|
||||
href="https://discord.gg/nDt7AjGJQH"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
>
|
||||
Discord
|
||||
{strings.website.footer.about.discord}
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/tresabhi/blitzkit"
|
||||
target="_blank"
|
||||
color="gray"
|
||||
>
|
||||
GitHub
|
||||
{strings.website.footer.about.github}
|
||||
</Link>
|
||||
<LinkI18n {locale} href="/docs/patches" color="gray">Patches</LinkI18n
|
||||
>
|
||||
<LinkI18n {locale} href="/docs/patches" color="gray">
|
||||
{strings.website.footer.about.patches}
|
||||
</LinkI18n>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -105,13 +107,13 @@ const { locale } = Astro.params;
|
||||
sm: 'auto',
|
||||
}}
|
||||
>
|
||||
<Heading size="3">Legal</Heading>
|
||||
<Heading size="3">{strings.website.footer.legal.title}</Heading>
|
||||
<Flex direction="column">
|
||||
<LinkI18n {locale} href="/docs/legal/privacy-policy" color="gray">
|
||||
Privacy policy
|
||||
{strings.website.footer.legal.privacy}
|
||||
</LinkI18n>
|
||||
<LinkI18n {locale} href="/docs/legal/terms-of-service" color="gray">
|
||||
Terms of service
|
||||
{strings.website.footer.legal.terms}
|
||||
</LinkI18n>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
---
|
||||
import { Code, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { resolveBranchName } from '../core/blitzkit/branches';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
import { literals } from '../core/i18n/literals';
|
||||
|
||||
const branchName = resolveBranchName();
|
||||
const { locale } = Astro.params;
|
||||
const branchName = resolveBranchName(locale);
|
||||
const isBranchNamed = branchName !== undefined;
|
||||
const strings = getStrings(locale);
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -141,7 +146,9 @@ const isBranchNamed = branchName !== undefined;
|
||||
</Flex>
|
||||
</Heading>
|
||||
|
||||
<Text color="gray" align="center" size="4">By TrèsAbhi ❤️</Text>
|
||||
<Text color="gray" align="center" size="4"
|
||||
>{literals(strings.website.home.hero.credit, ['TrèsAbhi'])}</Text
|
||||
>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
@@ -3,7 +3,8 @@ import { EyeOpenIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { google } from 'googleapis';
|
||||
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions';
|
||||
import { LocaleProvider } from '../hooks/useLocale';
|
||||
import { literals } from '../core/i18n/literals';
|
||||
import { LocaleProvider, useLocale } from '../hooks/useLocale';
|
||||
import { TankopediaPersistent } from '../stores/tankopediaPersistent';
|
||||
import { TankCard } from './TankCard';
|
||||
|
||||
@@ -79,13 +80,15 @@ export function HomePageHotTanks({ locale }: HomePageHotTanksProps) {
|
||||
}
|
||||
|
||||
function Content() {
|
||||
const { locale, strings } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex direction="column" pt="4" pb="8">
|
||||
<Heading align="center" size="5">
|
||||
Popular tanks
|
||||
{strings.website.home.hot_tanks.title}
|
||||
</Heading>
|
||||
<Text color="gray" align="center" mb="5" size="2">
|
||||
Last 7 days
|
||||
{literals(strings.website.home.hot_tanks.subtitle, ['7'])}
|
||||
</Text>
|
||||
|
||||
<Flex justify="center" gap="4" wrap="wrap">
|
||||
@@ -96,7 +99,7 @@ function Content() {
|
||||
discriminator={
|
||||
<Flex align="center" gap="1" justify="center">
|
||||
<EyeOpenIcon />
|
||||
{Math.round(row.views * (30 / 7)).toLocaleString('en-US')}
|
||||
{Math.round(row.views * (30 / 7)).toLocaleString(locale)}
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -19,14 +19,15 @@ import {
|
||||
} from '@radix-ui/themes';
|
||||
import { resolveBranchName } from '../core/blitzkit/branches';
|
||||
import { homeTool, TOOLS } from '../constants/tools';
|
||||
import { NavbarAccount } from './NavbarAccount';
|
||||
import { LinkI18n } from './LinkI18n';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
|
||||
const toolsFiltered = TOOLS.filter((tool) => !('href' in tool));
|
||||
const tools = [homeTool, ...toolsFiltered];
|
||||
const branchName = resolveBranchName();
|
||||
const isBranchNamed = branchName !== undefined;
|
||||
const { locale } = Astro.params;
|
||||
const branchName = resolveBranchName(locale);
|
||||
const isBranchNamed = branchName !== undefined;
|
||||
const strings = getStrings(locale);
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -139,14 +140,17 @@ const { locale } = Astro.params;
|
||||
(branch) =>
|
||||
branch !== assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
|
||||
);
|
||||
const selected = Astro.url.pathname.startsWith(
|
||||
`/tools/${tool.id}`,
|
||||
);
|
||||
const selected = Astro.url.pathname.includes(`/tools/${tool.id}`);
|
||||
const toolStrings =
|
||||
strings.website.tools[
|
||||
(tool.stringsId ??
|
||||
tool.id) as keyof typeof strings.website.tools
|
||||
];
|
||||
|
||||
if (unavailableOnBranch) return null;
|
||||
|
||||
return (
|
||||
<Fragment key={tool.title}>
|
||||
<Fragment key={tool.id}>
|
||||
{index > 0 && <Separator orientation="vertical" size="1" />}
|
||||
<LinkI18n
|
||||
{locale}
|
||||
@@ -156,7 +160,7 @@ const { locale } = Astro.params;
|
||||
href={`/tools/${tool.id}`}
|
||||
underline={selected ? 'always' : 'hover'}
|
||||
>
|
||||
{tool.title}
|
||||
{toolStrings.name}
|
||||
</LinkI18n>
|
||||
</Fragment>
|
||||
);
|
||||
@@ -212,6 +216,11 @@ const { locale } = Astro.params;
|
||||
(branch) =>
|
||||
branch !== assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
|
||||
);
|
||||
const toolStrings =
|
||||
strings.website.tools[
|
||||
(tool.stringsId ??
|
||||
tool.id) as keyof typeof strings.website.tools
|
||||
];
|
||||
|
||||
if (unavailableOnBranch) return null;
|
||||
|
||||
@@ -265,7 +274,7 @@ const { locale } = Astro.params;
|
||||
WebkitBackdropFilter: 'blur(4rem)',
|
||||
}}
|
||||
>
|
||||
<Text>{tool.title}</Text>
|
||||
<Text>{toolStrings.name}</Text>
|
||||
</Flex>
|
||||
</LinkI18n>
|
||||
</Flex>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { assertSecret, imgur, ImgurSize } from '@blitzkit/core';
|
||||
import { CaretRightIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Button, Code, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import type { Tool } from '../constants/tools';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
import { LinkI18n } from './LinkI18n';
|
||||
|
||||
interface Props {
|
||||
@@ -14,6 +15,11 @@ const disabled = tool.branches?.every(
|
||||
(branch) => branch !== assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
|
||||
);
|
||||
const { locale } = Astro.params;
|
||||
const strings = getStrings(locale);
|
||||
const toolStrings =
|
||||
strings.website.tools[
|
||||
(tool.stringsId??tool.id ) as keyof typeof strings.website.tools
|
||||
];
|
||||
---
|
||||
|
||||
<style>
|
||||
@@ -101,7 +107,7 @@ const { locale } = Astro.params;
|
||||
size={{ initial: '6', sm: tool.significant ? '7' : '5' }}
|
||||
weight="medium"
|
||||
>
|
||||
{tool.title}
|
||||
{toolStrings.name}
|
||||
</Heading>
|
||||
<Text
|
||||
align={{ initial: 'center', sm: 'left' }}
|
||||
@@ -115,7 +121,7 @@ const { locale } = Astro.params;
|
||||
<Code>{assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH)}</Code>
|
||||
</>
|
||||
) : (
|
||||
tool.description
|
||||
toolStrings.description
|
||||
)
|
||||
}
|
||||
</Text>
|
||||
@@ -132,7 +138,7 @@ const { locale } = Astro.params;
|
||||
color={tool.button.color}
|
||||
style={{ cursor: 'inherit' }}
|
||||
>
|
||||
{tool.button.text}
|
||||
{toolStrings.button}
|
||||
<CaretRightIcon />
|
||||
</Button>
|
||||
)
|
||||
|
||||
@@ -3,15 +3,12 @@ import type { ComponentProps } from 'react';
|
||||
|
||||
export interface Tool {
|
||||
id: string;
|
||||
stringsId?: string;
|
||||
image: string;
|
||||
title: string;
|
||||
description: string;
|
||||
disabled?: boolean;
|
||||
href?: string;
|
||||
pageDescription?: string;
|
||||
button: {
|
||||
highContrast?: boolean;
|
||||
text: string;
|
||||
color: ComponentProps<typeof Button>['color'];
|
||||
};
|
||||
significant?: boolean;
|
||||
@@ -20,13 +17,8 @@ export interface Tool {
|
||||
|
||||
export const tankopediaTool: Tool = {
|
||||
id: 'tankopedia',
|
||||
title: 'Tankopedia',
|
||||
description: 'Blitz tank encyclopedia',
|
||||
pageDescription:
|
||||
'Statistics, armor, and more for all tanks in World of Tanks Blitz',
|
||||
image: 'D8j6GZx',
|
||||
button: {
|
||||
text: 'Find tanks',
|
||||
color: 'purple',
|
||||
},
|
||||
significant: true,
|
||||
@@ -35,13 +27,8 @@ export const tankopediaTool: Tool = {
|
||||
|
||||
// export const ratingTool: Tool = {
|
||||
// id: 'rating',
|
||||
// title: 'Rating',
|
||||
// description: 'Full rating leaderboard',
|
||||
// pageDescription:
|
||||
// 'Live and archived full rating leaderboard for World of Tanks Blitz',
|
||||
// image: 'WEyMZH3',
|
||||
// button: {
|
||||
// text: 'View leaderboard',
|
||||
// color: 'orange',
|
||||
// },
|
||||
// branches: ['main', 'dev'],
|
||||
@@ -49,13 +36,8 @@ export const tankopediaTool: Tool = {
|
||||
|
||||
export const compareTool: Tool = {
|
||||
id: 'compare',
|
||||
title: 'Compare',
|
||||
description: 'Compare tank statistics',
|
||||
pageDescription:
|
||||
'Compare tanks statistics and loadouts in World of Tanks Blitz',
|
||||
image: 'O6VNl6e',
|
||||
button: {
|
||||
text: 'Compare tanks',
|
||||
color: 'crimson',
|
||||
},
|
||||
branches: ['main', 'dev', 'opentest', 'preview'],
|
||||
@@ -64,13 +46,8 @@ export const compareTool: Tool = {
|
||||
export const sessionTool: Tool = {
|
||||
// TODO: rename to tracker
|
||||
id: 'session',
|
||||
title: 'Session',
|
||||
description: 'Live session stats',
|
||||
pageDescription:
|
||||
'Track your stats in real time as you play World of Tanks Blitz',
|
||||
image: 'HdG9sTf',
|
||||
button: {
|
||||
text: 'Start tracking',
|
||||
color: 'blue',
|
||||
},
|
||||
branches: ['main', 'dev'],
|
||||
@@ -78,35 +55,25 @@ export const sessionTool: Tool = {
|
||||
|
||||
export const discordTool: Tool = {
|
||||
id: 'discord',
|
||||
title: 'Discord bot',
|
||||
description: 'Stats right in Discord',
|
||||
href: 'https://discord.com/application-directory/1097673957865443370',
|
||||
image: '0bWE5hC',
|
||||
button: {
|
||||
text: 'Install now',
|
||||
color: 'indigo',
|
||||
},
|
||||
};
|
||||
|
||||
export const moreTool: Tool = {
|
||||
id: 'more',
|
||||
title: 'More coming soon',
|
||||
description: "What we're making",
|
||||
href: 'https://discord.gg/nDt7AjGJQH',
|
||||
image: '1nPm6VI',
|
||||
button: {
|
||||
text: 'Join Discord',
|
||||
color: 'plum',
|
||||
},
|
||||
};
|
||||
|
||||
export const playerStatsTool: Tool = {
|
||||
id: 'player-stats',
|
||||
title: 'Player stats',
|
||||
description: 'Periodical player statistics',
|
||||
pageDescription: 'Periodical player statistics in World of Tanks Blitz',
|
||||
export const playersTool: Tool = {
|
||||
id: 'players',
|
||||
button: {
|
||||
text: 'Lookup player',
|
||||
color: 'blue',
|
||||
},
|
||||
image: 'YelSOfT',
|
||||
@@ -116,11 +83,7 @@ export const playerStatsTool: Tool = {
|
||||
|
||||
export const performanceTool: Tool = {
|
||||
id: 'performance',
|
||||
title: 'Performance',
|
||||
description: 'Tank performance statistics',
|
||||
pageDescription: 'Best tanks and their performances in World of Tanks Blitz',
|
||||
button: {
|
||||
text: 'View tanks',
|
||||
color: 'jade',
|
||||
},
|
||||
image: 'vOKFB03',
|
||||
@@ -129,12 +92,7 @@ export const performanceTool: Tool = {
|
||||
|
||||
export const embedTool: Tool = {
|
||||
id: 'embed',
|
||||
title: 'Embed',
|
||||
description: 'Embeds for streaming',
|
||||
pageDescription:
|
||||
'Create embeds for streaming World of Tanks Blitz with extreme customization',
|
||||
button: {
|
||||
text: 'Create now',
|
||||
color: 'red',
|
||||
},
|
||||
image: 'Q0faRYg',
|
||||
@@ -142,20 +100,14 @@ export const embedTool: Tool = {
|
||||
};
|
||||
|
||||
export const homeTool: Tool = {
|
||||
id: '',
|
||||
title: 'Home',
|
||||
button: { color: 'blue', text: '' },
|
||||
description: '',
|
||||
id: 'home',
|
||||
button: { color: 'blue' },
|
||||
image: 'rUPie9G',
|
||||
};
|
||||
|
||||
export const galleryTool: Tool = {
|
||||
id: 'gallery',
|
||||
title: 'Gallery',
|
||||
description: 'Blitz player avatars',
|
||||
pageDescription: 'All World of Tanks Blitz player avatars',
|
||||
button: {
|
||||
text: 'Browse all',
|
||||
color: 'gold',
|
||||
},
|
||||
image: 'VhKlknk',
|
||||
@@ -164,12 +116,8 @@ export const galleryTool: Tool = {
|
||||
|
||||
export const tierListTool: Tool = {
|
||||
id: 'tier-list',
|
||||
title: 'Tier list',
|
||||
description: 'Rank Blitz tanks',
|
||||
pageDescription:
|
||||
'Explore tier lists ranking tanks from best to worst, based on player ratings and statistical performance in World of Tanks Blitz',
|
||||
stringsId: 'tier_list',
|
||||
button: {
|
||||
text: 'Rank tanks',
|
||||
color: 'orange',
|
||||
},
|
||||
image: 'oKKc8Xv',
|
||||
@@ -178,12 +126,7 @@ export const tierListTool: Tool = {
|
||||
|
||||
export const chartsTool: Tool = {
|
||||
id: 'charts',
|
||||
title: 'Charts',
|
||||
description: 'Lots of graphs',
|
||||
pageDescription:
|
||||
'Charts and graphs on anything and everything about tanks, players, and events in World of Tanks Blitz',
|
||||
button: {
|
||||
text: 'Nerd out',
|
||||
color: 'bronze',
|
||||
},
|
||||
image: '6fhkljA',
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import { assertSecret, DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
||||
|
||||
const BRANCH_NAMES: Record<string, string> = {
|
||||
dev: 'beta',
|
||||
opentest: 'opentest',
|
||||
preview: 'preview',
|
||||
};
|
||||
|
||||
export function resolveBranchName() {
|
||||
export function resolveBranchName(locale = DEFAULT_LOCALE) {
|
||||
const strings = localizedStrings[locale];
|
||||
const secret = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
|
||||
|
||||
if (
|
||||
assertSecret(import.meta.env.MODE) === 'development' &&
|
||||
secret === 'dev'
|
||||
) {
|
||||
return 'dev';
|
||||
return strings.common.branches.local;
|
||||
} else {
|
||||
return BRANCH_NAMES[secret] as string | undefined;
|
||||
return (strings.common.branches as Record<string, string>)[secret] as
|
||||
| string
|
||||
| undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
||||
|
||||
export function getStrings(locale: string = DEFAULT_LOCALE) {
|
||||
return localizedStrings[locale];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export function literals(string: string, literals: string[]) {
|
||||
return literals.reduce((accumulator, literal, index) => {
|
||||
return accumulator.replace(`%s${index + 1}`, literal);
|
||||
}, string);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { createContext, useContext, type ReactNode } from 'react';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
|
||||
const LocaleContext = createContext<{ locale: string | undefined } | null>(
|
||||
null,
|
||||
);
|
||||
const LocaleContext = createContext<{
|
||||
locale: string;
|
||||
localeRaw: string | undefined;
|
||||
} | null>(null);
|
||||
|
||||
interface LocaleProviderProps {
|
||||
locale: string | undefined;
|
||||
@@ -11,7 +14,9 @@ interface LocaleProviderProps {
|
||||
|
||||
export function LocaleProvider({ locale, children }: LocaleProviderProps) {
|
||||
return (
|
||||
<LocaleContext.Provider value={{ locale }}>
|
||||
<LocaleContext.Provider
|
||||
value={{ locale: locale ?? DEFAULT_LOCALE, localeRaw: locale }}
|
||||
>
|
||||
{children}
|
||||
</LocaleContext.Provider>
|
||||
);
|
||||
@@ -24,5 +29,7 @@ export function useLocale() {
|
||||
throw new Error('useLocale must be used within a LocaleProvider');
|
||||
}
|
||||
|
||||
return { locale: context.locale };
|
||||
const strings = getStrings(context.locale);
|
||||
|
||||
return { locale: context.locale, strings };
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
import {
|
||||
assertSecret,
|
||||
DEFAULT_LOCALE,
|
||||
localizedStrings,
|
||||
SUPPORTED_LOCALES,
|
||||
} from '@blitzkit/core';
|
||||
import strings from '@blitzkit/core/lang/en.json';
|
||||
import { Box, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
import { AesonPlug } from '../../components/AesonPlug';
|
||||
@@ -23,10 +23,12 @@ import {
|
||||
tankopediaTool,
|
||||
tierListTool,
|
||||
} from '../../constants/tools';
|
||||
import { getStrings } from '../../core/i18n/getStrings';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
|
||||
const branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
|
||||
const { locale } = Astro.params;
|
||||
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
|
||||
const strings = getStrings(Astro.params.locale);
|
||||
|
||||
export const getStaticPaths = (() => {
|
||||
return [
|
||||
@@ -45,6 +47,7 @@ export const getStaticPaths = (() => {
|
||||
const selected =
|
||||
thisLocale === locale ||
|
||||
(locale === undefined && thisLocale === DEFAULT_LOCALE);
|
||||
const localeNames = localizedStrings[thisLocale].common.locales;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -55,7 +58,7 @@ export const getStaticPaths = (() => {
|
||||
highContrast={selected}
|
||||
href={`/${thisLocale === DEFAULT_LOCALE ? '' : thisLocale}`}
|
||||
>
|
||||
{strings.common.locales[thisLocale]}
|
||||
{localeNames[thisLocale as keyof typeof localeNames]}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
@@ -105,33 +108,24 @@ export const getStaticPaths = (() => {
|
||||
<Box flexGrow="1" />
|
||||
</Flex>
|
||||
|
||||
<Heading align="center" mt="6">Other cool projects</Heading>
|
||||
<Heading align="center" mt="6"
|
||||
>{strings.website.home.other_projects.title}</Heading
|
||||
>
|
||||
|
||||
<Flex gap="4" mb="6" direction={{ initial: 'column', sm: 'row' }}>
|
||||
<ToolCard
|
||||
tool={{
|
||||
button: {
|
||||
text: 'Analyze',
|
||||
color: 'blue',
|
||||
},
|
||||
description: 'Rich historic average stats',
|
||||
id: 'blitz-analysis',
|
||||
button: { color: 'blue' },
|
||||
id: 'blitz_analysis',
|
||||
image: 'j76YXGl',
|
||||
title: 'BlitzAnalysis[]',
|
||||
href: 'https://blitzanalysiz.com/',
|
||||
}}
|
||||
/>
|
||||
<ToolCard
|
||||
tool={{
|
||||
button: {
|
||||
color: 'purple',
|
||||
text: 'Install',
|
||||
highContrast: true,
|
||||
},
|
||||
button: { color: 'purple', highContrast: true },
|
||||
image: 'zYrrYbR',
|
||||
description: 'In-game session stats',
|
||||
id: 'blitz-insider',
|
||||
title: 'Blitz Insider',
|
||||
id: 'blitz_insider',
|
||||
href: 'https://discord.gg/UjWf9eGgtR',
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user