mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 03:29:51 +02:00
feat: localize game assets
This commit is contained in:
1 parent
d8459d373c
commit
ef9778ea96
90 files changed
+336
-251
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { Flex, Heading, IconButton, Table } from '@radix-ui/themes';
|
||||
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
||||
import { CompareRow } from './CompareRow';
|
||||
@@ -15,6 +16,7 @@ export function Body({ stats }: BodyProps) {
|
||||
const hasNonRegularGun = members.some(
|
||||
({ gun }) => gun.gun_type!.$case !== 'regular',
|
||||
);
|
||||
const { unwrap } = useLocale();
|
||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||
|
||||
return (
|
||||
@@ -60,7 +62,7 @@ export function Body({ stats }: BodyProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
alt={unwrap(thisShell.name)}
|
||||
width={16}
|
||||
height={16}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDe
|
||||
import { awaitableSkillDefinitions } from '../../core/awaitables/skillDefinitions';
|
||||
import type { TankCharacteristics } from '../../core/blitzkit/tankCharacteristics';
|
||||
import { Var } from '../../core/radix/var';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||
import type { EquipmentMatrix } from '../../stores/duel';
|
||||
import { BlitzkitButtonGrayIcon } from '../BlitzkitButtonGrayIcon';
|
||||
@@ -43,6 +44,7 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
|
||||
const members = CompareEphemeral.use((state) => state.members);
|
||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<StickyTableRoot
|
||||
@@ -293,10 +295,10 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
<Flex direction="column" justify="center">
|
||||
{provisions.map((provision, index) => (
|
||||
<img
|
||||
alt={
|
||||
alt={unwrap(
|
||||
provisionDefinitions.provisions[provision]
|
||||
.name
|
||||
}
|
||||
.name,
|
||||
)}
|
||||
key={provision}
|
||||
src={asset(
|
||||
`/icons/provisions/${provision}.webp`,
|
||||
@@ -473,11 +475,11 @@ export function CompareTable({ stats }: CompareTableProps) {
|
||||
<Flex direction="column" justify="center">
|
||||
{consumables.map((consumable, index) => (
|
||||
<img
|
||||
alt={
|
||||
alt={unwrap(
|
||||
consumableDefinitions.consumables[
|
||||
consumable
|
||||
].name
|
||||
}
|
||||
].name,
|
||||
)}
|
||||
key={consumable}
|
||||
src={asset(
|
||||
`/icons/consumables/${consumable}.webp`,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type TankDefinition, tankIcon } from '@blitzkit/core';
|
||||
import { Flex, Text } from '@radix-ui/themes';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Vector2 } from 'three';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { CompareEphemeral } from '../../stores/compareEphemeral';
|
||||
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
|
||||
import { InsertionMarker } from './IntersectionMarker';
|
||||
@@ -16,6 +17,7 @@ interface TankCardProps {
|
||||
export function TankCard({ index, tank }: TankCardProps) {
|
||||
const draggable = useRef<HTMLDivElement>(null);
|
||||
const mutateCompareEphemeral = CompareEphemeral.useMutation();
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
useEffect(() => {
|
||||
const initial = new Vector2();
|
||||
@@ -118,7 +120,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={tank.name}
|
||||
alt={unwrap(tank.name)}
|
||||
src={tankIcon(tank.id)}
|
||||
width={64}
|
||||
height={64}
|
||||
@@ -136,7 +138,7 @@ export function TankCard({ index, tank }: TankCardProps) {
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{tank.name}
|
||||
{unwrap(tank.name)}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
type AccountListItem,
|
||||
type AccountListWithServer,
|
||||
} from '@blitzkit/core';
|
||||
import strings from '@blitzkit/core/lang/en.json';
|
||||
import strings from '@blitzkit/i18n/strings/en.json';
|
||||
import { CaretLeftIcon, Link2Icon, PersonIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
Badge,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
sumCompositeStats,
|
||||
type IndividualTankStats,
|
||||
} from '@blitzkit/core';
|
||||
import strings from '@blitzkit/core/lang/en.json';
|
||||
import strings from '@blitzkit/i18n/strings/en.json';
|
||||
import { ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { ContextMenu } from '@radix-ui/themes';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||
import { GlobeIcon } from '@radix-ui/react-icons';
|
||||
import { Flex, Text } from '@radix-ui/themes';
|
||||
import type { LocaleAcceptorProps } from '../../hooks/useLocale';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||
import { Link, type LinkProps } from '@radix-ui/themes';
|
||||
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {} from '@blitzkit/core';
|
||||
import {
|
||||
DEFAULT_LOCALE,
|
||||
localizedStrings,
|
||||
SUPPORTED_LOCALE_FLAGS,
|
||||
SUPPORTED_LOCALES,
|
||||
} from '@blitzkit/core';
|
||||
SUPPORTED_LOCALE_FLAGS,
|
||||
localizedStrings,
|
||||
} from '@blitzkit/i18n';
|
||||
import { Select } from '@radix-ui/themes';
|
||||
import type { LocaleAcceptorProps } from '../hooks/useLocale';
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ClockIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { Flex, Text } from '@radix-ui/themes';
|
||||
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
||||
import { useDelta } from '../../hooks/useDelta';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||
|
||||
@@ -22,11 +23,12 @@ export function ConsumableButton({
|
||||
}: ConsumableButtonProps) {
|
||||
const cooldownDelta = useDelta(cooldown ?? 0);
|
||||
const durationDelta = useDelta(duration ?? 0);
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<GenericTankComponentButton
|
||||
style={{ width: '6rem' }}
|
||||
tooltip={consumableDefinitions.consumables[consumable].name}
|
||||
tooltip={unwrap(consumableDefinitions.consumables[consumable].name)}
|
||||
icon={asset(`icons/consumables/${consumable}.webp`)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { awaitableEquipmentDefinitions } from '../../core/awaitables/equipmentDefinitions';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||
|
||||
@@ -10,9 +11,11 @@ interface EquipmentButtonProps extends TankComponentButtonProps {
|
||||
const equipmentDefinitions = await awaitableEquipmentDefinitions;
|
||||
|
||||
export function EquipmentButton({ equipment, ...props }: EquipmentButtonProps) {
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<GenericTankComponentButton
|
||||
tooltip={equipmentDefinitions.equipments[equipment].name}
|
||||
tooltip={unwrap(equipmentDefinitions.equipments[equipment].name)}
|
||||
icon={asset(`icons/equipment/${equipment}.webp`)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||
import type { TankComponentButtonProps } from './TankComponentButton';
|
||||
|
||||
@@ -10,9 +11,11 @@ interface ProvisionButtonProps extends TankComponentButtonProps {
|
||||
const provisionDefinitions = await awaitableProvisionDefinitions;
|
||||
|
||||
export function ProvisionButton({ provision, ...props }: ProvisionButtonProps) {
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<GenericTankComponentButton
|
||||
tooltip={provisionDefinitions.provisions[provision].name}
|
||||
tooltip={unwrap(provisionDefinitions.provisions[provision].name)}
|
||||
icon={asset(`icons/provisions/${provision}.webp`)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@blitzkit/core';
|
||||
import { DropdownMenu, Flex } from '@radix-ui/themes';
|
||||
import { useState } from 'react';
|
||||
import { useLocale } from '../hooks/useLocale';
|
||||
import { ModuleButton } from './ModuleButtons/ModuleButton';
|
||||
|
||||
interface ModuleManagerProps {
|
||||
@@ -35,6 +36,7 @@ export function ModuleManager({
|
||||
const [gunMenuOpen, setGunMenuOpen] = useState(false);
|
||||
const [engineMenuOpen, setEngineMenuOpen] = useState(false);
|
||||
const [trackMenuOpen, setTrackMenuOpen] = useState(false);
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex gap="1" wrap="wrap">
|
||||
@@ -71,7 +73,7 @@ export function ModuleManager({
|
||||
module="turret"
|
||||
discriminator={TIER_ROMAN_NUMERALS[turret.tier]}
|
||||
/>{' '}
|
||||
{turret.name}
|
||||
{unwrap(turret.name)}
|
||||
</DropdownMenu.RadioItem>
|
||||
);
|
||||
})}
|
||||
@@ -117,7 +119,7 @@ export function ModuleManager({
|
||||
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
|
||||
}
|
||||
/>{' '}
|
||||
{gun.gun_type!.value.base.name}
|
||||
{unwrap(gun.gun_type!.value.base.name)}
|
||||
</DropdownMenu.RadioItem>
|
||||
);
|
||||
})}
|
||||
@@ -156,7 +158,7 @@ export function ModuleManager({
|
||||
module="engine"
|
||||
discriminator={TIER_ROMAN_NUMERALS[engine.tier]}
|
||||
/>{' '}
|
||||
{engine.name}
|
||||
{unwrap(engine.name)}
|
||||
</DropdownMenu.RadioItem>
|
||||
);
|
||||
})}
|
||||
@@ -195,7 +197,7 @@ export function ModuleManager({
|
||||
module="chassis"
|
||||
discriminator={TIER_ROMAN_NUMERALS[track.tier]}
|
||||
/>{' '}
|
||||
{track.name}
|
||||
{unwrap(track.name)}
|
||||
</DropdownMenu.RadioItem>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -28,7 +28,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
||||
}: TankCardProps,
|
||||
ref,
|
||||
) => {
|
||||
const { locale } = useLocale();
|
||||
const { unwrap, locale } = useLocale();
|
||||
const provideLink = !noLink && onSelect === undefined;
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const Icon = classIcons[tank.class];
|
||||
@@ -73,7 +73,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={tank.name}
|
||||
alt={unwrap(tank.name)}
|
||||
src={asset(`icons/tanks/blitzkit/${tank.id}.webp`)}
|
||||
className="image"
|
||||
draggable={false}
|
||||
@@ -89,7 +89,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
|
||||
>
|
||||
<Icon className="class-icon" />
|
||||
<Text align="center" className="name">
|
||||
{tank.name}
|
||||
{unwrap(tank.name)}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ interface TankRowHeaderCellProps {
|
||||
}
|
||||
|
||||
export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
||||
const { locale } = useLocale();
|
||||
const { locale, unwrap } = useLocale();
|
||||
const Icon = classIcons[tank.class];
|
||||
|
||||
return (
|
||||
@@ -24,7 +24,7 @@ export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
||||
tabIndex={-1}
|
||||
>
|
||||
<img
|
||||
alt={tank.name}
|
||||
alt={unwrap(tank.name)}
|
||||
draggable={false}
|
||||
src={tankIcon(tank.id)}
|
||||
style={{
|
||||
@@ -70,7 +70,7 @@ export function TankRowHeaderCell({ tank }: TankRowHeaderCellProps) {
|
||||
<ExperimentIcon style={{ width: '1em', height: '1em' }} />
|
||||
)}
|
||||
|
||||
{tank.name}
|
||||
{unwrap(tank.name)}
|
||||
</LinkI18n>
|
||||
</StickyRowHeaderCell>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ interface QuickLinkProps {
|
||||
|
||||
export function QuickLink({ topResult }: QuickLinkProps) {
|
||||
const tankFilters = useStore($tankFilters);
|
||||
const { locale } = useLocale();
|
||||
const { locale, unwrap } = useLocale();
|
||||
|
||||
if (!tankFilters.search || !topResult || tankFilters.searching) return null;
|
||||
|
||||
@@ -20,7 +20,7 @@ export function QuickLink({ topResult }: QuickLinkProps) {
|
||||
<TextField.Slot>
|
||||
<LinkI18n locale={locale} href={`/tools/tankopedia/${topResult.id}`}>
|
||||
<Button variant="ghost">
|
||||
{topResult.name} <CaretRightIcon />
|
||||
{unwrap(topResult.name)} <CaretRightIcon />
|
||||
</Button>
|
||||
</LinkI18n>
|
||||
</TextField.Slot>
|
||||
|
||||
+4
-2
@@ -27,6 +27,7 @@ import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/pr
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { useLocale } from '../../../../../hooks/useLocale';
|
||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
@@ -39,6 +40,7 @@ const [equipmentDefinitions, provisionDefinitions] = await Promise.all([
|
||||
]);
|
||||
|
||||
export function Characteristics() {
|
||||
const { unwrap } = useLocale();
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||
@@ -146,7 +148,7 @@ export function Characteristics() {
|
||||
|
||||
<Flex>
|
||||
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => (
|
||||
<Tooltip content={thisShell.name} key={thisShell.id}>
|
||||
<Tooltip content={unwrap(thisShell.name)} key={thisShell.id}>
|
||||
<IconButton
|
||||
color={thisShell.id === shell.id ? undefined : 'gray'}
|
||||
variant="soft"
|
||||
@@ -172,7 +174,7 @@ export function Characteristics() {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
alt={unwrap(thisShell.name)}
|
||||
width={16}
|
||||
height={16}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ function ModuleButton({
|
||||
gap={isTank ? '1' : undefined}
|
||||
>
|
||||
<Text size="1" color="gray">
|
||||
{formatCompact(unlock.cost.value)}
|
||||
{formatCompact(locale, unlock.cost.value)}
|
||||
</Text>
|
||||
|
||||
<img
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { awaitableConsumableDefinitions } from '../../core/awaitables/consumableDefinitions';
|
||||
import { awaitableGameDefinitions } from '../../core/awaitables/gameDefinitions';
|
||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
||||
import { useLocale } from '../../hooks/useLocale';
|
||||
import { Duel } from '../../stores/duel';
|
||||
|
||||
const [gameDefinitions, consumableDefinitions, provisionDefinitions] =
|
||||
@@ -13,6 +14,7 @@ const [gameDefinitions, consumableDefinitions, provisionDefinitions] =
|
||||
]);
|
||||
|
||||
export function GameModeSection() {
|
||||
const { unwrap } = useLocale();
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const roles = Object.entries(tank.roles);
|
||||
|
||||
@@ -67,7 +69,7 @@ export function GameModeSection() {
|
||||
justify="center"
|
||||
>
|
||||
<Text size="4" weight="bold">
|
||||
{gameMode.name}
|
||||
{unwrap(gameMode.name)}
|
||||
</Text>
|
||||
|
||||
<Flex direction="column">
|
||||
@@ -84,9 +86,9 @@ export function GameModeSection() {
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
src={asset(`icons/provisions/${id}.webp`)}
|
||||
alt={provisions.name}
|
||||
alt={unwrap(provisions.name)}
|
||||
/>
|
||||
{provisions.name}
|
||||
{unwrap(provisions.name)}
|
||||
</Flex>
|
||||
</Text>
|
||||
);
|
||||
@@ -104,9 +106,9 @@ export function GameModeSection() {
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
src={asset(`icons/consumables/${id}.webp`)}
|
||||
alt={consumable.name}
|
||||
alt={unwrap(consumable.name)}
|
||||
/>
|
||||
{consumable.name}
|
||||
{unwrap(consumable.name)}
|
||||
</Flex>
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -89,7 +89,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const { strings } = useLocale();
|
||||
const { strings, unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -244,7 +244,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={thisShell.name}
|
||||
alt={unwrap(thisShell.name)}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
style={{
|
||||
width: '50%',
|
||||
@@ -362,7 +362,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
|
||||
<Button variant="ghost">
|
||||
<Flex gap="2" align="center">
|
||||
<SmallTankIcon id={antagonistTank.id} size={16} />
|
||||
{antagonistTank.name}
|
||||
{unwrap(antagonistTank.name)}
|
||||
</Flex>
|
||||
</Button>
|
||||
</Dialog.Trigger>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { NAVBAR_HEIGHT } from '../../../constants/navbar';
|
||||
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
|
||||
import { Var } from '../../../core/radix/var';
|
||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||
import { useLocale } from '../../../hooks/useLocale';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||
@@ -18,6 +19,7 @@ import { TankSandboxLoader } from './components/TankSandboxLoader';
|
||||
const tankDefinitions = await awaitableTankDefinitions;
|
||||
|
||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
const { unwrap } = useLocale();
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const isFullScreen = useFullScreen();
|
||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||
@@ -117,7 +119,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
wrap="nowrap"
|
||||
color={treeColor}
|
||||
>
|
||||
{protagonist.name}
|
||||
{unwrap(protagonist.name)}
|
||||
</Heading>
|
||||
</Flex>
|
||||
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ import {
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { useState } from 'react';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { App } from '../../../../../../stores/app';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { Stars } from '../../Stars';
|
||||
@@ -24,6 +25,7 @@ export function VoteCaster() {
|
||||
const [postVote, setPostVote] = useState<boolean | null>(null);
|
||||
// true = open, false = closed, null = casting
|
||||
const [showDialogue, setShowDialogue] = useState<boolean | null>(false);
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -35,7 +37,7 @@ export function VoteCaster() {
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content maxWidth="23rem">
|
||||
<Dialog.Title>Voting for {tank.name}</Dialog.Title>
|
||||
<Dialog.Title>Voting for {unwrap(tank.name)}</Dialog.Title>
|
||||
|
||||
<Flex direction="column" mt="4">
|
||||
<Flex align="center" justify="between">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { formatCompact } from '@blitzkit/core';
|
||||
import { Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
|
||||
import { memo } from 'react';
|
||||
import { useLocale } from '../../../../../hooks/useLocale';
|
||||
import { useTankVotes } from '../../../../../hooks/useTankVotes';
|
||||
import { App } from '../../../../../stores/app';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
@@ -13,13 +14,14 @@ export const Votes = memo(
|
||||
const id = Duel.use((state) => state.protagonist.tank.id);
|
||||
const votes = useTankVotes(id);
|
||||
const wargaming = App.use((state) => state.logins.wargaming);
|
||||
const { locale } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="3">
|
||||
<Flex justify="between" align="center">
|
||||
<Heading size="4">
|
||||
{!votes && <Skeleton height="1em" width="4rem" />}
|
||||
{votes && formatCompact(votes.votes)}{' '}
|
||||
{votes && formatCompact(locale, votes.votes)}{' '}
|
||||
{votes && (votes.votes > 1 ? 'votes' : 'vote')}
|
||||
</Heading>
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export function MetaSection() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
|
||||
const [showSwapDialog, setShowSwapDialog] = useState(false);
|
||||
const { locale, strings } = useLocale();
|
||||
const { locale, strings, unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex justify="center" align="center">
|
||||
@@ -124,7 +124,7 @@ export function MetaSection() {
|
||||
<Flex direction="column" width="100%">
|
||||
{protagonist.name_full && (
|
||||
<Listing label={strings.website.tools.tankopedia.meta.full_name}>
|
||||
{protagonist.name_full}
|
||||
{unwrap(protagonist.name_full)}
|
||||
</Listing>
|
||||
)}
|
||||
<Listing label={strings.website.tools.tankopedia.meta.nation}>
|
||||
|
||||
@@ -20,7 +20,7 @@ const [tankDefinitions, averageDefinitions] = await Promise.all([
|
||||
]);
|
||||
|
||||
export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
||||
const { locale, strings } = useLocale();
|
||||
const { locale, strings, unwrap } = useLocale();
|
||||
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
|
||||
const tank = tankDefinitions.tanks[id];
|
||||
const nextTanks = nextIds?.map((id) => tankDefinitions.tanks[id]);
|
||||
@@ -78,7 +78,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt={tank.name}
|
||||
alt={unwrap(tank.name)}
|
||||
src={asset(`icons/tanks/big/${id}.webp`)}
|
||||
width={64}
|
||||
height={64}
|
||||
@@ -92,7 +92,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
|
||||
<Text color="gray" size="1">
|
||||
{TIER_ROMAN_NUMERALS[tank.tier]}
|
||||
</Text>
|
||||
<Text wrap="nowrap">{tank.name}</Text>
|
||||
<Text wrap="nowrap">{unwrap(tank.name)}</Text>
|
||||
</Flex>
|
||||
<Flex gap="2" align="center">
|
||||
<Text color="gray" size="1" wrap="nowrap">
|
||||
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
type CompositeStats,
|
||||
type CompositeStatsKey,
|
||||
} from '@blitzkit/core';
|
||||
import strings from '@blitzkit/core/lang/en.json';
|
||||
import strings from '@blitzkit/i18n/strings/en.json';
|
||||
import { amberDark, blueDark } from '@radix-ui/colors';
|
||||
import { Flex, Text, type FlexProps } from '@radix-ui/themes';
|
||||
import { times } from 'lodash-es';
|
||||
import type { ReactNode } from 'react';
|
||||
import { breakdownConfig } from '../constants/embeds';
|
||||
import { toRadiusVar } from '../core/radix/utils';
|
||||
import { useLocale } from '../hooks/useLocale';
|
||||
import { useEmbedStateCurry } from '../stores/embedState/utilities';
|
||||
import { ClassHeavy } from './ClassIcon/components/ClassHeavy';
|
||||
import { ClassLight } from './ClassIcon/components/ClassLight';
|
||||
@@ -52,6 +53,7 @@ export function BreakdownEmbedCard({
|
||||
}: BreakdownEmbedCardProps) {
|
||||
const { useEmbedState, useRichText } =
|
||||
useEmbedStateCurry<typeof breakdownConfig>();
|
||||
const { unwrap } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -111,7 +113,7 @@ export function BreakdownEmbedCard({
|
||||
? 'Total'
|
||||
: tank === undefined
|
||||
? `Unknown tank`
|
||||
: tank.name}
|
||||
: unwrap(tank.name)}
|
||||
</Flex>
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { assertSecret, DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/i18n';
|
||||
|
||||
export function resolveBranchName(locale: string = DEFAULT_LOCALE) {
|
||||
const strings = localizedStrings[locale];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||
|
||||
export function getBestLocale() {
|
||||
const desiredLocale =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, localizedStrings } from '@blitzkit/i18n';
|
||||
|
||||
export function getStrings(locale: string = DEFAULT_LOCALE) {
|
||||
return localizedStrings[locale];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
||||
import { createContext, useContext, type ReactNode } from 'react';
|
||||
import { getStrings } from '../core/i18n/getStrings';
|
||||
|
||||
@@ -29,8 +29,9 @@ export function useLocale() {
|
||||
}
|
||||
|
||||
const strings = getStrings(context.locale);
|
||||
const unwrap = unwrapper(context.locale);
|
||||
|
||||
return { locale: context.locale, strings };
|
||||
return { locale: context.locale, strings, unwrap };
|
||||
}
|
||||
|
||||
export interface LocaleAcceptorProps {
|
||||
|
||||
@@ -6,7 +6,7 @@ import Footer from '../components/Footer.astro';
|
||||
import Navbar from '../components/Navbar.astro';
|
||||
import Party3 from '../components/Party3.astro';
|
||||
import './base.css';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||
|
||||
export interface Props {
|
||||
noFooter?: boolean;
|
||||
@@ -40,7 +40,7 @@ const isRoot = pathname === '/';
|
||||
<Party3 />
|
||||
|
||||
<script>
|
||||
import { SUPPORTED_LOCALES } from '@blitzkit/core';
|
||||
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||
import { getBestLocale } from '../core/i18n/getBestLocale';
|
||||
|
||||
const isI18nPath = SUPPORTED_LOCALES.some((locale) =>
|
||||
@@ -51,8 +51,6 @@ const isRoot = pathname === '/';
|
||||
window.location.pathname.startsWith(`/${locale}`),
|
||||
);
|
||||
|
||||
console.log(bestLocale);
|
||||
|
||||
if (bestLocale !== currentPathLocale) {
|
||||
window.location.pathname = isI18nPath
|
||||
? window.location.pathname.replace(
|
||||
|
||||
@@ -3,7 +3,7 @@ import { App } from '../../../../stores/app';
|
||||
|
||||
export const AUTH_PROVIDERS = ['wargaming'] as const;
|
||||
|
||||
type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
||||
export type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
||||
|
||||
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
||||
wargaming: 'Wargaming',
|
||||
|
||||
@@ -4,7 +4,12 @@ import type { GetStaticPaths } from 'astro';
|
||||
import { PageWrapper } from '../../../../components/PageWrapper';
|
||||
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
||||
import { getStaticPaths as parentGetStaticPaths } from '../../index.astro';
|
||||
import { AUTH_PROVIDERS, AUTH_PROVIDER_NAMES, Authorize } from './_index';
|
||||
import {
|
||||
AUTH_PROVIDER_NAMES,
|
||||
AUTH_PROVIDERS,
|
||||
Authorize,
|
||||
type AuthProvider,
|
||||
} from './_index';
|
||||
|
||||
export const getStaticPaths = (() => {
|
||||
const parentPaths = parentGetStaticPaths();
|
||||
@@ -14,7 +19,7 @@ export const getStaticPaths = (() => {
|
||||
).flat();
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
const { provider } = Astro.params;
|
||||
const provider = Astro.params.provider as AuthProvider;
|
||||
---
|
||||
|
||||
<BaseLayout>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
---
|
||||
import {
|
||||
assertSecret,
|
||||
DEFAULT_LOCALE,
|
||||
SUPPORTED_LOCALES,
|
||||
} from '@blitzkit/core';
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@blitzkit/i18n';
|
||||
import { Box, Flex, Heading } from '@radix-ui/themes';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
import { AesonPlug } from '../../components/AesonPlug';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||
import { Flex, Switch, Text } from '@radix-ui/themes';
|
||||
import { LocaleSwitcher } from '../../../components/LocaleSwitcher';
|
||||
import { PageWrapper } from '../../../components/PageWrapper';
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type IndividualAccountInfo,
|
||||
type IndividualTankStats,
|
||||
} from '@blitzkit/core';
|
||||
import strings from '@blitzkit/core/lang/en.json';
|
||||
import strings from '@blitzkit/i18n/strings/en.json';
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
MagnifyingGlassIcon,
|
||||
|
||||
@@ -4,7 +4,6 @@ import { CalloutsSection } from '../../../../../components/Tankopedia/CalloutsSe
|
||||
import { CharacteristicsSection } from '../../../../../components/Tankopedia/CharacteristicsSection';
|
||||
import { GameModeSection } from '../../../../../components/Tankopedia/GameModeSection';
|
||||
import { HeroSection } from '../../../../../components/Tankopedia/HeroSection';
|
||||
import { HistorySection } from '../../../../../components/Tankopedia/HistorySection';
|
||||
import { MetaSection } from '../../../../../components/Tankopedia/MetaSection';
|
||||
import { TechTreeSection } from '../../../../../components/Tankopedia/TechTreeSection';
|
||||
import { VideoSection } from '../../../../../components/Tankopedia/VideoSection';
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
asset,
|
||||
createDefaultProvisions,
|
||||
createDefaultSkills,
|
||||
DEFAULT_LOCALE,
|
||||
fetchEquipmentDefinitions,
|
||||
fetchModelDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
fetchTankDefinitions,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
} from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||
import { getStrings } from '../../../../../core/i18n/getStrings';
|
||||
@@ -44,8 +44,9 @@ const tankModelDefinitions = await fetchModelDefinitions();
|
||||
const tank = tankDefinitions.tanks[id];
|
||||
const tankModelDefinition = tankModelDefinitions.models[tank.id];
|
||||
const name = tank.name_full ?? tank.name;
|
||||
const unwrap = unwrapper(locale);
|
||||
const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
|
||||
name,
|
||||
unwrap(name),
|
||||
TIER_ROMAN_NUMERALS[tank.tier],
|
||||
strings.common.nations_adjectives[
|
||||
tank.nation as keyof typeof strings.common.nations_adjectives
|
||||
@@ -84,7 +85,7 @@ const characteristics = tankCharacteristics(
|
||||
const description = literals(
|
||||
strings.website.tools.tankopedia.tank_seo_description,
|
||||
[
|
||||
name,
|
||||
unwrap(name),
|
||||
Math.round(characteristics.dpm).toLocaleString(locale),
|
||||
Math.round(characteristics.damage).toLocaleString(locale),
|
||||
Math.round(characteristics.penetration).toLocaleString(locale),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/core';
|
||||
import { DEFAULT_LOCALE } from '@blitzkit/i18n';
|
||||
import type { APIRoute, GetStaticPaths } from 'astro';
|
||||
import { fetchGlossary } from '../../../core/blitz/fetchGlossary';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import en from '@blitzkit/core/lang/en.json';
|
||||
import en from '@blitzkit/i18n/strings/en.json';
|
||||
import type { TankPerformanceSortType } from '.';
|
||||
|
||||
export const TankPerformanceSortTypeNamesArray = Object.keys(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type en from '@blitzkit/core/lang/en.json';
|
||||
import type en from '@blitzkit/i18n/strings/en.json';
|
||||
import { create } from 'zustand';
|
||||
import { createContextualStore } from '../../core/zustand/createContextualStore';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type en from '@blitzkit/core/lang/en.json';
|
||||
import type en from '@blitzkit/i18n/strings/en.json';
|
||||
import * as lodash from 'lodash-es';
|
||||
import { create } from 'zustand';
|
||||
import { persist, subscribeWithSelector } from 'zustand/middleware';
|
||||
|
||||
Reference in new issue
Block a user