diff --git a/packages/core/lang/en.json b/packages/core/lang/en.json index c2b47bd14..9c5d0994f 100644 --- a/packages/core/lang/en.json +++ b/packages/core/lang/en.json @@ -6,6 +6,16 @@ }, "common": { + "player_count_period": { + "total": "Total", + "d_120": "Past 120 days", + "d_90": "Past 90 days", + "d_60": "Past 60 days", + "d_30": "Past month", + "d_7": "Past week", + "d_1": "Yesterday" + }, + "branches": { "local": "dev", "dev": "beta", @@ -738,6 +748,30 @@ "body": "Estimated stats based on %s1 players. Updated %s2 ago.", "hours": "%s1 hours", "minutes": "%s1 minutes" + }, + "player_count": { + "label": "Player count" + }, + "table": { + "tanks": { + "header": "Tank", + "total": "Total" + }, + "stats": { + "winrate": "Winrate", + "players": "Players", + "damage": "Damage", + "survival": "Survival", + "xp": "XP", + "kills": "Kills", + "spots": "Spots", + "accuracy": "Accuracy", + "shots": "Shots", + "hits": "Hits", + "damage_ratio": "Dmg. ratio", + "damage_taken": "Dmg. taken", + "capture_points": "Cap points" + } } }, "gallery": { diff --git a/packages/core/src/i18n/strings.ts b/packages/core/src/i18n/strings.ts index cddaf653e..17e7d0b46 100644 --- a/packages/core/src/i18n/strings.ts +++ b/packages/core/src/i18n/strings.ts @@ -1,11 +1,8 @@ import { merge } from 'lodash-es'; import type en from '../../lang/en.json'; -import { assertSecret, DEFAULT_LOCALE, SUPPORTED_LOCALES } from '../blitzkit'; +import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '../blitzkit'; import { TranslationTree } from './translator'; -const PUBLIC_DEBUG_MISSING_I18N = assertSecret( - import.meta.env.PUBLIC_DEBUG_MISSING_I18N, -); const files = import.meta.glob('../../lang/*.json', { eager: true }); export const localizedStrings: Record = {}; @@ -25,7 +22,11 @@ SUPPORTED_LOCALES.forEach((locale) => { const strings = files[`../../lang/${locale}.json`]; const mergedStrings = merge({}, defaultStrings, strings); - if (PUBLIC_DEBUG_MISSING_I18N === 'true') nuke(mergedStrings); + /** + * For some reason, this won't load on the server on the first render so I am + * not asserting this one lol + */ + if (import.meta.env.PUBLIC_DEBUG_MISSING_I18N === 'true') nuke(mergedStrings); localizedStrings[locale] = mergedStrings as typeof en; }); diff --git a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx index ad5214ffa..0a384daea 100644 --- a/packages/website/src/components/Armor/components/ShotDisplayCard.tsx +++ b/packages/website/src/components/Armor/components/ShotDisplayCard.tsx @@ -8,6 +8,7 @@ import { } from '@radix-ui/themes'; import type { ComponentProps } from 'react'; import { radToDeg } from 'three/src/math/MathUtils.js'; +import { useLocale } from '../../../hooks/useLocale'; import type { Shot, ShotLayer, @@ -49,6 +50,8 @@ function LayerEntry({ layer: ShotLayer; angle?: number; }) { + const { locale } = useLocale(); + return ( @@ -61,7 +64,7 @@ function LayerEntry({ {layer.type === null && ( <> - {Math.round(layer.distance * 1000).toLocaleString()} + {Math.round(layer.distance * 1000).toLocaleString(locale)} mm @@ -73,7 +76,7 @@ function LayerEntry({ {layer.type === ArmorType.External && ( - {Math.round(layer.thickness).toLocaleString()} + {Math.round(layer.thickness).toLocaleString(locale)} mm )} @@ -82,14 +85,14 @@ function LayerEntry({ layer.type === ArmorType.Spaced) && ( <> - {Math.round(layer.thicknessAngled).toLocaleString()} + {Math.round(layer.thicknessAngled).toLocaleString(locale)} {`mm${ angle === undefined ? '' : ` ${Math.round(radToDeg(angle))}°` }`} - {Math.round(layer.thickness).toLocaleString()} + {Math.round(layer.thickness).toLocaleString(locale)} mm nominal @@ -103,6 +106,8 @@ interface ShotDisplayCardProps extends CardProps { } export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { + const { locale } = useLocale(); + if (!shot) return null; const outTitleColor = shot.out @@ -117,7 +122,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { {shot.in.status !== 'blocked' && shot.in.status !== 'ricochet' && ( <> - {Math.round(shot.damage).toLocaleString()} + {Math.round(shot.damage).toLocaleString(locale)} hp )} @@ -182,7 +187,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) { {shot.out.status !== 'blocked' && shot.out.status !== 'ricochet' && ( <> - {Math.round(shot.damage).toLocaleString()} + {Math.round(shot.damage).toLocaleString(locale)} hp )} diff --git a/packages/website/src/components/DatePicker/index.tsx b/packages/website/src/components/DatePicker/index.tsx index 767e332ca..143acb86a 100644 --- a/packages/website/src/components/DatePicker/index.tsx +++ b/packages/website/src/components/DatePicker/index.tsx @@ -16,6 +16,7 @@ import { } from '@radix-ui/themes'; import { times } from 'lodash-es'; import { useState } from 'react'; +import { useLocale } from '../../hooks/useLocale'; import './index.css'; const DAY_TITLES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; @@ -37,6 +38,7 @@ export function DatePicker({ const [yearOffset, setYearOffset] = useState(0); const [monthOffset, setMonthOffset] = useState(0); const date = new Date(year + yearOffset, month + monthOffset, day); + const { locale } = useLocale(); const daysInLastMonth = new Date( year + yearOffset, @@ -94,11 +96,7 @@ export function DatePicker({ - - {date.toLocaleString(undefined, { - month: 'long', - })} - + {date.toLocaleString(locale, { month: 'long' })} setYearScroll(0)}> diff --git a/packages/website/src/components/Gallery/List.tsx b/packages/website/src/components/Gallery/List.tsx index c2d173297..961f1830a 100644 --- a/packages/website/src/components/Gallery/List.tsx +++ b/packages/website/src/components/Gallery/List.tsx @@ -2,6 +2,7 @@ import { Flex, Text } from '@radix-ui/themes'; import fuzzysort from 'fuzzysort'; import { times } from 'lodash-es'; import { useMemo, useState } from 'react'; +import { useLocale } from '../../hooks/useLocale'; import { GalleryEphemeral } from '../../stores/galleryEphemeral'; import { GalleryCard } from './Card'; @@ -20,6 +21,7 @@ const PREVIEW_COUNT = 28; export function GalleryList({ avatars }: GalleryListProps) { const search = GalleryEphemeral.use((state) => state.search); const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED); + const { locale } = useLocale(); const filtered = useMemo(() => { setLoadedCards(DEFAULT_LOADED); @@ -35,7 +37,7 @@ export function GalleryList({ avatars }: GalleryListProps) { return ( <> - {filtered.length.toLocaleString()} avatars + {filtered.length.toLocaleString(locale)} avatars diff --git a/packages/website/src/components/Performance/Header.tsx b/packages/website/src/components/Performance/Header.tsx index f0c173f57..fbbfb5b39 100644 --- a/packages/website/src/components/Performance/Header.tsx +++ b/packages/website/src/components/Performance/Header.tsx @@ -4,21 +4,23 @@ import { CaretUpIcon, } from '@radix-ui/react-icons'; import { Button, Flex, Table } from '@radix-ui/themes'; +import { useLocale } from '../../hooks/useLocale'; import { TankPerformanceSort } from '../../stores/tankPerformanceSort'; -import { - TankPerformanceSortTypeNames, - TankPerformanceSortTypeNamesArray, -} from '../../stores/tankPerformanceSort/constants'; +import { TankPerformanceSortTypeNamesArray } from '../../stores/tankPerformanceSort/constants'; import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell'; export function Header() { const sort = TankPerformanceSort.use(); const store = TankPerformanceSort.useStore(); + const { strings } = useLocale(); return ( - Tank + + {strings.website.tools.performance.table.tanks.header} + + {TankPerformanceSortTypeNamesArray.map((type) => { const isSelected = sort.type === type; @@ -42,7 +44,7 @@ export function Header() { } }} > - {TankPerformanceSortTypeNames[type]} + {strings.website.tools.performance.table.stats[type]} {!isSelected && } {isSelected && ( diff --git a/packages/website/src/components/Performance/PlayerCountControl.tsx b/packages/website/src/components/Performance/PlayerCountControl.tsx index 18c13e01e..6b7b45792 100644 --- a/packages/website/src/components/Performance/PlayerCountControl.tsx +++ b/packages/website/src/components/Performance/PlayerCountControl.tsx @@ -1,4 +1,5 @@ import { Flex, Select, Text } from '@radix-ui/themes'; +import { useLocale } from '../../hooks/useLocale'; import { TankPerformanceEphemeral, type PlayerCountPeriod, @@ -9,10 +10,11 @@ export function PlayerCountControl() { (state) => state.playerCountPeriod, ); const mutateTankPerformanceSort = TankPerformanceEphemeral.useMutation(); + const { strings } = useLocale(); return ( - Player count + {strings.website.tools.performance.player_count.label} { @@ -23,13 +25,27 @@ export function PlayerCountControl() { > - Total - Past 120 days - Past 90 days - Past 60 days - This month - This week - Yesterday + + {strings.common.player_count_period.total} + + + {strings.common.player_count_period.d_120} + + + {strings.common.player_count_period.d_90} + + + {strings.common.player_count_period.d_60} + + + {strings.common.player_count_period.d_30} + + + {strings.common.player_count_period.d_7} + + + {strings.common.player_count_period.d_1} + diff --git a/packages/website/src/components/Performance/TankRow.tsx b/packages/website/src/components/Performance/TankRow.tsx index e8c339751..9c4b9ef4a 100644 --- a/packages/website/src/components/Performance/TankRow.tsx +++ b/packages/website/src/components/Performance/TankRow.tsx @@ -3,6 +3,7 @@ import { Table } from '@radix-ui/themes'; import { memo } from 'react'; import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; +import { useLocale } from '../../hooks/useLocale'; import { TankPerformanceEphemeral } from '../../stores/tankPerformanceEphemeral'; import { TankRowHeaderCell } from '../TankRowHeaderCell'; @@ -19,6 +20,7 @@ export const TankRow = memo( const playerCountPeriod = TankPerformanceEphemeral.use( (state) => state.playerCountPeriod, ); + const { locale } = useLocale(); return ( @@ -29,13 +31,14 @@ export const TankRow = memo( {formatCompact( + locale, Math.round(ratio * averages.samples[playerCountPeriod]), )} {Math.round( averages.mu.damage_dealt / averages.mu.battles, - ).toLocaleString()} + ).toLocaleString(locale)} {Math.round( @@ -44,7 +47,9 @@ export const TankRow = memo( % - {Math.round(averages.mu.xp / averages.mu.battles).toLocaleString()} + {Math.round(averages.mu.xp / averages.mu.battles).toLocaleString( + locale, + )} {(averages.mu.frags / averages.mu.battles).toFixed(2)} @@ -67,7 +72,7 @@ export const TankRow = memo( {Math.round( averages.mu.damage_received / averages.mu.battles, - ).toLocaleString()} + ).toLocaleString(locale)} {(averages.mu.capture_points / averages.mu.battles).toFixed(2)} diff --git a/packages/website/src/components/Performance/Total.tsx b/packages/website/src/components/Performance/Total.tsx index c75ae5c5d..5002ee56b 100644 --- a/packages/website/src/components/Performance/Total.tsx +++ b/packages/website/src/components/Performance/Total.tsx @@ -7,6 +7,7 @@ import { Table } from '@radix-ui/themes'; import { memo, useCallback } from 'react'; import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; +import { useLocale } from '../../hooks/useLocale'; import { TankPerformanceEphemeral } from '../../stores/tankPerformanceEphemeral'; import { StickyRowHeaderCell } from '../StickyRowHeaderCell'; @@ -25,6 +26,7 @@ export const Total = memo( const playerCountPeriod = TankPerformanceEphemeral.use( (state) => state.playerCountPeriod, ); + const { strings, locale } = useLocale(); const ratio = useAveragesExclusionRatio(); const sum = useCallback( (slice: (tank: BlitzkitStats) => number) => { @@ -54,20 +56,23 @@ export const Total = memo( return ( - Total + {strings.website.tools.performance.table.tanks.total} {(winrate * 100).toFixed(1)}% - {formatCompact(ratio * averageDefinitions.samples[playerCountPeriod])} + {formatCompact( + locale, + ratio * averageDefinitions.samples[playerCountPeriod], + )} - {Math.round(damage).toLocaleString()} + {Math.round(damage).toLocaleString(locale)} {Math.round(survival * 100).toFixed(1)}% - {Math.round(xp).toLocaleString()} + {Math.round(xp).toLocaleString(locale)} {kills.toFixed(2)} {spots.toFixed(2)} @@ -76,7 +81,7 @@ export const Total = memo( {hits.toFixed(1)} {damageRatio.toFixed(2)} - {Math.round(damageReceived).toLocaleString()} + {Math.round(damageReceived).toLocaleString(locale)} {capturePoints.toFixed(2)} diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index cee708ec6..6a309351c 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -187,7 +187,7 @@ export function MetaSection() { } price`} > - {protagonist.price.value.toLocaleString()} + {protagonist.price.value.toLocaleString(locale)} {TankPriceType[protagonist.price.type]} - {(protagonist.price.value / 2).toLocaleString()} + {(protagonist.price.value / 2).toLocaleString(locale)} {TankPriceType[protagonist.price.type]} state.protagonist.tank); const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); @@ -105,7 +107,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) { objectPosition: 'center', }} /> - {Math.round(totalXp / xpMultiplier).toLocaleString()} + {Math.round(totalXp / xpMultiplier).toLocaleString(locale)} @@ -121,7 +123,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) { objectPosition: 'center', }} /> - {totalCredits.toLocaleString()} + {totalCredits.toLocaleString(locale)} diff --git a/packages/website/src/pages/[...locale]/tools/session/_index.tsx b/packages/website/src/pages/[...locale]/tools/session/_index.tsx index 233de62fc..3013623d8 100644 --- a/packages/website/src/pages/[...locale]/tools/session/_index.tsx +++ b/packages/website/src/pages/[...locale]/tools/session/_index.tsx @@ -39,6 +39,7 @@ import { StickyTableRoot } from '../../../../components/StickyTableRoot'; import { TankRowHeaderCell } from '../../../../components/TankRowHeaderCell'; import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; +import { useLocale } from '../../../../hooks/useLocale'; import { Session, type SessionTracking } from '../../../../stores/session'; const [tankDefinitions, averageDefinitions] = await Promise.all([ @@ -55,6 +56,7 @@ export function Page() { } function Content() { + const { locale } = useLocale(); const [showSearch, setShowSearch] = useState(false); const [searching, setSearching] = useState(false); const [searchResults, setSearchResults] = useState([]); @@ -265,7 +267,7 @@ function Content() { Tracking {accountInfo?.nickname} - Since {new Date(session.player.since).toLocaleString()} + Since {new Date(session.player.since).toLocaleString(locale)} diff --git a/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro index d7bfffdd8..a2b9168e8 100644 --- a/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro +++ b/packages/website/src/pages/[...locale]/tools/tankopedia/[id]/index.astro @@ -1,14 +1,14 @@ --- import { - asset, - createDefaultProvisions, - createDefaultSkills, - fetchEquipmentDefinitions, - fetchModelDefinitions, - fetchProvisionDefinitions, - fetchSkillDefinitions, - fetchTankDefinitions, - TIER_ROMAN_NUMERALS, +asset, +createDefaultProvisions, +createDefaultSkills, +fetchEquipmentDefinitions, +fetchModelDefinitions, +fetchProvisionDefinitions, +fetchSkillDefinitions, +fetchTankDefinitions, +TIER_ROMAN_NUMERALS, } from '@blitzkit/core'; import strings from '@blitzkit/core/lang/en-US.json'; import type { GetStaticPaths } from 'astro'; @@ -31,8 +31,8 @@ export const getStaticPaths = (async () => { .flat(); }) satisfies GetStaticPaths; -const { id, locale } = Astro.params; - +const {id} = Astro.params; +const locale = Astro.params.locale ?? DEFAULT_LOCALE; const tankDefinitions = await fetchTankDefinitions(); const skillDefinitions = await fetchSkillDefinitions(); const provisionDefinitions = await fetchProvisionDefinitions(); @@ -77,7 +77,7 @@ const description = `${ name } armor, statistics, equipment, provisions, and more. ${Math.round( characteristics.dpm, -).toLocaleString()} DPM, ${characteristics.damage.toFixed(0)}hp alpha, ${characteristics.penetration.toFixed( +).toLocaleString(locale)} DPM, ${characteristics.damage.toFixed(0)}hp alpha, ${characteristics.penetration.toFixed( 0, )}mm penetration, ${characteristics.speedForwards.toFixed( 0, diff --git a/packages/website/src/stores/tankPerformanceSort/constants.ts b/packages/website/src/stores/tankPerformanceSort/constants.ts index dd519eb98..8b245199f 100644 --- a/packages/website/src/stores/tankPerformanceSort/constants.ts +++ b/packages/website/src/stores/tankPerformanceSort/constants.ts @@ -1,21 +1,6 @@ +import en from '@blitzkit/core/lang/en.json'; import type { TankPerformanceSortType } from '.'; -export const TankPerformanceSortTypeNames = { - winrate: 'Winrate', - players: 'Players', - damage: 'Damage', - survival: 'Survival', - xp: 'XP', - kills: 'Kills', - spots: 'Spots', - accuracy: 'Accuracy', - shots: 'Shots', - hits: 'Hits', - damageRatio: 'Dmg. ratio', - damageTaken: 'Dmg. taken', - capturePoints: 'Cap points', -} as const; - export const TankPerformanceSortTypeNamesArray = Object.keys( - TankPerformanceSortTypeNames, + en.website.tools.performance.table.stats, ) as TankPerformanceSortType[]; diff --git a/packages/website/src/stores/tankPerformanceSort/index.ts b/packages/website/src/stores/tankPerformanceSort/index.ts index e6a2f8c9a..8e5f52b64 100644 --- a/packages/website/src/stores/tankPerformanceSort/index.ts +++ b/packages/website/src/stores/tankPerformanceSort/index.ts @@ -1,8 +1,9 @@ +import type en from '@blitzkit/core/lang/en.json'; import { create } from 'zustand'; import { createContextualStore } from '../../core/zustand/createContextualStore'; -import { TankPerformanceSortTypeNames } from './constants'; -export type TankPerformanceSortType = keyof typeof TankPerformanceSortTypeNames; +export type TankPerformanceSortType = + keyof typeof en.website.tools.performance.table.stats; export interface TankPerformanceSort { type: TankPerformanceSortType;