From c4f1d9f3b05ef4f544b5e191c8d94561184467ce Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Tue, 14 Jan 2025 20:13:38 -0600 Subject: [PATCH] feat: localize tankopedia chars --- .../src/blitzkit/tankDefinitions/constants.ts | 14 +- packages/i18n/strings/en.json | 113 ++++++++ .../Characteristics/components/Heading.tsx | 25 +- .../components/InfoWithDelta.tsx | 46 ++- .../components/Characteristics/index.tsx | 270 ++++++++++-------- 5 files changed, 322 insertions(+), 146 deletions(-) diff --git a/packages/core/src/blitzkit/tankDefinitions/constants.ts b/packages/core/src/blitzkit/tankDefinitions/constants.ts index cebc801ed..efa2e7e16 100644 --- a/packages/core/src/blitzkit/tankDefinitions/constants.ts +++ b/packages/core/src/blitzkit/tankDefinitions/constants.ts @@ -13,13 +13,13 @@ export const SHELL_NAMES: Record = { [ShellType.HEAT]: 'HEAT', [ShellType.HE]: 'HE', }; -export const CREW_MEMBER_NAMES: Record = { - [CrewType.COMMANDER]: 'Commander', - [CrewType.DRIVER]: 'Driver', - [CrewType.GUNNER]: 'Gunner', - [CrewType.LOADER]: 'Loader', - [CrewType.RADIOMAN]: 'Radioman', -}; +export const CREW_MEMBER_NAMES = { + [CrewType.COMMANDER]: 'commander', + [CrewType.DRIVER]: 'driver', + [CrewType.GUNNER]: 'gunner', + [CrewType.LOADER]: 'loader', + [CrewType.RADIOMAN]: 'radioman', +} as const; export const GUN_TYPE_NAMES: Record< Exclude['$case'], string diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 0fa6ed9d1..cc45bd9a9 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -973,6 +973,119 @@ "miscellaneous": { "title": "Miscellaneous" } + }, + + "characteristics": { + "title": "Characteristics", + "compare": { + "label": "Compare against", + "class": "Tier %s1 %s2", + "tier": "All of tier %s1", + "all": "All tanks" + }, + "values": { + "shell_index": "Shell %s1", + "at_distance": "At %s1m", + "at_distance_slider_label": "Distance", + "dispersion_at_distance": "Dispersion at %s1m", + "gun_flexibility": "Gun flexibility", + "speed": "Speed", + "terrain_coefficients": "Terrain coefficients", + "raw_terrain_coefficients": "Raw terrain coefficients", + "power_to_weight_ratio": "Power to weight ratio", + "hull_traverse_speed": "Hull traverse speed", + "camouflage": "Camouflage", + + "crewCount": "Crew count", + "shellNormalization": "Normalization", + "shellRicochet": "Ricochet", + "dpm": "DPM", + "dpmEffective": "Effective at 60s", + "shells": "Shells", + "mostOptimalShellIndex": "Most optimal shell", + "shellReloads": "Shell reloads", + "shellReload": "Shell reload", + "intraClip": "Intra-clip", + "caliber": "Caliber", + "penetration": "Penetration", + "damage": "Damage", + "clipDamage": "Clipping potential", + "moduleDamage": "Module damage", + "explosionRadius": "Explosion radius", + "shellVelocity": "Shell velocity", + "aimTime": "Aim time", + "dispersion": "Still", + "dispersionMoving": "Moving", + "dispersionHullTraversing": "Hull traversing", + "dispersionTurretTraversing": "Turret traversing", + "dispersionShooting": "Shooting", + "dispersionGunDamaged": "Gun damaged", + "gunDepression": "Depression", + "gunElevation": "Elevation", + "gunFrontalDepression": "Frontal depression", + "gunFrontalElevation": "Frontal elevation", + "gunRearDepression": "Rear depression", + "gunRearElevation": "Rear elevation", + "azimuthLeft": "Azimuth left", + "azimuthRight": "Azimuth right", + "speedForwards": "Forwards", + "speedBackwards": "Backwards", + "hardTerrainCoefficient": "Hard", + "hardTerrainCoefficientRaw": "Hard", + "mediumTerrainCoefficient": "Medium", + "mediumTerrainCoefficientRaw": "Medium", + "softTerrainCoefficient": "Soft", + "softTerrainCoefficientRaw": "Soft", + "enginePower": "Engine power", + "powerToWeightRatioHardTerrain": "Hard", + "powerToWeightRatioMediumTerrain": "Medium", + "powerToWeightRatioSoftTerrain": "Soft", + "weight": "Weight", + "turretTraverseSpeed": "Turret traverse speed", + "gunTraverseSpeed": "Gun traverse speed", + "hullTraverseHardTerrain": "Hard", + "hullTraverseMediumTerrain": "Medium", + "hullTraverseSoftTerrain": "Soft", + "health": "Health", + "fireChance": "Fire chance", + "viewRange": "View range", + "camouflageStill": "Still", + "camouflageMoving": "Moving", + "camouflageShootingStill": "Still", + "camouflageShootingMoving": "Moving", + "camouflageCaughtOnFire": "Caught on fire", + "width": "Width", + "height": "Height", + "length": "Length", + "volume": "Volume", + "shellRange": "Shell range", + "shellCapacity": "Shell capacity", + "gunType": "Gun type", + "penetrationAt250m": "Penetration at 250m" + } + }, + + "firepower": { + "title": "Firepower" + }, + + "crew": { + "title": "Crew training", + "info": "represents the other roles a crew member can take.", + "count": "Crew count", + "commander": "Commander", + "gunner": "Gunner", + "driver": "Driver", + "radioman": "Radioman", + "loader": "Loader" + }, + + "maneuverability": { + "title": "Maneuverability" + }, + + "survivability": { + "title": "Survivability" } }, "compare": { diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Heading.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Heading.tsx index ba8335e29..675fc4ca8 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Heading.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/Heading.tsx @@ -1,5 +1,6 @@ import { TIER_ROMAN_NUMERALS } from '@blitzkit/core'; import { Flex, Heading, SegmentedControl, Text } from '@radix-ui/themes'; +import { literals } from '../../../../../../core/i18n/literals'; import { useLocale } from '../../../../../../hooks/useLocale'; import { Duel } from '../../../../../../stores/duel'; import { @@ -21,9 +22,13 @@ export function CharacteristicsHeading() { gap="2" direction="column" > - Characteristics + + {strings.website.tools.tankopedia.characteristics.title} + - Compare against + + {strings.website.tools.tankopedia.characteristics.compare.label} + - Tier {TIER_ROMAN_NUMERALS[tank.tier]}{' '} - {strings.common.tank_class_short_plural_lowercase[tank.class]} + {literals( + strings.website.tools.tankopedia.characteristics.compare.class, + [ + TIER_ROMAN_NUMERALS[tank.tier], + strings.common.tank_class_short_plural_lowercase[tank.class], + ], + )} - All of tier {TIER_ROMAN_NUMERALS[tank.tier]} + {literals( + strings.website.tools.tankopedia.characteristics.compare.tier, + [TIER_ROMAN_NUMERALS[tank.tier]], + )} - All tanks + {strings.website.tools.tankopedia.characteristics.compare.all} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx index b8e7a5eaf..e035d72c6 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/InfoWithDelta.tsx @@ -1,6 +1,6 @@ import { createDefaultSkills } from '@blitzkit/core'; import { Flex, Progress, Text } from '@radix-ui/themes'; -import { useMemo, type ComponentProps } from 'react'; +import { useMemo, type ComponentProps, type ReactNode } from 'react'; import { awaitableEquipmentDefinitions } from '../../../../../../core/awaitables/equipmentDefinitions'; import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions'; import { awaitableProvisionDefinitions } from '../../../../../../core/awaitables/provisionDefinitions'; @@ -12,6 +12,7 @@ import { } from '../../../../../../core/blitzkit/tankCharacteristics'; import { tankToDuelMember } from '../../../../../../core/blitzkit/tankToDuelMember'; import { useDelta } from '../../../../../../hooks/useDelta'; +import { useLocale } from '../../../../../../hooks/useLocale'; import { Duel } from '../../../../../../stores/duel'; import { TankopediaEphemeral, @@ -19,13 +20,18 @@ import { } from '../../../../../../stores/tankopediaEphemeral'; import { Info, type InfoProps } from './Info'; -interface InfoWithDeltaProps extends InfoProps { +type InfoWithDeltaProps = Omit & { stats: TankCharacteristics; - value: - | keyof TankCharacteristics - | ((tank: TankCharacteristics) => number | undefined); noRanking?: boolean; -} +} & ( + | { + value: keyof TankCharacteristics; + } + | { + value: (tank: TankCharacteristics) => number | undefined; + name: ReactNode; + } + ); const [ tankDefinitions, @@ -42,18 +48,20 @@ const [ ]); export function InfoWithDelta({ - value, stats, indent, noRanking, deltaType, ...props }: InfoWithDeltaProps) { + const { strings } = useLocale(); const relativeAgainst = TankopediaEphemeral.use( (state) => state.relativeAgainst, ); const uhWhatDoICallThisVariable = - typeof value === 'function' ? value(stats)! : (stats[value] as number); + typeof props.value === 'function' + ? props.value(stats)! + : (stats[props.value] as number); const delta = useDelta(uhWhatDoICallThisVariable); const protagonistTank = Duel.use((state) => state.protagonist.tank); const others = useMemo(() => { @@ -102,14 +110,18 @@ export function InfoWithDelta({ }) .filter((tank) => { const othersValue = - typeof value === 'function' ? value(tank)! : (tank[value] as number); + typeof props.value === 'function' + ? props.value(tank)! + : (tank[props.value] as number); return othersValue !== undefined; }); }, [relativeAgainst]); const betterTanks = others.filter((tank) => { const othersValue = - typeof value === 'function' ? value(tank)! : (tank[value] as number); + typeof props.value === 'function' + ? props.value(tank)! + : (tank[props.value] as number); if (othersValue === undefined) return false; return deltaType === 'lowerIsBetter' @@ -126,7 +138,19 @@ export function InfoWithDelta({ return ( - + {uhWhatDoICallThisVariable} diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx index da3ddc9ca..8dea59793 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/index.tsx @@ -26,6 +26,7 @@ import { awaitableEquipmentDefinitions } from '../../../../../core/awaitables/eq import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits'; import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics'; +import { literals } from '../../../../../core/i18n/literals'; import { useEquipment } from '../../../../../hooks/useEquipment'; import { useLocale } from '../../../../../hooks/useLocale'; import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition'; @@ -40,7 +41,7 @@ const [equipmentDefinitions, provisionDefinitions] = await Promise.all([ ]); export function Characteristics() { - const { unwrap } = useLocale(); + const { unwrap, strings } = useLocale(); const mutateDuel = Duel.useMutation(); const crewSkills = TankopediaEphemeral.use((state) => state.skills); const penetrationDistanceInput = useRef(null); @@ -144,7 +145,9 @@ export function Characteristics() { - Firepower + + {strings.website.tools.tankopedia.firepower.title} + {gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => ( @@ -184,10 +187,15 @@ export function Characteristics() { ))} - {GUN_TYPE_NAMES[gun.gun_type!.$case]} + + {GUN_TYPE_NAMES[gun.gun_type!.$case]} + )} {gun.gun_type!.$case !== 'regular' && ( - + )} {gun.gun_type!.$case === 'auto_reloader' ? ( <> - + {stats.mostOptimalShellIndex! + 1} - + {stats.shellReloads!.map((reload, index) => ( @@ -259,7 +279,11 @@ export function Characteristics() { stats={stats} indent decimals={0} - name={`At ${penetrationDistance}m`} + name={literals( + strings.website.tools.tankopedia.characteristics.values + .at_distance, + [`${penetrationDistance}`], + )} noRanking value={() => lerp( @@ -272,7 +296,12 @@ export function Characteristics() { } /> - Distance + + { + strings.website.tools.tankopedia.characteristics.values + .at_distance_slider_label + } + )} - + {gun.gun_type!.$case !== 'regular' && ( )} + - - + - + @@ -422,7 +429,6 @@ export function Characteristics() { decimals={3} prefix="+" indent - name="Hull traversing" deltaType="lowerIsBetter" value="dispersionHullTraversing" /> @@ -431,7 +437,6 @@ export function Characteristics() { decimals={3} prefix="+" indent - name="Turret traversing" deltaType="lowerIsBetter" value="dispersionTurretTraversing" /> @@ -440,7 +445,6 @@ export function Characteristics() { decimals={3} prefix="+" indent - name="After shooting" deltaType="lowerIsBetter" value="dispersionShooting" /> @@ -449,24 +453,26 @@ export function Characteristics() { decimals={1} prefix="x " indent - name="Gun damaged" - unit="scalar" deltaType="lowerIsBetter" value="dispersionGunDamaged" /> - + {gunModelDefinition.pitch.front && ( @@ -475,14 +481,12 @@ export function Characteristics() { stats={stats} decimals={1} indent - name="Frontal depression" value="gunFrontalDepression" /> @@ -493,14 +497,12 @@ export function Characteristics() { stats={stats} decimals={1} indent - name="Rear depression" value="gunRearDepression" /> @@ -511,14 +513,12 @@ export function Characteristics() { stats={stats} decimals={1} indent - name="Azimuth left" value="azimuthLeft" /> @@ -527,7 +527,9 @@ export function Characteristics() { - Crew training + + {strings.website.tools.tankopedia.crew.title} + @@ -539,20 +541,13 @@ export function Characteristics() { - - represents the other roles a crew member can take. - + {strings.website.tools.tankopedia.crew.info} - + {tank.crew.map((member) => { return ( @@ -560,8 +555,16 @@ export function Characteristics() { 1 ? ` x ${member.count}` : '' + name={`${ + strings.website.tools.tankopedia.crew[ + CREW_MEMBER_NAMES[member.type] + ] + }${ + member.count > 1 + ? ` ${literals(strings.common.units.x, [ + `${member.count}`, + ])}` + : '' }`} unit="%" decimals={0} @@ -586,7 +589,11 @@ export function Characteristics() { {member.substitute .map((sub, index) => - index === 0 ? CREW_MEMBER_NAMES[sub] : sub, + index === 0 + ? strings.website.tools.tankopedia.crew[ + CREW_MEMBER_NAMES[sub] + ] + : sub, ) .join(', ')} @@ -604,52 +611,51 @@ export function Characteristics() { - Maneuverability - + + {strings.website.tools.tankopedia.maneuverability.title} + + - + - + - + @@ -684,75 +693,77 @@ export function Characteristics() { decimals={2} unit="%" indent - name="Soft" deltaType="lowerIsBetter" value="softTerrainCoefficientRaw" /> - + - + - Survivability - + + {strings.website.tools.tankopedia.survivability.title} + + - + stats.camouflageStill * 100} stats={stats} indent - name="Still" + name={ + strings.website.tools.tankopedia.characteristics.values + .camouflageStill + } decimals={2} /> stats.camouflageMoving * 100} stats={stats} indent - name="Moving" + name={ + strings.website.tools.tankopedia.characteristics.values + .camouflageMoving + } decimals={2} /> stats.camouflageShootingStill * 100} /> stats.camouflageShootingMoving * 100} /> stats.camouflageCaughtOnFire * 100} />