diff --git a/packages/core/lang/en.json b/packages/core/lang/en.json index e3b74f683..6cea62945 100644 --- a/packages/core/lang/en.json +++ b/packages/core/lang/en.json @@ -716,6 +716,68 @@ "common": { "copy_button": { "copied": "Copied!" + }, + + "tank_search": { + "search_bar_hint": "Search tanks...", + "recent": "Recently viewed", + "select_all": "Select all", + "count_singular": "1 tank", + "count_plural": "%s1 tanks", + "sorting_by": "Sorting by %s1", + "sort": { + "meta.none": "none", + "survivability.health": "health", + "survivability.viewRange": "view range", + "survivability.camouflageStill": "camouflage still", + "survivability.camouflageMoving": "camouflage moving", + "survivability.camouflageShooting": "camouflage shooting", + "survivability.volume": "volume", + "survivability.length": "length", + "fire.dpm": "standard DPM", + "fire.dpmPremium": "premium DPM", + "fire.reload": "reload", + "fire.caliber": "caliber", + "fire.standardPenetration": "standard penetration", + "fire.premiumPenetration": "premium penetration", + "fire.damage": "damage", + "fire.moduleDamage": "module damage", + "fire.shellVelocity": "shell velocity", + "fire.shellRange": "shell range", + "fire.shellCapacity": "shell capacity", + "fire.aimTime": "aim time", + "fire.dispersionStill": "dispersion still", + "fire.dispersionMoving": "dispersion moving", + "fire.gunDepression": "gun depression", + "fire.gunElevation": "gun elevation", + "maneuverability.forwardsSpeed": "forwards speed", + "maneuverability.backwardsSpeed": "backwards speed", + "maneuverability.power": "power", + "maneuverability.powerToWeight": "power to weight", + "maneuverability.weight": "weight", + "maneuverability.traverseSpeed": "traverse speed" + }, + "sort_dropdown": { + "label": "Sort", + "groups": { + "sort": { + "no_sort": "No sort", + "label": "Sort by", + "fire": "Fire", + "maneuverability": "Maneuverability", + "survivability": "Survivability" + }, + "direction": { + "label": "Direction", + "ascending": "Ascending", + "descending": "Descending" + } + } + } + }, + + "warnings": { + "test": "Tanks in testing are subject to change and may not represent the final product." } }, @@ -743,7 +805,24 @@ "name": "Compare", "button": "Compare tanks", "description": "Compare tank statistics", - "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz" + "seo_description": "Compare tanks statistics and loadouts in World of Tanks Blitz", + "actions": { + "add": { + "button": "Add", + "title": "Add tanks", + "description": "Select tanks to add to comparison" + }, + "clear": "Clear", + "deltas": { + "none": "No deltas", + "percentage": "Percentage", + "absolute": "Absolute" + } + }, + "no_tanks": { + "title": "No tanks selected", + "body": "Press the Add button to get started" + } }, "performance": { "name": "Performance", diff --git a/packages/website/src/components/Compare/Controls.tsx b/packages/website/src/components/Compare/Controls.tsx index 50edd5113..b17f8745e 100644 --- a/packages/website/src/components/Compare/Controls.tsx +++ b/packages/website/src/components/Compare/Controls.tsx @@ -2,6 +2,7 @@ import { PlusIcon, TrashIcon } from '@radix-ui/react-icons'; import { Button, Dialog, Flex, SegmentedControl } from '@radix-ui/themes'; import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions'; import { tankToCompareMember } from '../../core/blitzkit/tankToCompareMember'; +import { useLocale } from '../../hooks/useLocale'; import { CompareEphemeral } from '../../stores/compareEphemeral'; import { ComparePersistent, @@ -23,6 +24,7 @@ export function Controls({ const deltaMode = ComparePersistent.use((state) => state.deltaMode); const mutateCompareEphemeral = CompareEphemeral.useMutation(); const mutateComparePersistent = ComparePersistent.useMutation(); + const { strings } = useLocale(); return ( @@ -32,14 +34,16 @@ export function Controls({ > - Add tanks + + {strings.website.tools.compare.actions.add.title} + - Select tanks to add to comparison + {strings.website.tools.compare.actions.add.description} @@ -87,7 +91,7 @@ export function Controls({ }); }} > - Clear + {strings.website.tools.compare.actions.clear} - No deltas + {strings.website.tools.compare.actions.deltas.none} - Percentage + {strings.website.tools.compare.actions.deltas.percentage} - Absolute + {strings.website.tools.compare.actions.deltas.absolute} diff --git a/packages/website/src/components/TankSearch/components/SearchBar.tsx b/packages/website/src/components/TankSearch/components/SearchBar.tsx index d06c0d09f..36b05044a 100644 --- a/packages/website/src/components/TankSearch/components/SearchBar.tsx +++ b/packages/website/src/components/TankSearch/components/SearchBar.tsx @@ -4,6 +4,7 @@ import { MagnifyingGlassIcon } from '@radix-ui/react-icons'; import { Flex, Spinner, TextField } from '@radix-ui/themes'; import { debounce } from 'lodash-es'; import { type KeyboardEventHandler, useCallback, useRef } from 'react'; +import { useLocale } from '../../../hooks/useLocale'; import { $tankFilters } from '../../../stores/tankFilters'; import { QuickLink } from './QuickLink'; import { Sort } from './Sort'; @@ -14,6 +15,7 @@ interface SearchBarProps { } export function SearchBar({ topResult, onSelect }: SearchBarProps) { + const { strings } = useLocale(); const tankFilters = useStore($tankFilters); const input = useRef(null); const performSearch = useCallback( @@ -67,7 +69,7 @@ export function SearchBar({ topResult, onSelect }: SearchBarProps) { defaultValue={tankFilters.search} style={{ flex: 1 }} ref={input} - placeholder="Search tanks..." + placeholder={strings.website.common.tank_search.search_bar_hint} onChange={handleChange} onKeyDown={handleKeyDown} > diff --git a/packages/website/src/components/TankSearch/components/Sort.tsx b/packages/website/src/components/TankSearch/components/Sort.tsx index ee2620fc8..7a43836a5 100644 --- a/packages/website/src/components/TankSearch/components/Sort.tsx +++ b/packages/website/src/components/TankSearch/components/Sort.tsx @@ -1,308 +1,122 @@ import { useStore } from '@nanostores/react'; import { CaretDownIcon } from '@radix-ui/react-icons'; import { Button, DropdownMenu } from '@radix-ui/themes'; -import type { TankopediaSortDirection } from '../../../stores/tankopediaPersistent'; +import { useLocale } from '../../../hooks/useLocale'; +import type { + TankopediaSortBy, + TankopediaSortDirection, +} from '../../../stores/tankopediaPersistent'; import { $tankopediaSort } from '../../../stores/tankopediaSort'; +interface ItemProps { + by: TankopediaSortBy; +} + +interface ItemProps { + by: TankopediaSortBy; +} + +function Item({ by }: ItemProps) { + const { strings } = useLocale(); + const tankopediaSort = useStore($tankopediaSort); + + return ( + { + $tankopediaSort.setKey('by', by); + }} + > + {strings.website.common.tank_search.sort[by]} + + ); +} + export function Sort() { const tankopediaSort = useStore($tankopediaSort); + const { strings } = useLocale(); return ( - { - // mutateTankopediaSort((draft) => { - // draft.by = 'meta.none'; - // }); - $tankopediaSort.setKey('by', 'meta.none'); - }} - > - No sort - + + {strings.website.common.tank_search.sort_dropdown.groups.sort.label} + - Sort by + - Fire + + {strings.website.common.tank_search.sort_dropdown.groups.sort.fire} + - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.dpm'; - // }); - $tankopediaSort.setKey('by', 'fire.dpm'); - }} - > - Standard DPM - - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.dpmPremium'; - // }); - $tankopediaSort.setKey('by', 'fire.dpmPremium'); - }} - > - Premium DPM - - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.reload'; - // }); - $tankopediaSort.setKey('by', 'fire.reload'); - }} - > - Reload - - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.caliber'; - // }); - $tankopediaSort.setKey('by', 'fire.caliber'); - }} - > - Caliber - - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.standardPenetration'; - // }); - $tankopediaSort.setKey('by', 'fire.standardPenetration'); - }} - > - Standard penetration - - { - // mutateTankopediaSort((draft) => { - // draft.by = 'fire.premiumPenetration'; - // }); - $tankopediaSort.setKey('by', 'fire.premiumPenetration'); - }} - > - Premium penetration - - { - $tankopediaSort.setKey('by', 'fire.damage'); - }} - > - Damage - - { - $tankopediaSort.setKey('by', 'fire.moduleDamage'); - }} - > - Module damage - - { - $tankopediaSort.setKey('by', 'fire.shellVelocity'); - }} - > - Shell velocity - - { - $tankopediaSort.setKey('by', 'fire.shellRange'); - }} - > - Shell range - - { - $tankopediaSort.setKey('by', 'fire.shellCapacity'); - }} - > - Shell capacity - - { - $tankopediaSort.setKey('by', 'fire.aimTime'); - }} - > - Aim time - - { - $tankopediaSort.setKey('by', 'fire.dispersionStill'); - }} - > - Dispersion still - - { - $tankopediaSort.setKey('by', 'fire.dispersionMoving'); - }} - > - Dispersion moving - - { - $tankopediaSort.setKey('by', 'fire.gunDepression'); - }} - > - Gun depression - - { - $tankopediaSort.setKey('by', 'fire.gunElevation'); - }} - > - Gun elevation - + + + + + + + + + + + + + + + + - Maneuverability + + { + strings.website.common.tank_search.sort_dropdown.groups.sort + .maneuverability + } + - { - $tankopediaSort.setKey('by', 'maneuverability.forwardsSpeed'); - }} - > - Forwards speed - - { - $tankopediaSort.setKey('by', 'maneuverability.backwardsSpeed'); - }} - > - Backwards speed - - { - $tankopediaSort.setKey('by', 'maneuverability.power'); - }} - > - Power - - { - $tankopediaSort.setKey('by', 'maneuverability.powerToWeight'); - }} - > - Power to weight - - { - $tankopediaSort.setKey('by', 'maneuverability.weight'); - }} - > - Weight - - { - $tankopediaSort.setKey('by', 'maneuverability.traverseSpeed'); - }} - > - Traverse speed - + + + + + + - Survivability + + { + strings.website.common.tank_search.sort_dropdown.groups.sort + .survivability + } + - { - $tankopediaSort.setKey('by', 'survivability.health'); - }} - > - Health - - { - $tankopediaSort.setKey('by', 'survivability.viewRange'); - }} - > - View range - - { - $tankopediaSort.setKey('by', 'survivability.camouflageStill'); - }} - > - Camouflage - - { - $tankopediaSort.setKey('by', 'survivability.camouflageMoving'); - }} - > - Camouflage moving - - { - $tankopediaSort.setKey( - 'by', - 'survivability.camouflageShooting', - ); - }} - > - Camouflage shooting - - { - $tankopediaSort.setKey('by', 'survivability.volume'); - }} - > - Volume - - { - $tankopediaSort.setKey('by', 'survivability.length'); - }} - > - Length - + + + + + + + - Direction + + { + strings.website.common.tank_search.sort_dropdown.groups.direction + .label + } + { @@ -313,10 +127,16 @@ export function Sort() { }} > - Ascending + { + strings.website.common.tank_search.sort_dropdown.groups.direction + .ascending + } - Descending + { + strings.website.common.tank_search.sort_dropdown.groups.direction + .descending + } diff --git a/packages/website/src/components/TankSearch/index.tsx b/packages/website/src/components/TankSearch/index.tsx index f3735b4bd..5f8ea8226 100644 --- a/packages/website/src/components/TankSearch/index.tsx +++ b/packages/website/src/components/TankSearch/index.tsx @@ -16,12 +16,11 @@ import { awaitableTankDefinitions } from '../../core/awaitables/tankDefinitions' import { awaitableTankNames } from '../../core/awaitables/tankNames'; import { filterTank } from '../../core/blitzkit/filterTank'; import { resolveReload } from '../../core/blitzkit/resolveReload'; +import { literals } from '../../core/i18n/literals'; +import { useLocale } from '../../hooks/useLocale'; import { $tankFilters } from '../../stores/tankFilters'; import { TankopediaPersistent } from '../../stores/tankopediaPersistent'; -import { - SORT_NAMES, - SORT_UNITS, -} from '../../stores/tankopediaPersistent/constants'; +import { SORT_UNITS } from '../../stores/tankopediaPersistent/constants'; import { $tankopediaSort } from '../../stores/tankopediaSort'; import { ExperimentIcon } from '../ExperimentIcon'; import { TankSearchCard } from './components/Card'; @@ -52,6 +51,7 @@ const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] = export const TankSearch = memo( ({ compact, onSelect, onSelectAll, ...props }) => { + const { strings, locale } = useLocale(); const mutateTankopediaPersistent = TankopediaPersistent.useMutation(); const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks); const tankFilters = useStore($tankFilters); @@ -438,8 +438,11 @@ export const TankSearch = memo( - {tanksFiltered.length} tank - {tanksFiltered.length === 1 ? '' : 's'} + {tanksFiltered.length === 1 + ? strings.website.common.tank_search.count_singular + : literals(strings.website.common.tank_search.count_plural, [ + `${tanksFiltered.length.toLocaleString(locale)}`, + ])} {onSelectAll && ( @@ -457,14 +460,16 @@ export const TankSearch = memo( }); }} > - Select all + {strings.website.common.tank_search.select_all} )} {tankopediaSort.by !== 'meta.none' && ( - Sorting by {SORT_NAMES[tankopediaSort.by]} + {literals(strings.website.common.tank_search.sorting_by, [ + strings.website.common.tank_search.sort[tankopediaSort.by], + ])} {SORT_UNITS[tankopediaSort.by] === undefined ? '' : ` (${SORT_UNITS[tankopediaSort.by]})`} @@ -480,8 +485,7 @@ export const TankSearch = memo( - Tanks in testing are subject to change and may not represent the - final product. + {strings.website.common.warnings.test} diff --git a/packages/website/src/pages/[...locale]/tools/compare/_index.tsx b/packages/website/src/pages/[...locale]/tools/compare/_index.tsx index a8d74a7cd..59d3106a8 100644 --- a/packages/website/src/pages/[...locale]/tools/compare/_index.tsx +++ b/packages/website/src/pages/[...locale]/tools/compare/_index.tsx @@ -1,5 +1,4 @@ import { createDefaultSkills } from '@blitzkit/core'; -import { PlusIcon } from '@radix-ui/react-icons'; import { Flex, Heading, Text } from '@radix-ui/themes'; import { useEffect, useMemo, useState } from 'react'; import { CompareTable } from '../../../../components/Compare/CompareTable'; @@ -13,6 +12,11 @@ import { awaitableSkillDefinitions } from '../../../../core/awaitables/skillDefi import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions'; import { tankCharacteristics } from '../../../../core/blitzkit/tankCharacteristics'; import { tankToCompareMember } from '../../../../core/blitzkit/tankToCompareMember'; +import { + LocaleProvider, + useLocale, + type LocaleAcceptorProps, +} from '../../../../hooks/useLocale'; import { App } from '../../../../stores/app'; import { CompareEphemeral } from '../../../../stores/compareEphemeral'; import { ComparePersistent } from '../../../../stores/comparePersistent'; @@ -32,19 +36,21 @@ const [ awaitableTankDefinitions, ]); -export function Page() { +export function Page({ locale }: LocaleAcceptorProps) { return ( - - - - - - - - - + + + + + + + + + + + ); } @@ -52,6 +58,7 @@ function Content() { const members = CompareEphemeral.use((state) => state.members); const [addTankDialogOpen, setAddTankDialogOpen] = useState(false); const crewSkills = CompareEphemeral.use((state) => state.crewSkills); + const { strings } = useLocale(); const stats = useMemo[]>( () => members.map((thisMember) => @@ -136,19 +143,6 @@ function Content() { {members.length > 0 && ( - - {/* setAddTankDialogOpen(true)} - > - - */} )} @@ -159,9 +153,11 @@ function Content() { direction="column" style={{ flex: 1 }} > - No tanks selected + + {strings.website.tools.compare.no_tanks.title} + - Press the Add button to get started + {strings.website.tools.compare.no_tanks.body} )} diff --git a/packages/website/src/pages/[...locale]/tools/compare/index.astro b/packages/website/src/pages/[...locale]/tools/compare/index.astro index 8c28f897f..6cf98cffd 100644 --- a/packages/website/src/pages/[...locale]/tools/compare/index.astro +++ b/packages/website/src/pages/[...locale]/tools/compare/index.astro @@ -5,10 +5,12 @@ import BaseLayout from '../../../../layouts/BaseLayout.astro'; import { Page } from './_index'; export { getStaticPaths } from '../../index.astro'; + +const { locale } = Astro.params; --- - + diff --git a/packages/website/src/stores/tankopediaPersistent/constants.ts b/packages/website/src/stores/tankopediaPersistent/constants.ts index f06f1d624..49bc4bda1 100644 --- a/packages/website/src/stores/tankopediaPersistent/constants.ts +++ b/packages/website/src/stores/tankopediaPersistent/constants.ts @@ -32,38 +32,7 @@ export const SORT_UNITS: Record = { 'survivability.viewRange': 'm', 'survivability.volume': 'm^3', }; -export const SORT_NAMES = { - 'meta.none': 'none', - 'survivability.health': 'health', - 'survivability.viewRange': 'view range', - 'survivability.camouflageStill': 'camouflage still', - 'survivability.camouflageMoving': 'camouflage moving', - 'survivability.camouflageShooting': 'camouflage shooting', - 'survivability.volume': 'volume', - 'survivability.length': 'length', - 'fire.dpm': 'standard DPM', - 'fire.dpmPremium': 'premium DPM', - 'fire.reload': 'reload', - 'fire.caliber': 'caliber', - 'fire.standardPenetration': 'standard penetration', - 'fire.premiumPenetration': 'premium penetration', - 'fire.damage': 'damage', - 'fire.moduleDamage': 'module damage', - 'fire.shellVelocity': 'shell velocity', - 'fire.shellRange': 'shell range', - 'fire.shellCapacity': 'shell capacity', - 'fire.aimTime': 'aim time', - 'fire.dispersionStill': 'dispersion still', - 'fire.dispersionMoving': 'dispersion moving', - 'fire.gunDepression': 'gun depression', - 'fire.gunElevation': 'gun elevation', - 'maneuverability.forwardsSpeed': 'forwards speed', - 'maneuverability.backwardsSpeed': 'backwards speed', - 'maneuverability.power': 'power', - 'maneuverability.powerToWeight': 'power to weight', - 'maneuverability.weight': 'weight', - 'maneuverability.traverseSpeed': 'traverse speed', -} as const; + export enum TankopediaDisplay { Model, DynamicArmor, diff --git a/packages/website/src/stores/tankopediaPersistent/index.ts b/packages/website/src/stores/tankopediaPersistent/index.ts index 26807b389..06ed651be 100644 --- a/packages/website/src/stores/tankopediaPersistent/index.ts +++ b/packages/website/src/stores/tankopediaPersistent/index.ts @@ -1,10 +1,10 @@ +import type en from '@blitzkit/core/lang/en.json'; import * as lodash from 'lodash-es'; import { create } from 'zustand'; import { persist, subscribeWithSelector } from 'zustand/middleware'; import { createContextualStore } from '../../core/zustand/createContextualStore'; -import { SORT_NAMES } from './constants'; -export type TankopediaSortBy = keyof typeof SORT_NAMES; +export type TankopediaSortBy = keyof typeof en.website.common.tank_search.sort; export type TankopediaSortDirection = 'ascending' | 'descending'; export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';