feat: localize tankopedia tech tree

This commit is contained in:
tresabhi committed 2025-01-14 14:13:00 -06:00
1 parent ceb7842d49
commit 7900c61a9b
7 files changed
+44 -27

No files matched your search

+10 -2
View File
@@ -6,11 +6,12 @@
}, },
"common": { "common": {
"units_suffix": { "units": {
"m": "%s1m", "m": "%s1m",
"mm": "%s1mm", "mm": "%s1mm",
"deg": "%s1°", "deg": "%s1°",
"hp": "%s1hp" "hp": "%s1hp",
"x": "x%s1"
}, },
"shells": { "shells": {
@@ -923,6 +924,13 @@
"restoration": "Restoration price", "restoration": "Restoration price",
"sale": "Sale price", "sale": "Sale price",
"research": "Research XP" "research": "Research XP"
},
"tech_tree": {
"title": "Tech tree",
"battle": "1 battle",
"battles": "%s1 battles",
"route": "Route %s1 of %s2"
} }
}, },
"compare": { "compare": {
@@ -48,7 +48,7 @@ function LayerEntry({
{layer.type === null && ( {layer.type === null && (
<> <>
<Text size="2" color={shotStatusColor} style={{ width: 64 }}> <Text size="2" color={shotStatusColor} style={{ width: 64 }}>
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
Math.round(layer.distance * 1000).toLocaleString(locale), Math.round(layer.distance * 1000).toLocaleString(locale),
])} ])}
</Text> </Text>
@@ -65,7 +65,7 @@ function LayerEntry({
{layer.type === ArmorType.External && ( {layer.type === ArmorType.External && (
<Text size="2" color={shotStatusColor} style={{ width: 64 }}> <Text size="2" color={shotStatusColor} style={{ width: 64 }}>
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
Math.round(layer.thickness).toLocaleString(locale), Math.round(layer.thickness).toLocaleString(locale),
])} ])}
</Text> </Text>
@@ -77,7 +77,7 @@ function LayerEntry({
<Text size="2" color={shotStatusColor} style={{ width: 80 }}> <Text size="2" color={shotStatusColor} style={{ width: 80 }}>
{literals( {literals(
angle === undefined angle === undefined
? strings.common.units_suffix.mm ? strings.common.units.mm
: strings.website.tools.tankopedia.sandbox.dynamic.shot_card : strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.stats.thickness_and_angle, .stats.thickness_and_angle,
[ [
@@ -151,7 +151,7 @@ export function CustomShellButton() {
}} }}
> >
<TextField.Slot side="right"> <TextField.Slot side="right">
{literals(strings.common.units_suffix.mm, [''])} {literals(strings.common.units.mm, [''])}
</TextField.Slot> </TextField.Slot>
</TextField.Root> </TextField.Root>
</Flex> </Flex>
@@ -187,7 +187,7 @@ export function CustomShellButton() {
}} }}
> >
<TextField.Slot side="right"> <TextField.Slot side="right">
{literals(strings.common.units_suffix.mm, [''])} {literals(strings.common.units.mm, [''])}
</TextField.Slot> </TextField.Slot>
</TextField.Root> </TextField.Root>
</Flex> </Flex>
@@ -211,7 +211,7 @@ export function CustomShellButton() {
minWidth="8rem" minWidth="8rem"
> >
<Text color="gray"> <Text color="gray">
{literals(strings.common.units_suffix.deg, [ {literals(strings.common.units.deg, [
(customShell.normalization ?? 0).toFixed(0), (customShell.normalization ?? 0).toFixed(0),
])} ])}
</Text> </Text>
@@ -241,7 +241,7 @@ export function CustomShellButton() {
<Flex align="center" gap="2" flexGrow="1" maxWidth="15rem"> <Flex align="center" gap="2" flexGrow="1" maxWidth="15rem">
<Text color="gray"> <Text color="gray">
{literals(strings.common.units_suffix.deg, [ {literals(strings.common.units.deg, [
(customShell.ricochet ?? 90).toFixed(0), (customShell.ricochet ?? 90).toFixed(0),
])} ])}
</Text> </Text>
@@ -55,7 +55,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
> >
<Flex direction="column" align="end" justify="between"> <Flex direction="column" align="end" justify="between">
<Text color="gray" size="1"> <Text color="gray" size="1">
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
(thicknessRange.value * 1.5).toFixed(0), (thicknessRange.value * 1.5).toFixed(0),
])} ])}
</Text> </Text>
@@ -63,7 +63,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
{strings.website.tools.tankopedia.sandbox.static.primary} {strings.website.tools.tankopedia.sandbox.static.primary}
</Text> </Text>
<Text color="gray" size="1"> <Text color="gray" size="1">
{literals(strings.common.units_suffix.mm, ['0'])} {literals(strings.common.units.mm, ['0'])}
</Text> </Text>
</Flex> </Flex>
@@ -96,7 +96,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
> >
<Flex direction="column" align="end" justify="between"> <Flex direction="column" align="end" justify="between">
<Text color="gray" size="1"> <Text color="gray" size="1">
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
thicknessRange.value.toFixed(0), thicknessRange.value.toFixed(0),
])} ])}
</Text> </Text>
@@ -104,7 +104,7 @@ export function Thicknesses({ thicknessRange }: ThicknessesProps) {
{strings.website.tools.tankopedia.sandbox.static.spaced} {strings.website.tools.tankopedia.sandbox.static.spaced}
</Text> </Text>
<Text color="gray" size="1"> <Text color="gray" size="1">
{literals(strings.common.units_suffix.mm, ['0'])} {literals(strings.common.units.mm, ['0'])}
</Text> </Text>
</Flex> </Flex>
@@ -127,7 +127,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
> >
{!hasCustomShell && ( {!hasCustomShell && (
<Text color="gray" size={{ initial: '1', sm: '2' }}> <Text color="gray" size={{ initial: '1', sm: '2' }}>
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
( (
resolvePenetrationCoefficient( resolvePenetrationCoefficient(
hasCalibratedShells, hasCalibratedShells,
@@ -202,7 +202,7 @@ export function Options({ thicknessRange, canvas }: OptionsProps) {
} }
secondaryDiscriminator={ secondaryDiscriminator={
<Text style={{ fontSize: '0.75em' }}> <Text style={{ fontSize: '0.75em' }}>
{literals(strings.common.units_suffix.mm, [ {literals(strings.common.units.mm, [
gun.gun_type!.value.base.shells[0].caliber.toFixed( gun.gun_type!.value.base.shells[0].caliber.toFixed(
0, 0,
), ),
@@ -2,6 +2,7 @@ import { asset, formatCompact, TIER_ROMAN_NUMERALS } from '@blitzkit/core';
import { Box, Flex, Skeleton, Text } from '@radix-ui/themes'; import { Box, Flex, Skeleton, Text } from '@radix-ui/themes';
import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions'; import { awaitableAverageDefinitions } from '../../../../core/awaitables/averageDefinitions';
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
import { literals } from '../../../../core/i18n/literals';
import { useLocale } from '../../../../hooks/useLocale'; import { useLocale } from '../../../../hooks/useLocale';
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps'; import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps';
@@ -19,7 +20,7 @@ const [tankDefinitions, averageDefinitions] = await Promise.all([
]); ]);
export function Node({ id, highlight, nextIds, skeleton }: NodeProps) { export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
const { locale } = useLocale(); const { locale, strings } = useLocale();
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier); const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
const tank = tankDefinitions.tanks[id]; const tank = tankDefinitions.tanks[id];
const nextTanks = nextIds?.map((id) => tankDefinitions.tanks[id]); const nextTanks = nextIds?.map((id) => tankDefinitions.tanks[id]);
@@ -106,7 +107,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
objectPosition: 'center', objectPosition: 'center',
}} }}
/> />
{formatCompact(thisTankXp)} {formatCompact(locale, thisTankXp)}
</Flex> </Flex>
</Text> </Text>
<Text color="gray" size="1" wrap="nowrap"> <Text color="gray" size="1" wrap="nowrap">
@@ -121,7 +122,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
objectPosition: 'center', objectPosition: 'center',
}} }}
/> />
{formatCompact(tank.price.value)} {formatCompact(locale, tank.price.value)}
</Flex> </Flex>
</Text> </Text>
</Flex> </Flex>
@@ -129,11 +130,13 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
{averages && nextTanks && tank.tier !== 10 && ( {averages && nextTanks && tank.tier !== 10 && (
<Text color="gray" size="1" mt="2"> <Text color="gray" size="1" mt="2">
{skeleton && <Skeleton height="1em" width="4em" />} {skeleton && <Skeleton height="1em" width="4em" />}
{!skeleton && ( {!skeleton &&
<> (games === 1
{games!} {games === 1 ? 'battle' : 'battles'} ? strings.website.tools.tankopedia.tech_tree.battle
</> : literals(
)} strings.website.tools.tankopedia.tech_tree.battles,
[`${games}`],
))}
</Text> </Text>
)} )}
</Flex> </Flex>
@@ -3,6 +3,7 @@ import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes'; import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'; import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
import { literals } from '../../../core/i18n/literals';
import { useLocale } from '../../../hooks/useLocale'; import { useLocale } from '../../../hooks/useLocale';
import { Duel } from '../../../stores/duel'; import { Duel } from '../../../stores/duel';
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
@@ -17,7 +18,7 @@ const tankDefinitions = await awaitableTankDefinitions;
export const XP_MULTIPLIERS = [1, 2, 3, 4, 5, 10]; export const XP_MULTIPLIERS = [1, 2, 3, 4, 5, 10];
export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) { export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
const { locale } = useLocale(); const { locale, strings } = useLocale();
const master = Duel.use((state) => state.protagonist.tank); const master = Duel.use((state) => state.protagonist.tank);
const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier); const xpMultiplier = TankopediaEphemeral.use((state) => state.xpMultiplier);
const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
@@ -91,7 +92,9 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
py="6" py="6"
> >
<Flex direction="column" align="center"> <Flex direction="column" align="center">
<Heading size="6">Tech tree</Heading> <Heading size="6">
{strings.website.tools.tankopedia.tech_tree.title}
</Heading>
{master.tier > 1 && ( {master.tier > 1 && (
<Flex gap="4" align="center"> <Flex gap="4" align="center">
@@ -156,7 +159,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
pl="1" pl="1"
> >
<Text size="2"> <Text size="2">
x<b>{multiplier}</b> {literals(strings.common.units.x, [`${multiplier}`])}
</Text> </Text>
</Flex> </Flex>
); );
@@ -216,7 +219,10 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
<CaretLeftIcon /> <CaretLeftIcon />
</IconButton> </IconButton>
<Text> <Text>
Route {lineIndex + 1} of {lines.length} {literals(strings.website.tools.tankopedia.tech_tree.route, [
`${lineIndex + 1}`,
`${lines.length}`,
])}
</Text> </Text>
<IconButton <IconButton
size="2" size="2"