diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4c05ed9..0560e9d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -139,8 +139,9 @@ export default function App() { window.history.replaceState({}, '', newUrl); trackEvent('Purchase'); setShowLicenseSuccess(true); - refreshAuth(); } + // Always refresh auth on startup to pick up server-side subscription changes + refreshAuth().catch(() => {}); }, []); // eslint-disable-line react-hooks/exhaustive-deps const savedSearches = useSavedSearches(user?.id ?? null); @@ -419,6 +420,8 @@ export default function App() { isPropertySaved={user ? savedProperties.isPropertySaved : undefined} getSavedPropertyId={user ? savedProperties.getSavedPropertyId : undefined} deferTutorial={showLicenseSuccess} + onSaveSearch={user ? savedSearches.saveSearch : undefined} + savingSearch={savedSearches.saving} /> )} {showAuthModal && ( diff --git a/frontend/src/components/home/HomePage.tsx b/frontend/src/components/home/HomePage.tsx index 7fe5928..7af103b 100644 --- a/frontend/src/components/home/HomePage.tsx +++ b/frontend/src/components/home/HomePage.tsx @@ -58,7 +58,7 @@ export default function HomePage({ }, []); return ( -
+
{/* Hero */}
@@ -78,7 +78,7 @@ export default function HomePage({

{t('home.heroDescription')}

-
+
-
+
diff --git a/frontend/src/components/map/Filters.tsx b/frontend/src/components/map/Filters.tsx index 233fa4f..b8eb746 100644 --- a/frontend/src/components/map/Filters.tsx +++ b/frontend/src/components/map/Filters.tsx @@ -2,7 +2,7 @@ import { Fragment, memo, useState, useMemo, useRef, useCallback, useEffect } fro import { useTranslation } from 'react-i18next'; import { ts } from '../../i18n/server'; import { Slider } from '../ui/Slider'; -import { ChevronIcon, LightbulbIcon } from '../ui/icons'; +import { ChevronIcon, CloseIcon, LightbulbIcon, SpinnerIcon } from '../ui/icons'; import { PillToggle } from '../ui/PillToggle'; import { PillGroup } from '../ui/PillGroup'; @@ -203,6 +203,9 @@ interface FiltersProps { onUpgradeClick?: () => void; onResetTutorial?: () => void; filterImpacts?: Record; + onClearAll: () => void; + onSaveSearch?: (name: string) => Promise; + savingSearch?: boolean; } export default memo(function Filters({ @@ -242,6 +245,9 @@ export default memo(function Filters({ onUpgradeClick, onResetTutorial, filterImpacts, + onClearAll, + onSaveSearch, + savingSearch, }: FiltersProps) { const { t } = useTranslation(); const modeRestrictions = useMemo(() => { @@ -416,6 +422,50 @@ export default memo(function Filters({ const badgeCount = enabledFeatureList.length + activeEntryCount; + const [showClearPopup, setShowClearPopup] = useState(false); + const [clearSaveName, setClearSaveName] = useState(''); + const [clearSaveError, setClearSaveError] = useState(null); + + const handleClearAllClick = useCallback(() => { + if (badgeCount === 0) return; + if (onSaveSearch) { + setShowClearPopup(true); + setClearSaveName(''); + setClearSaveError(null); + } else { + onClearAll(); + } + }, [badgeCount, onSaveSearch, onClearAll]); + + const handleSaveAndClear = useCallback( + async (e: React.FormEvent) => { + e.preventDefault(); + if (!clearSaveName.trim() || savingSearch) return; + try { + await onSaveSearch!(clearSaveName.trim()); + setShowClearPopup(false); + onClearAll(); + } catch { + setClearSaveError(t('saveSearch.saving')); + } + }, + [clearSaveName, savingSearch, onSaveSearch, onClearAll, t] + ); + + const handleClearWithoutSaving = useCallback(() => { + setShowClearPopup(false); + onClearAll(); + }, [onClearAll]); + + useEffect(() => { + if (!showClearPopup) return; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') setShowClearPopup(false); + }; + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [showClearPopup]); + return (
- +
+ {badgeCount > 0 && ( + { + e.stopPropagation(); + handleClearAllClick(); + }} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.stopPropagation(); + handleClearAllClick(); + } + }} + className="text-xs text-warm-500 dark:text-warm-400 hover:text-warm-700 dark:hover:text-warm-200 underline" + > + {t('filters.clearAll')} + + )} + +
{!activeFilterCollapsed &&
@@ -521,6 +591,7 @@ export default memo(function Filters({ onDragEnd={() => onTravelTimeDragEnd(index)} onToggleBest={() => onTravelTimeToggleBest(index)} onRemove={() => onTravelTimeRemoveEntry(index)} + filterImpact={filterImpacts?.[travelFieldKey(entry)]} />
))} @@ -618,6 +689,7 @@ export default memo(function Filters({ onDragEnd={() => onTravelTimeDragEnd(index)} onToggleBest={() => onTravelTimeToggleBest(index)} onRemove={() => onTravelTimeRemoveEntry(index)} + filterImpact={filterImpacts?.[travelFieldKey(entry)]} />
))} @@ -625,7 +697,7 @@ export default memo(function Filters({ data-filter-name={feature.name} className={`space-y-0.5 px-2 py-1.5 rounded ${isActive ? 'ring-2 ring-teal-400 bg-teal-50 dark:bg-teal-900/30' : isPinned ? 'ring-2 ring-teal-400 bg-teal-50/50 dark:bg-teal-900/20' : ''}`} > -
+
onTravelTimeDragEnd(index)} onToggleBest={() => onTravelTimeToggleBest(index)} onRemove={() => onTravelTimeRemoveEntry(index)} + filterImpact={filterImpacts?.[travelFieldKey(entry)]} />
))} @@ -715,8 +788,7 @@ export default memo(function Filters({
{!addFilterCollapsed && ( -
+
)} + + {showClearPopup && ( +
setShowClearPopup(false)}> +
+
e.stopPropagation()} + > +
+

+ {t('filters.clearAllTitle')} +

+ +
+
+

+ {t('filters.clearAllSavePrompt')} +

+
+ setClearSaveName(e.target.value)} + className="w-full px-3 py-2 text-sm rounded border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-800 text-navy-950 dark:text-white placeholder-warm-400 dark:placeholder-warm-500 outline-none focus:ring-2 ring-teal-400 dark:ring-teal-500" + placeholder={t('saveSearch.namePlaceholder')} + autoFocus + /> +
+ {clearSaveError && ( +

{clearSaveError}

+ )} +
+ + +
+
+
+
+ )}
); }); diff --git a/frontend/src/components/map/LocationSearch.tsx b/frontend/src/components/map/LocationSearch.tsx index 9259d3c..f23c404 100644 --- a/frontend/src/components/map/LocationSearch.tsx +++ b/frontend/src/components/map/LocationSearch.tsx @@ -1,9 +1,11 @@ import { useState, useCallback, useRef, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import type { PostcodeGeometry } from '../../types'; import { authHeaders } from '../../lib/api'; import { useIsMobile } from '../../hooks/useIsMobile'; import { useLocationSearch, type SearchResult } from '../../hooks/useLocationSearch'; import { PlaceSearchInput } from '../ui/PlaceSearchInput'; +import { LocateIcon } from '../ui/icons/LocateIcon'; import { SearchIcon } from '../ui/icons/SearchIcon'; export interface SearchedLocation { @@ -14,6 +16,7 @@ export interface SearchedLocation { const ZOOM_FOR_TYPE: Record = { city: 10, borough: 12, + outcode: 14, town: 13, suburb: 14, quarter: 14, @@ -35,6 +38,7 @@ export default function LocationSearch({ onLocationSearched?: (postcode: SearchedLocation | null) => void; onMouseEnter?: () => void; }) { + const { t } = useTranslation(); const search = useLocationSearch(); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); @@ -80,7 +84,7 @@ export default function LocationSearch({ try { const res = await fetch(`/api/postcode/${encodeURIComponent(result.label)}`, authHeaders()); if (!res.ok) { - setError('Postcode not found'); + setError(t('locationSearch.postcodeNotFound')); return; } const json: { @@ -94,7 +98,7 @@ export default function LocationSearch({ search.clear(); if (isMobile) setExpanded(false); } catch { - setError('Lookup failed'); + setError(t('locationSearch.lookupFailed')); } finally { setLoading(false); } @@ -102,17 +106,71 @@ export default function LocationSearch({ [onFlyTo, onLocationSearched, isMobile, search] ); - // Mobile collapsed state: just a search icon button + const [locating, setLocating] = useState(false); + + const locateUser = useCallback(async () => { + if (!navigator.geolocation) { + setError(t('locationSearch.geolocationUnsupported')); + return; + } + setError(null); + setLocating(true); + search.close(); + try { + const position = await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition(resolve, reject, { + enableHighAccuracy: true, + timeout: 10000, + }); + }); + const { latitude, longitude } = position.coords; + const res = await fetch( + `/api/nearest-postcode?lat=${latitude}&lng=${longitude}`, + authHeaders() + ); + if (!res.ok) { + setError(t('locationSearch.lookupFailed')); + return; + } + const json: { + postcode: string; + latitude: number; + longitude: number; + geometry: PostcodeGeometry; + } = await res.json(); + onFlyTo(json.latitude, json.longitude, 16); + onLocationSearched?.({ postcode: json.postcode, geometry: json.geometry }); + search.clear(); + if (isMobile) setExpanded(false); + } catch { + setError(t('locationSearch.geolocationFailed')); + } finally { + setLocating(false); + } + }, [onFlyTo, onLocationSearched, isMobile, search, t]); + + // Mobile collapsed state: search icon + locate button if (isMobile && !expanded) { return ( - +
+ + +
); } @@ -129,12 +187,22 @@ export default function LocationSearch({ search={search} onSelect={selectResult} loading={loading} - placeholder="Search places or postcodes..." + placeholder={t('locationSearch.placeholder')} size="sm" inputClassName="px-2 py-2 text-sm w-56 border-none outline-none bg-transparent text-warm-700 dark:text-warm-200 placeholder-warm-400 dark:placeholder-warm-500" inputRef={inputRef} onInputChange={() => setError(null)} /> +
{error && ( diff --git a/frontend/src/components/map/Map.tsx b/frontend/src/components/map/Map.tsx index e4c4d53..27fcf49 100644 --- a/frontend/src/components/map/Map.tsx +++ b/frontend/src/components/map/Map.tsx @@ -30,6 +30,7 @@ import { CloseIcon } from '../ui/icons/CloseIcon'; import type { FeatureFilters } from '../../types'; import { useDeckLayers } from '../../hooks/useDeckLayers'; import { useTranslatedModes, type TravelTimeEntry } from '../../hooks/useTravelTime'; +import { ts } from '../../i18n/server'; interface MapProps { data: HexagonData[]; @@ -59,6 +60,7 @@ interface MapProps { hideLegend?: boolean; travelTimeEntries?: TravelTimeEntry[]; densityLabel?: string; + totalCount?: number; } const EMPTY_TRAVEL_ENTRIES: TravelTimeEntry[] = []; @@ -115,11 +117,13 @@ export default memo(function Map({ bounds: viewportBounds, hideLegend = false, travelTimeEntries = EMPTY_TRAVEL_ENTRIES, - densityLabel = 'Number of properties', + densityLabel: densityLabelProp, + totalCount: totalCountProp, }: MapProps) { const containerRef = useRef(null); const { t } = useTranslation(); const modes = useTranslatedModes(); + const densityLabel = densityLabelProp ?? t('mapLegend.numberOfProperties'); const [internalViewState, setInternalViewState] = useState( initialViewState || INITIAL_VIEW_STATE ); @@ -288,8 +292,8 @@ export default memo(function Map({ void; onToggleBest: () => void; onRemove: () => void; + filterImpact?: number; } export function TravelTimeCard({ @@ -48,7 +50,10 @@ export function TravelTimeCard({ onDragEnd, onToggleBest, onRemove, + filterImpact, }: TravelTimeCardProps) { + const { t } = useTranslation(); + const modes = useTranslatedModes(); const { destinations, loading: destinationsLoading } = useTravelDestinations(mode); const [showInfo, setShowInfo] = useState(false); const [showBestInfo, setShowBestInfo] = useState(false); @@ -75,23 +80,23 @@ export function TravelTimeCard({
- Travel Time ({MODE_LABELS[mode]}) + {t('travel.travelTime', { mode: modes.label(mode) })}
- setShowInfo(true)} title="Feature info"> + setShowInfo(true)} title={t('filters.featureInfo')}> {slug && ( )} - onRemove()} title="Remove travel time"> + onRemove()} title={t('travel.removeTravelTime')}>
@@ -104,14 +109,14 @@ export function TravelTimeCard({ onSelect={handleDestinationSelect} value={label || undefined} onClear={() => onSetDestination('', '', 0, 0)} - placeholder="Select destination..." + placeholder={t('travel.selectDestination')} /> {/* Best-case toggle — transit only, shown when destination is set */} {slug && mode === 'transit' && (
- - setShowBestInfo(true)} title="What is best case?"> + + setShowBestInfo(true)} title={t('travel.bestCaseTitle')}>
@@ -120,12 +125,11 @@ export function TravelTimeCard({ {showInfo && setShowInfo(false)} />} {showBestInfo && ( - setShowBestInfo(false)}> -

- Uses the fastest realistic journey time (if you time your departure well and catch good - connections). The default uses the median, representing a typical journey - regardless of when you leave. -

+ setShowBestInfo(false)}> +

)} @@ -133,7 +137,7 @@ export function TravelTimeCard({ {slug && (

- Max time + {t('travel.maxTime')} onDragEnd()} />
- {formatFilterValue(displayRange[0])} min - {formatFilterValue(displayRange[1])} min + {formatFilterValue(displayRange[0])} {t('common.min')} + {formatFilterValue(displayRange[1])} {t('common.min')}
+ {filterImpact != null && filterImpact > 0 && ( +

+ +{formatNumber(filterImpact)} without this filter +

+ )}
)}
diff --git a/frontend/src/components/ui/LanguageDropdown.tsx b/frontend/src/components/ui/LanguageDropdown.tsx new file mode 100644 index 0000000..11923e4 --- /dev/null +++ b/frontend/src/components/ui/LanguageDropdown.tsx @@ -0,0 +1,60 @@ +import { useState, useRef, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { SUPPORTED_LANGUAGES, type LanguageCode } from '../../i18n'; + +export default function LanguageDropdown() { + const { i18n } = useTranslation(); + const [open, setOpen] = useState(false); + const ref = useRef(null); + + const current = SUPPORTED_LANGUAGES.find((l) => l.code === i18n.language) ?? SUPPORTED_LANGUAGES[0]; + + useEffect(() => { + if (!open) return; + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); + }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, [open]); + + const changeLanguage = (code: LanguageCode) => { + i18n.changeLanguage(code); + localStorage.setItem('language', code); + setOpen(false); + }; + + return ( +
+ + + {open && ( +
+ {SUPPORTED_LANGUAGES.map((lang) => ( + + ))} +
+ )} +
+ ); +} diff --git a/frontend/src/components/ui/Slider.tsx b/frontend/src/components/ui/Slider.tsx index 1404b05..60c6c47 100644 --- a/frontend/src/components/ui/Slider.tsx +++ b/frontend/src/components/ui/Slider.tsx @@ -16,7 +16,7 @@ export function Slider({ className, ...props }: SliderProps) { {props.value?.map((_, i) => ( ))} diff --git a/frontend/src/hooks/useAiFilters.ts b/frontend/src/hooks/useAiFilters.ts index e945e4c..3bc3d65 100644 --- a/frontend/src/hooks/useAiFilters.ts +++ b/frontend/src/hooks/useAiFilters.ts @@ -17,8 +17,6 @@ export interface AiFiltersResult { notes: string; /** Human-readable summary of what was set */ summary: string; - /** The listing mode used (historical/buy/rent) */ - listingType: string; /** Number of properties matching the proposed filters (excludes travel time) */ matchCount: number; } @@ -34,8 +32,7 @@ export interface AiFiltersContext { interface UseAiFiltersResult { fetchAiFilters: ( query: string, - context?: AiFiltersContext, - listingType?: string + context?: AiFiltersContext ) => Promise; loading: boolean; error: string | null; @@ -88,8 +85,7 @@ export function useAiFilters(): UseAiFiltersResult { const fetchAiFilters = useCallback( async ( query: string, - context?: AiFiltersContext, - listingType?: string + context?: AiFiltersContext ): Promise => { abortRef.current?.abort(); const controller = new AbortController(); @@ -104,7 +100,6 @@ export function useAiFilters(): UseAiFiltersResult { try { const url = apiUrl('ai-filters'); const bodyObj: Record = { query }; - if (listingType) bodyObj.listing_type = listingType; if (context) { bodyObj.context = { filters: context.filters, @@ -155,7 +150,6 @@ export function useAiFilters(): UseAiFiltersResult { travelTimeFilters, notes: json.notes || '', summary: summaryText, - listingType: json.listing_type || 'historical', matchCount, }; setNotes(result.notes || null); diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts index 67976fe..0c194fb 100644 --- a/frontend/src/hooks/useAuth.ts +++ b/frontend/src/hooks/useAuth.ts @@ -126,8 +126,10 @@ export function useAuth() { const result = await pb.collection('users').authRefresh(); setUser(recordToUser(result.record)); } catch (err) { - const msg = err instanceof Error ? err.message : 'Auth refresh failed'; - setError(msg); + // Token is invalid/expired — clear auth state but don't set error, + // since this is a background refresh, not a user-initiated action + pb.authStore.clear(); + setUser(null); throw err; } finally { setLoading(false); diff --git a/frontend/src/hooks/useFilterCounts.ts b/frontend/src/hooks/useFilterCounts.ts new file mode 100644 index 0000000..553679a --- /dev/null +++ b/frontend/src/hooks/useFilterCounts.ts @@ -0,0 +1,93 @@ +import { useState, useEffect, useRef, useMemo } from 'react'; +import type { FeatureMeta, FeatureFilters, Bounds } from '../types'; +import { apiUrl, buildFilterString, logNonAbortError, authHeaders, isAbortError } from '../lib/api'; +import type { TravelTimeEntry } from './useTravelTime'; + +const DEBOUNCE_MS = 400; + +interface FilterCountsResponse { + total: number; + impacts: Record; +} + +/** + * Fetches per-filter marginal impact counts: for each active filter, + * how many more properties would be visible if that filter were removed. + */ +export function useFilterCounts( + filters: FeatureFilters, + features: FeatureMeta[], + bounds: Bounds | null, + travelTimeEntries: TravelTimeEntry[] +) { + const [impacts, setImpacts] = useState>({}); + const [total, setTotal] = useState(0); + const debounceRef = useRef | null>(null); + const abortRef = useRef(null); + + // Build the travel param string (same format as useMapData) + const travelParam = useMemo(() => { + const segments: string[] = []; + for (const entry of travelTimeEntries) { + if (!entry.slug) continue; + let seg = `${entry.mode}:${entry.slug}`; + if (entry.useBest) seg += ':best'; + if (entry.timeRange) seg += `:${entry.timeRange[0]}:${entry.timeRange[1]}`; + segments.push(seg); + } + return segments.join('|'); + }, [travelTimeEntries]); + + useEffect(() => { + if (!bounds) return; + + const filterCount = Object.keys(filters).length; + const hasTravelFilters = travelTimeEntries.some((e) => e.slug && e.timeRange); + if (filterCount === 0 && !hasTravelFilters) { + setImpacts({}); + setTotal(0); + return; + } + + if (debounceRef.current) clearTimeout(debounceRef.current); + + debounceRef.current = setTimeout(async () => { + if (abortRef.current) abortRef.current.abort(); + abortRef.current = new AbortController(); + + try { + const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`; + const filtersStr = buildFilterString(filters, features); + const params = new URLSearchParams({ bounds: boundsStr }); + if (filtersStr) params.set('filters', filtersStr); + if (travelParam) params.set('travel', travelParam); + + const res = await fetch( + apiUrl('filter-counts', params), + authHeaders({ signal: abortRef.current!.signal }) + ); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const json: FilterCountsResponse = await res.json(); + setImpacts(json.impacts); + setTotal(json.total); + } catch (err) { + if (!isAbortError(err)) { + logNonAbortError('Failed to fetch filter counts', err); + } + } + }, DEBOUNCE_MS); + + return () => { + if (debounceRef.current) clearTimeout(debounceRef.current); + }; + }, [filters, features, bounds, travelParam, travelTimeEntries]); + + // Cancel in-flight on unmount + useEffect(() => { + return () => { + abortRef.current?.abort(); + }; + }, []); + + return { impacts, total }; +} diff --git a/frontend/src/hooks/useHexagonSelection.ts b/frontend/src/hooks/useHexagonSelection.ts index a29976a..8254c9c 100644 --- a/frontend/src/hooks/useHexagonSelection.ts +++ b/frontend/src/hooks/useHexagonSelection.ts @@ -266,6 +266,14 @@ export function useHexagonSelection({ setSelectedPostcodeGeometry(null); } else { setAreaStats(stats); + // Re-fetch properties if the properties tab is active + if (rightPaneTab === 'properties') { + if (selectedHexagon.type === 'postcode') { + fetchPostcodeProperties(selectedHexagon.id, 0); + } else { + fetchHexagonProperties(selectedHexagon.id, selectedHexagon.resolution, 0); + } + } } }) .catch((error) => { @@ -279,7 +287,7 @@ export function useHexagonSelection({ return () => { cancelled = true; }; - }, [filterStr, selectedHexagon, fetchHexagonStats, fetchPostcodeStats]); + }, [filterStr, selectedHexagon, fetchHexagonStats, fetchPostcodeStats, rightPaneTab, fetchHexagonProperties, fetchPostcodeProperties]); const handleLocationSearch = useCallback( (postcode: string, geometry: PostcodeGeometry) => { diff --git a/frontend/src/hooks/useLocationSearch.ts b/frontend/src/hooks/useLocationSearch.ts index b9c5a83..d9d5688 100644 --- a/frontend/src/hooks/useLocationSearch.ts +++ b/frontend/src/hooks/useLocationSearch.ts @@ -2,10 +2,12 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import type { PlaceResult } from '../types'; import { authHeaders, logNonAbortError } from '../lib/api'; -const POSTCODE_RE = /^[A-Z]{1,2}\d[A-Z\d]?\s*\d?[A-Z]{0,2}$/i; +/** Matches a full UK postcode with complete inward code (e.g. "E14 2DG", "SW1A1AA"). + * Outcodes like "E14" or "SW1A" intentionally do NOT match — they go through /api/places instead. */ +const FULL_POSTCODE_RE = /^[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}$/i; function looksLikePostcode(s: string) { - return POSTCODE_RE.test(s.trim()); + return FULL_POSTCODE_RE.test(s.trim()); } /** Normalize a UK postcode: uppercase, strip spaces, insert canonical space before inward code. */ @@ -83,7 +85,7 @@ export function useLocationSearch(mode?: string) { place_type: p.place_type, lat: p.lat, lon: p.lon, - city: p.city, + city: p.city === 'City of London' ? 'London' : p.city, })); setResults(placeResults); setOpen(placeResults.length > 0); diff --git a/frontend/src/hooks/usePaneResize.ts b/frontend/src/hooks/usePaneResize.ts index 57ab46f..c306a07 100644 --- a/frontend/src/hooks/usePaneResize.ts +++ b/frontend/src/hooks/usePaneResize.ts @@ -7,43 +7,87 @@ interface PaneResizeHandlers { } export function usePaneResize( - initialWidth: number, - minWidth: number, - maxWidth: number, - side: 'left' | 'right' -): [number, PaneResizeHandlers] { - const [width, setWidth] = useState(initialWidth); + initialSize: number, + minSize: number, + maxSize: number, + side: 'left' | 'right' | 'top' | 'bottom' +): [number, PaneResizeHandlers, React.RefCallback] { + const [size, setSize] = useState(initialSize); const draggingRef = useRef(false); + const liveSizeRef = useRef(initialSize); + const targetRef = useRef(null); + const containerOffsetRef = useRef(0); + const containerSizeRef = useRef(0); - const handlePointerDown = useCallback((e: React.PointerEvent) => { - e.preventDefault(); - (e.target as HTMLElement).setPointerCapture(e.pointerId); - draggingRef.current = true; + const isVertical = side === 'top' || side === 'bottom'; + const styleProp = isVertical ? 'height' : 'width'; + + const targetCallbackRef = useCallback((el: HTMLElement | null) => { + targetRef.current = el; }, []); + const computeSize = useCallback( + (e: React.PointerEvent): number => { + if (isVertical) { + const total = containerSizeRef.current || window.innerHeight; + const resolvedMax = maxSize <= 1 ? total * maxSize : maxSize; + const pos = e.clientY - containerOffsetRef.current; + return side === 'top' + ? Math.min(resolvedMax, Math.max(minSize, pos)) + : Math.min(resolvedMax, Math.max(minSize, total - pos)); + } else { + const resolvedMax = maxSize <= 1 ? window.innerWidth * maxSize : maxSize; + return side === 'left' + ? Math.min(resolvedMax, Math.max(minSize, e.clientX)) + : Math.min(resolvedMax, Math.max(minSize, window.innerWidth - e.clientX)); + } + }, + [side, isVertical, minSize, maxSize] + ); + + const handlePointerDown = useCallback( + (e: React.PointerEvent) => { + e.preventDefault(); + (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); + draggingRef.current = true; + if (isVertical) { + const container = (e.currentTarget as HTMLElement).parentElement; + if (container) { + const rect = container.getBoundingClientRect(); + containerOffsetRef.current = rect.top; + containerSizeRef.current = rect.height; + } + } + }, + [isVertical] + ); + const handlePointerMove = useCallback( (e: React.PointerEvent) => { if (!draggingRef.current) return; - const resolvedMax = maxWidth <= 1 ? window.innerWidth * maxWidth : maxWidth; - const newWidth = - side === 'left' - ? Math.min(resolvedMax, Math.max(minWidth, e.clientX)) - : Math.min(resolvedMax, Math.max(minWidth, window.innerWidth - e.clientX)); - setWidth(newWidth); + const newSize = computeSize(e); + liveSizeRef.current = newSize; + if (targetRef.current) { + targetRef.current.style[styleProp] = `${newSize}px`; + } else { + setSize(newSize); + } }, - [side, minWidth, maxWidth] + [computeSize, styleProp] ); const handlePointerUp = useCallback(() => { draggingRef.current = false; + setSize(liveSizeRef.current); }, []); return [ - width, + size, { onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, }, + targetCallbackRef, ]; } diff --git a/frontend/src/i18n/descriptions.ts b/frontend/src/i18n/descriptions.ts index eb99d55..c7d5c67 100644 --- a/frontend/src/i18n/descriptions.ts +++ b/frontend/src/i18n/descriptions.ts @@ -13,275 +13,271 @@ import i18n from 'i18next'; const descriptions: Record> = { fr: { 'Listing status': 'Indique si le bien provient de ventes historiques, est en vente ou en location', - 'Property type': 'Type de bien : individuel, jumel\u00E9, mitoyen, appartement ou autre', - 'Leasehold/Freehold': 'Indique si le bien est en bail ou en pleine propri\u00E9t\u00E9', - 'Last known price': 'Dernier prix de vente enregistr\u00E9 au Land Registry', - 'Estimated current price': 'Estimation du prix actuel ajust\u00E9 \u00E0 l\u2019inflation', - 'Asking price': 'Prix demand\u00E9 pour les biens actuellement en vente', - 'Price per sqm': 'Prix de vente divis\u00E9 par la surface totale', - 'Est. price per sqm': 'Prix actuel estim\u00E9 divis\u00E9 par la surface totale', - 'Asking price per sqm': 'Prix demand\u00E9 divis\u00E9 par la surface totale', - 'Estimated monthly rent': 'Loyer mensuel priv\u00E9 m\u00E9dian pour le secteur', - 'Asking rent (monthly)': 'Loyer mensuel affich\u00E9 pour les biens en location', - 'Total floor area (sqm)': 'Surface int\u00E9rieure issue du diagnostic EPC', - 'Number of bedrooms & living rooms': 'Nombre de pi\u00E8ces habitables selon le diagnostic EPC', - 'Bedrooms': 'Nombre de chambres selon l\u2019annonce en ligne', - 'Bathrooms': 'Nombre de salles de bain selon l\u2019annonce en ligne', - 'Construction year': 'Ann\u00E9e de construction estim\u00E9e selon l\u2019EPC', - 'Date of last transaction': 'Date de la derni\u00E8re vente enregistr\u00E9e au Land Registry', - 'Listing date': 'Date de premi\u00E8re mise en ligne du bien', - 'Former council house': 'Indique si le bien a \u00E9t\u00E9 r\u00E9pertori\u00E9 comme logement social', - 'Current energy rating': 'Classement \u00E9nerg\u00E9tique EPC actuel (A = meilleur, G = pire)', - 'Potential energy rating': 'Classement EPC potentiel si toutes les am\u00E9liorations recommand\u00E9es \u00E9taient r\u00E9alis\u00E9es', - 'Interior height (m)': 'Hauteur moyenne d\u2019\u00E9tage selon le diagnostic EPC', - 'Distance to nearest train or tube station (km)': 'Distance \u00E0 la gare ou station de m\u00E9tro la plus proche', - 'Train or tube stations within 1km': 'Nombre de gares ou stations de m\u00E9tro \u00E0 moins d\u20191 km', - 'Good+ primary schools within 2km': '\u00C9coles primaires not\u00E9es Bien ou Excellent par Ofsted dans un rayon de 2 km', - 'Good+ secondary schools within 2km': 'Coll\u00E8ges/lyc\u00E9es not\u00E9s Bien ou Excellent par Ofsted dans un rayon de 2 km', - 'Good+ primary schools within 5km': '\u00C9coles primaires not\u00E9es Bien ou Excellent par Ofsted dans un rayon de 5 km', - 'Good+ secondary schools within 5km': 'Coll\u00E8ges/lyc\u00E9es not\u00E9s Bien ou Excellent par Ofsted dans un rayon de 5 km', - 'Education, Skills and Training Score': 'Score de qualit\u00E9 \u00E9ducative du secteur (plus \u00E9lev\u00E9 = meilleur)', - 'Income Score (rate)': 'Taux de pr\u00E9carit\u00E9 de revenu, invers\u00E9 (plus \u00E9lev\u00E9 = moins pr\u00E9caire)', - 'Employment Score (rate)': 'Taux de pr\u00E9carit\u00E9 d\u2019emploi, invers\u00E9 (plus \u00E9lev\u00E9 = moins pr\u00E9caire)', - 'Health Deprivation and Disability Score': 'Score de sant\u00E9 et handicap (plus \u00E9lev\u00E9 = meilleurs r\u00E9sultats)', - 'Living Environment Score': 'Qualit\u00E9 de l\u2019environnement int\u00E9rieur et ext\u00E9rieur (plus \u00E9lev\u00E9 = meilleur)', - 'Indoors Sub-domain Score': 'Qualit\u00E9 et \u00E9tat du logement (plus \u00E9lev\u00E9 = meilleur)', - 'Outdoors Sub-domain Score': 'Qualit\u00E9 de l\u2019air et s\u00E9curit\u00E9 routi\u00E8re (plus \u00E9lev\u00E9 = meilleur)', + 'Property type': 'Type de bien : individuel, jumelé, mitoyen, appartement ou autre', + 'Leasehold/Freehold': 'Indique si le bien est en bail ou en pleine propriété', + 'Last known price': 'Dernier prix de vente enregistré au Land Registry', + 'Estimated current price': 'Estimation du prix actuel ajusté à l’inflation', + 'Asking price': 'Prix demandé pour les biens actuellement en vente', + 'Price per sqm': 'Prix de vente divisé par la surface totale', + 'Est. price per sqm': 'Prix actuel estimé divisé par la surface totale', + 'Asking price per sqm': 'Prix demandé divisé par la surface totale', + 'Estimated monthly rent': 'Loyer mensuel privé médian pour le secteur', + 'Asking rent (monthly)': 'Loyer mensuel affiché pour les biens en location', + 'Total floor area (sqm)': 'Surface intérieure issue du diagnostic EPC', + 'Number of bedrooms & living rooms': 'Nombre de pièces habitables selon le diagnostic EPC', + 'Bedrooms': 'Nombre de chambres selon l’annonce en ligne', + 'Bathrooms': 'Nombre de salles de bain selon l’annonce en ligne', + 'Construction year': 'Année de construction estimée selon l’EPC', + 'Date of last transaction': 'Date de la dernière vente enregistrée au Land Registry', + 'Listing date': 'Date de première mise en ligne du bien', + 'Former council house': 'Indique si le bien a été répertorié comme logement social', + 'Current energy rating': 'Classement énergétique EPC actuel (A = meilleur, G = pire)', + 'Potential energy rating': 'Classement EPC potentiel si toutes les améliorations recommandées étaient réalisées', + 'Interior height (m)': 'Hauteur moyenne d’étage selon le diagnostic EPC', + 'Distance to nearest train or tube station (km)': 'Distance à la gare ou station de métro la plus proche', + 'Good+ primary schools within 2km': 'Écoles primaires notées Bien ou Excellent par Ofsted dans un rayon de 2 km', + 'Good+ secondary schools within 2km': 'Collèges/lycées notés Bien ou Excellent par Ofsted dans un rayon de 2 km', + 'Good+ primary schools within 5km': 'Écoles primaires notées Bien ou Excellent par Ofsted dans un rayon de 5 km', + 'Good+ secondary schools within 5km': 'Collèges/lycées notés Bien ou Excellent par Ofsted dans un rayon de 5 km', + 'Education, Skills and Training Score': 'Score de qualité éducative du secteur (plus élevé = meilleur)', + 'Income Score (rate)': 'Taux de précarité de revenu, inversé (plus élevé = moins précaire)', + 'Employment Score (rate)': 'Taux de précarité d’emploi, inversé (plus élevé = moins précaire)', + 'Health Deprivation and Disability Score': 'Score de santé et handicap (plus élevé = meilleurs résultats)', + 'Living Environment Score': 'Qualité de l’environnement intérieur et extérieur (plus élevé = meilleur)', + 'Indoors Sub-domain Score': 'Qualité et état du logement (plus élevé = meilleur)', + 'Outdoors Sub-domain Score': 'Qualité de l’air et sécurité routière (plus élevé = meilleur)', 'Serious crime per 1k residents (avg/yr)': 'Taux de crimes graves pour 1 000 habitants par an', - 'Minor crime per 1k residents (avg/yr)': 'Taux de d\u00E9lits mineurs pour 1 000 habitants par an', - 'Serious crime (avg/yr)': 'Agr\u00E9gat des cat\u00E9gories de crimes graves par an', - 'Minor crime (avg/yr)': 'Agr\u00E9gat des cat\u00E9gories de d\u00E9lits mineurs par an', + 'Minor crime per 1k residents (avg/yr)': 'Taux de délits mineurs pour 1 000 habitants par an', + 'Serious crime (avg/yr)': 'Agrégat des catégories de crimes graves par an', + 'Minor crime (avg/yr)': 'Agrégat des catégories de délits mineurs par an', 'Violence and sexual offences (avg/yr)': 'Moyenne annuelle des violences et infractions sexuelles dans le secteur', 'Burglary (avg/yr)': 'Moyenne annuelle des cambriolages dans le secteur', 'Robbery (avg/yr)': 'Moyenne annuelle des vols avec violence dans le secteur', - 'Vehicle crime (avg/yr)': 'Moyenne annuelle des crimes li\u00E9s aux v\u00E9hicules dans le secteur', + 'Vehicle crime (avg/yr)': 'Moyenne annuelle des crimes liés aux véhicules dans le secteur', 'Anti-social behaviour (avg/yr)': 'Moyenne annuelle des comportements antisociaux dans le secteur', - 'Criminal damage and arson (avg/yr)': 'Moyenne annuelle des d\u00E9gradations et incendies criminels dans le secteur', + 'Criminal damage and arson (avg/yr)': 'Moyenne annuelle des dégradations et incendies criminels dans le secteur', 'Other theft (avg/yr)': 'Moyenne annuelle des autres vols dans le secteur', - 'Theft from the person (avg/yr)': 'Moyenne annuelle des vols \u00E0 la personne dans le secteur', - 'Shoplifting (avg/yr)': 'Moyenne annuelle des vols \u00E0 l\u2019\u00E9talage dans le secteur', - 'Bicycle theft (avg/yr)': 'Moyenne annuelle des vols de v\u00E9los dans le secteur', - 'Drugs (avg/yr)': 'Moyenne annuelle des infractions li\u00E9es aux stup\u00E9fiants dans le secteur', - 'Possession of weapons (avg/yr)': 'Moyenne annuelle des infractions de possession d\u2019armes dans le secteur', - 'Public order (avg/yr)': 'Moyenne annuelle des troubles \u00E0 l\u2019ordre public dans le secteur', + 'Theft from the person (avg/yr)': 'Moyenne annuelle des vols à la personne dans le secteur', + 'Shoplifting (avg/yr)': 'Moyenne annuelle des vols à l’étalage dans le secteur', + 'Bicycle theft (avg/yr)': 'Moyenne annuelle des vols de vélos dans le secteur', + 'Drugs (avg/yr)': 'Moyenne annuelle des infractions liées aux stupéfiants dans le secteur', + 'Possession of weapons (avg/yr)': 'Moyenne annuelle des infractions de possession d’armes dans le secteur', + 'Public order (avg/yr)': 'Moyenne annuelle des troubles à l’ordre public dans le secteur', 'Other crime (avg/yr)': 'Moyenne annuelle des autres crimes dans le secteur', - 'Median age': '\u00C2ge m\u00E9dian de la population locale', - '% White': 'Pourcentage de la population se d\u00E9clarant Blanche', - '% South Asian': 'Pourcentage de la population se d\u00E9clarant Sud-Asiatique', - '% Black': 'Pourcentage de la population se d\u00E9clarant Noire', - '% East Asian': 'Pourcentage de la population se d\u00E9clarant Est-Asiatique', - '% Mixed': 'Pourcentage de la population se d\u00E9clarant M\u00E9tisse ou de plusieurs groupes ethniques', - '% Other': 'Pourcentage de la population se d\u00E9clarant d\u2019un autre groupe ethnique', + 'Median age': 'Âge médian de la population locale', + '% White': 'Pourcentage de la population se déclarant Blanche', + '% South Asian': 'Pourcentage de la population se déclarant Sud-Asiatique', + '% Black': 'Pourcentage de la population se déclarant Noire', + '% East Asian': 'Pourcentage de la population se déclarant Est-Asiatique', + '% Mixed': 'Pourcentage de la population se déclarant Métisse ou de plusieurs groupes ethniques', + '% Other': 'Pourcentage de la population se déclarant d’un autre groupe ethnique', 'Distance to nearest park (km)': 'Distance au parc ou espace vert le plus proche', - 'Number of parks within 2km': 'Nombre de parcs et espaces verts \u00E0 moins de 2 km', - 'Number of restaurants within 2km': 'Nombre de restaurants et caf\u00E9s \u00E0 moins de 2 km', - 'Number of grocery shops and supermarkets within 2km': 'Nombre d\u2019\u00E9piceries et supermarch\u00E9s \u00E0 moins de 2 km', - 'Noise (dB)': 'Niveau de bruit routier au code postal en d\u00E9cibels (Lden)', - 'Max available download speed (Mbps)': 'D\u00E9bit descendant maximal disponible au code postal', + 'Number of parks within 2km': 'Nombre de parcs et espaces verts à moins de 2 km', + 'Number of restaurants within 2km': 'Nombre de restaurants et cafés à moins de 2 km', + 'Number of grocery shops and supermarkets within 2km': 'Nombre d’épiceries et supermarchés à moins de 2 km', + 'Noise (dB)': 'Niveau de bruit routier au code postal en décibels (Lden)', + 'Max available download speed (Mbps)': 'Débit descendant maximal disponible au code postal', }, de: { - 'Listing status': 'Ob die Immobilie aus historischen Verk\u00E4ufen stammt, aktuell zum Verkauf oder zur Miete steht', - 'Property type': 'Immobilientyp: freistehend, Doppelhaush\u00E4lfte, Reihenhaus, Wohnung oder sonstige', + 'Listing status': 'Ob die Immobilie aus historischen Verkäufen stammt, aktuell zum Verkauf oder zur Miete steht', + 'Property type': 'Immobilientyp: freistehend, Doppelhaushälfte, Reihenhaus, Wohnung oder sonstige', 'Leasehold/Freehold': 'Ob die Immobilie Erbbaurecht oder Volleigentum ist', 'Last known price': 'Letzter Verkaufspreis laut Land Registry', - 'Estimated current price': 'Inflationsbereinigter Sch\u00E4tzwert der Immobilie', - 'Asking price': 'Angebotspreis f\u00FCr aktuell zum Verkauf stehende Immobilien', - 'Price per sqm': 'Verkaufspreis geteilt durch die Gesamtfl\u00E4che', - 'Est. price per sqm': 'Gesch\u00E4tzter aktueller Preis geteilt durch die Gesamtfl\u00E4che', - 'Asking price per sqm': 'Angebotspreis geteilt durch die Gesamtfl\u00E4che', + 'Estimated current price': 'Inflationsbereinigter Schätzwert der Immobilie', + 'Asking price': 'Angebotspreis für aktuell zum Verkauf stehende Immobilien', + 'Price per sqm': 'Verkaufspreis geteilt durch die Gesamtfläche', + 'Est. price per sqm': 'Geschätzter aktueller Preis geteilt durch die Gesamtfläche', + 'Asking price per sqm': 'Angebotspreis geteilt durch die Gesamtfläche', 'Estimated monthly rent': 'Mittlere monatliche Privatmiete in der Gegend', - 'Asking rent (monthly)': 'Angebotene Monatsmiete f\u00FCr Mietimmobilien', - 'Total floor area (sqm)': 'Wohnfl\u00E4che laut EPC-Gutachten', - 'Number of bedrooms & living rooms': 'Anzahl bewohnbarer R\u00E4ume laut EPC-Gutachten', + 'Asking rent (monthly)': 'Angebotene Monatsmiete für Mietimmobilien', + 'Total floor area (sqm)': 'Wohnfläche laut EPC-Gutachten', + 'Number of bedrooms & living rooms': 'Anzahl bewohnbarer Räume laut EPC-Gutachten', 'Bedrooms': 'Anzahl Schlafzimmer laut Online-Inserat', 'Bathrooms': 'Anzahl Badezimmer laut Online-Inserat', - 'Construction year': 'Gesch\u00E4tztes Baujahr laut EPC', + 'Construction year': 'Geschätztes Baujahr laut EPC', 'Date of last transaction': 'Datum des letzten Verkaufs laut Land Registry', - 'Listing date': 'Datum der Erstver\u00F6ffentlichung des Inserats', + 'Listing date': 'Datum der Erstveröffentlichung des Inserats', 'Former council house': 'Ob die Immobilie jemals als Sozialbau erfasst wurde', 'Current energy rating': 'Aktuelle EPC-Energieeffizienzklasse (A = beste, G = schlechteste)', - 'Potential energy rating': 'Potenzielle EPC-Klasse bei Umsetzung aller empfohlenen Ma\u00DFnahmen', - 'Interior height (m)': 'Durchschnittliche Geschossh\u00F6he laut EPC-Gutachten', - 'Distance to nearest train or tube station (km)': 'Entfernung zum n\u00E4chsten Bahn- oder U-Bahnhof', - 'Train or tube stations within 1km': 'Anzahl Bahn- oder U-Bahnh\u00F6fe im Umkreis von 1 km', + 'Potential energy rating': 'Potenzielle EPC-Klasse bei Umsetzung aller empfohlenen Maßnahmen', + 'Interior height (m)': 'Durchschnittliche Geschosshöhe laut EPC-Gutachten', + 'Distance to nearest train or tube station (km)': 'Entfernung zum nächsten Bahn- oder U-Bahnhof', 'Good+ primary schools within 2km': 'Von Ofsted mit Gut oder Hervorragend bewertete Grundschulen im Umkreis von 2 km', - 'Good+ secondary schools within 2km': 'Von Ofsted mit Gut oder Hervorragend bewertete weiterf\u00FChrende Schulen im Umkreis von 2 km', + 'Good+ secondary schools within 2km': 'Von Ofsted mit Gut oder Hervorragend bewertete weiterführende Schulen im Umkreis von 2 km', 'Good+ primary schools within 5km': 'Von Ofsted mit Gut oder Hervorragend bewertete Grundschulen im Umkreis von 5 km', - 'Good+ secondary schools within 5km': 'Von Ofsted mit Gut oder Hervorragend bewertete weiterf\u00FChrende Schulen im Umkreis von 5 km', - 'Education, Skills and Training Score': 'Bildungsqualit\u00E4tsscore der Gegend (h\u00F6her = besser)', - 'Income Score (rate)': 'Einkommensbenachteiligungsrate, invertiert (h\u00F6her = weniger benachteiligt)', - 'Employment Score (rate)': 'Besch\u00E4ftigungsbenachteiligungsrate, invertiert (h\u00F6her = weniger benachteiligt)', - 'Health Deprivation and Disability Score': 'Gesundheits- und Behinderungsscore (h\u00F6her = bessere Ergebnisse)', - 'Living Environment Score': 'Qualit\u00E4t der Innen- und Au\u00DFenumgebung (h\u00F6her = besser)', - 'Indoors Sub-domain Score': 'Wohnqualit\u00E4t und -zustand (h\u00F6her = besser)', - 'Outdoors Sub-domain Score': 'Luftqualit\u00E4t und Verkehrssicherheit (h\u00F6her = besser)', + 'Good+ secondary schools within 5km': 'Von Ofsted mit Gut oder Hervorragend bewertete weiterführende Schulen im Umkreis von 5 km', + 'Education, Skills and Training Score': 'Bildungsqualitätsscore der Gegend (höher = besser)', + 'Income Score (rate)': 'Einkommensbenachteiligungsrate, invertiert (höher = weniger benachteiligt)', + 'Employment Score (rate)': 'Beschäftigungsbenachteiligungsrate, invertiert (höher = weniger benachteiligt)', + 'Health Deprivation and Disability Score': 'Gesundheits- und Behinderungsscore (höher = bessere Ergebnisse)', + 'Living Environment Score': 'Qualität der Innen- und Außenumgebung (höher = besser)', + 'Indoors Sub-domain Score': 'Wohnqualität und -zustand (höher = besser)', + 'Outdoors Sub-domain Score': 'Luftqualität und Verkehrssicherheit (höher = besser)', 'Serious crime per 1k residents (avg/yr)': 'Rate schwerer Straftaten pro 1.000 Einwohner pro Jahr', 'Minor crime per 1k residents (avg/yr)': 'Rate leichter Straftaten pro 1.000 Einwohner pro Jahr', 'Serious crime (avg/yr)': 'Summe der schweren Straftaten-Kategorien pro Jahr', 'Minor crime (avg/yr)': 'Summe der leichten Straftaten-Kategorien pro Jahr', - 'Violence and sexual offences (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Gewalt- und Sexualdelikte in der Gegend', - 'Burglary (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Einbr\u00FCche in der Gegend', - 'Robbery (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Raub\u00FCberf\u00E4lle in der Gegend', - 'Vehicle crime (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Fahrzeugkriminalit\u00E4t in der Gegend', - 'Anti-social behaviour (avg/yr)': 'J\u00E4hrlicher Durchschnitt des antisozialen Verhaltens in der Gegend', - 'Criminal damage and arson (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Sachbesch\u00E4digungen und Brandstiftungen in der Gegend', - 'Other theft (avg/yr)': 'J\u00E4hrlicher Durchschnitt des sonstigen Diebstahls in der Gegend', - 'Theft from the person (avg/yr)': 'J\u00E4hrlicher Durchschnitt des Taschendiebstahls in der Gegend', - 'Shoplifting (avg/yr)': 'J\u00E4hrlicher Durchschnitt des Ladendiebstahls in der Gegend', - 'Bicycle theft (avg/yr)': 'J\u00E4hrlicher Durchschnitt des Fahrraddiebstahls in der Gegend', - 'Drugs (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Drogendelikte in der Gegend', - 'Possession of weapons (avg/yr)': 'J\u00E4hrlicher Durchschnitt der Waffenbesitzdelikte in der Gegend', - 'Public order (avg/yr)': 'J\u00E4hrlicher Durchschnitt der St\u00F6rungen der \u00F6ffentlichen Ordnung in der Gegend', - 'Other crime (avg/yr)': 'J\u00E4hrlicher Durchschnitt sonstiger Straftaten in der Gegend', - 'Median age': 'Medianalter der lokalen Bev\u00F6lkerung', - '% White': 'Anteil der Bev\u00F6lkerung, der sich als Wei\u00DF identifiziert', - '% South Asian': 'Anteil der Bev\u00F6lkerung, der sich als S\u00FCdasiatisch identifiziert', - '% Black': 'Anteil der Bev\u00F6lkerung, der sich als Schwarz identifiziert', - '% East Asian': 'Anteil der Bev\u00F6lkerung, der sich als Ostasiatisch identifiziert', - '% Mixed': 'Anteil der Bev\u00F6lkerung, der sich als gemischt oder mehreren ethnischen Gruppen zugeh\u00F6rig identifiziert', - '% Other': 'Anteil der Bev\u00F6lkerung, der sich einer anderen ethnischen Gruppe zuordnet', - 'Distance to nearest park (km)': 'Entfernung zum n\u00E4chsten Park oder Gr\u00FCnfl\u00E4che', - 'Number of parks within 2km': 'Anzahl Parks und Gr\u00FCnfl\u00E4chen im Umkreis von 2 km', - 'Number of restaurants within 2km': 'Anzahl Restaurants und Caf\u00E9s im Umkreis von 2 km', - 'Number of grocery shops and supermarkets within 2km': 'Anzahl Lebensmittelgesch\u00E4fte und Superm\u00E4rkte im Umkreis von 2 km', - 'Noise (dB)': 'Stra\u00DFenl\u00E4rmpegel an der Postleitzahl in Dezibel (Lden)', - 'Max available download speed (Mbps)': 'Maximal verf\u00FCgbare Breitband-Downloadgeschwindigkeit an der Postleitzahl', + 'Violence and sexual offences (avg/yr)': 'Jährlicher Durchschnitt der Gewalt- und Sexualdelikte in der Gegend', + 'Burglary (avg/yr)': 'Jährlicher Durchschnitt der Einbrüche in der Gegend', + 'Robbery (avg/yr)': 'Jährlicher Durchschnitt der Raubüberfälle in der Gegend', + 'Vehicle crime (avg/yr)': 'Jährlicher Durchschnitt der Fahrzeugkriminalität in der Gegend', + 'Anti-social behaviour (avg/yr)': 'Jährlicher Durchschnitt des antisozialen Verhaltens in der Gegend', + 'Criminal damage and arson (avg/yr)': 'Jährlicher Durchschnitt der Sachbeschädigungen und Brandstiftungen in der Gegend', + 'Other theft (avg/yr)': 'Jährlicher Durchschnitt des sonstigen Diebstahls in der Gegend', + 'Theft from the person (avg/yr)': 'Jährlicher Durchschnitt des Taschendiebstahls in der Gegend', + 'Shoplifting (avg/yr)': 'Jährlicher Durchschnitt des Ladendiebstahls in der Gegend', + 'Bicycle theft (avg/yr)': 'Jährlicher Durchschnitt des Fahrraddiebstahls in der Gegend', + 'Drugs (avg/yr)': 'Jährlicher Durchschnitt der Drogendelikte in der Gegend', + 'Possession of weapons (avg/yr)': 'Jährlicher Durchschnitt der Waffenbesitzdelikte in der Gegend', + 'Public order (avg/yr)': 'Jährlicher Durchschnitt der Störungen der öffentlichen Ordnung in der Gegend', + 'Other crime (avg/yr)': 'Jährlicher Durchschnitt sonstiger Straftaten in der Gegend', + 'Median age': 'Medianalter der lokalen Bevölkerung', + '% White': 'Anteil der Bevölkerung, der sich als Weiß identifiziert', + '% South Asian': 'Anteil der Bevölkerung, der sich als Südasiatisch identifiziert', + '% Black': 'Anteil der Bevölkerung, der sich als Schwarz identifiziert', + '% East Asian': 'Anteil der Bevölkerung, der sich als Ostasiatisch identifiziert', + '% Mixed': 'Anteil der Bevölkerung, der sich als gemischt oder mehreren ethnischen Gruppen zugehörig identifiziert', + '% Other': 'Anteil der Bevölkerung, der sich einer anderen ethnischen Gruppe zuordnet', + 'Distance to nearest park (km)': 'Entfernung zum nächsten Park oder Grünfläche', + 'Number of parks within 2km': 'Anzahl Parks und Grünflächen im Umkreis von 2 km', + 'Number of restaurants within 2km': 'Anzahl Restaurants und Cafés im Umkreis von 2 km', + 'Number of grocery shops and supermarkets within 2km': 'Anzahl Lebensmittelgeschäfte und Supermärkte im Umkreis von 2 km', + 'Noise (dB)': 'Straßenlärmpegel an der Postleitzahl in Dezibel (Lden)', + 'Max available download speed (Mbps)': 'Maximal verfügbare Breitband-Downloadgeschwindigkeit an der Postleitzahl', }, zh: { - 'Listing status': '\u8BE5\u623F\u4EA7\u662F\u5386\u53F2\u9500\u552E\u3001\u5F53\u524D\u5728\u552E\u8FD8\u662F\u51FA\u79DF', - 'Property type': '\u623F\u4EA7\u7C7B\u578B\uFF1A\u72EC\u7ACB\u5F0F\u3001\u534A\u72EC\u7ACB\u5F0F\u3001\u8054\u6392\u3001\u516C\u5BD3\u6216\u5176\u4ED6', - 'Leasehold/Freehold': '\u8BE5\u623F\u4EA7\u662F\u79DF\u8D41\u4EA7\u6743\u8FD8\u662F\u6C38\u4E45\u4EA7\u6743', - 'Last known price': 'Land Registry\u8BB0\u5F55\u7684\u6700\u8FD1\u4E00\u6B21\u552E\u4EF7', - 'Estimated current price': '\u7ECF\u901A\u80C0\u8C03\u6574\u540E\u7684\u5F53\u524D\u4F30\u8BA1\u4EF7\u503C', - 'Asking price': '\u5F53\u524D\u5728\u552E\u623F\u4EA7\u7684\u6302\u724C\u4EF7', - 'Price per sqm': '\u552E\u4EF7\u9664\u4EE5\u603B\u5EFA\u7B51\u9762\u79EF', - 'Est. price per sqm': '\u4F30\u8BA1\u5F53\u524D\u4EF7\u683C\u9664\u4EE5\u603B\u5EFA\u7B51\u9762\u79EF', - 'Asking price per sqm': '\u6302\u724C\u4EF7\u9664\u4EE5\u603B\u5EFA\u7B51\u9762\u79EF', - 'Estimated monthly rent': '\u5F53\u5730\u79C1\u4EBA\u79DF\u8D41\u7684\u4E2D\u4F4D\u6708\u79DF', - 'Asking rent (monthly)': '\u5F53\u524D\u51FA\u79DF\u623F\u4EA7\u7684\u6302\u724C\u6708\u79DF', - 'Total floor area (sqm)': 'EPC\u8BC4\u4F30\u7684\u5BA4\u5185\u5EFA\u7B51\u9762\u79EF', - 'Number of bedrooms & living rooms': 'EPC\u8BC4\u4F30\u7684\u5B9C\u5C45\u623F\u95F4\u6570', - 'Bedrooms': '\u5728\u7EBF\u623F\u6E90\u4E2D\u7684\u5367\u5BA4\u6570\u91CF', - 'Bathrooms': '\u5728\u7EBF\u623F\u6E90\u4E2D\u7684\u6D74\u5BA4\u6570\u91CF', - 'Construction year': 'EPC\u8BC4\u4F30\u7684\u5EFA\u9020\u5E74\u4EFD', - 'Date of last transaction': 'Land Registry\u8BB0\u5F55\u7684\u6700\u8FD1\u4E00\u6B21\u9500\u552E\u65E5\u671F', - 'Listing date': '\u623F\u4EA7\u9996\u6B21\u5728\u7EBF\u4E0A\u5E02\u7684\u65E5\u671F', - 'Former council house': '\u8BE5\u623F\u4EA7\u662F\u5426\u66FE\u88AB\u8BB0\u5F55\u4E3A\u516C\u5171\u4F4F\u623F', - 'Current energy rating': '\u5F53\u524DEPC\u80FD\u6548\u8BC4\u7EA7\uFF08A = \u6700\u4F73\uFF0CG = \u6700\u5DEE\uFF09', - 'Potential energy rating': '\u5B9E\u65BD\u6240\u6709\u5EFA\u8BAE\u6539\u8FDB\u540E\u7684\u6F5C\u5728EPC\u8BC4\u7EA7', - 'Interior height (m)': 'EPC\u8BC4\u4F30\u7684\u5E73\u5747\u5C42\u9AD8', - 'Distance to nearest train or tube station (km)': '\u5230\u6700\u8FD1\u706B\u8F66\u6216\u5730\u94C1\u7AD9\u7684\u8DDD\u79BB', - 'Train or tube stations within 1km': '1\u516C\u91CC\u5185\u706B\u8F66\u6216\u5730\u94C1\u7AD9\u7684\u6570\u91CF', - 'Good+ primary schools within 2km': 'Ofsted\u8BC4\u4E3A\u826F\u597D\u6216\u4F18\u79C0\u76842\u516C\u91CC\u5185\u5C0F\u5B66', - 'Good+ secondary schools within 2km': 'Ofsted\u8BC4\u4E3A\u826F\u597D\u6216\u4F18\u79C0\u76842\u516C\u91CC\u5185\u4E2D\u5B66', - 'Good+ primary schools within 5km': 'Ofsted\u8BC4\u4E3A\u826F\u597D\u6216\u4F18\u79C0\u76845\u516C\u91CC\u5185\u5C0F\u5B66', - 'Good+ secondary schools within 5km': 'Ofsted\u8BC4\u4E3A\u826F\u597D\u6216\u4F18\u79C0\u76845\u516C\u91CC\u5185\u4E2D\u5B66', - 'Education, Skills and Training Score': '\u5F53\u5730\u6559\u80B2\u8D28\u91CF\u5F97\u5206\uFF08\u8D8A\u9AD8\u8D8A\u597D\uFF09', - 'Income Score (rate)': '\u6536\u5165\u8D2B\u56F0\u7387\uFF0C\u53CD\u5411\u6307\u6807\uFF08\u8D8A\u9AD8\u8D8A\u4E0D\u8D2B\u56F0\uFF09', - 'Employment Score (rate)': '\u5C31\u4E1A\u8D2B\u56F0\u7387\uFF0C\u53CD\u5411\u6307\u6807\uFF08\u8D8A\u9AD8\u8D8A\u4E0D\u8D2B\u56F0\uFF09', - 'Health Deprivation and Disability Score': '\u5065\u5EB7\u4E0E\u6B8B\u969C\u5F97\u5206\uFF08\u8D8A\u9AD8\u5065\u5EB7\u72B6\u51B5\u8D8A\u597D\uFF09', - 'Living Environment Score': '\u5BA4\u5185\u5916\u73AF\u5883\u8D28\u91CF\uFF08\u8D8A\u9AD8\u8D8A\u597D\uFF09', - 'Indoors Sub-domain Score': '\u4F4F\u623F\u8D28\u91CF\u548C\u72B6\u51B5\uFF08\u8D8A\u9AD8\u8D8A\u597D\uFF09', - 'Outdoors Sub-domain Score': '\u7A7A\u6C14\u8D28\u91CF\u548C\u9053\u8DEF\u5B89\u5168\uFF08\u8D8A\u9AD8\u8D8A\u597D\uFF09', - 'Serious crime per 1k residents (avg/yr)': '\u6BCF\u5343\u4EBA\u6BCF\u5E74\u4E25\u91CD\u72AF\u7F6A\u7387', - 'Minor crime per 1k residents (avg/yr)': '\u6BCF\u5343\u4EBA\u6BCF\u5E74\u8F7B\u5FAE\u72AF\u7F6A\u7387', - 'Serious crime (avg/yr)': '\u4E25\u91CD\u72AF\u7F6A\u7C7B\u522B\u5E74\u5EA6\u603B\u8BA1', - 'Minor crime (avg/yr)': '\u8F7B\u5FAE\u72AF\u7F6A\u7C7B\u522B\u5E74\u5EA6\u603B\u8BA1', - 'Violence and sexual offences (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u66B4\u529B\u548C\u6027\u72AF\u7F6A\u6570', - 'Burglary (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u5165\u5BA4\u76D7\u7A83\u6570', - 'Robbery (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u62A2\u52AB\u6570', - 'Vehicle crime (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u8F66\u8F86\u72AF\u7F6A\u6570', - 'Anti-social behaviour (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u53CD\u793E\u4F1A\u884C\u4E3A\u6570', - 'Criminal damage and arson (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u5211\u4E8B\u6BC1\u574F\u548C\u7EB5\u706B\u6570', - 'Other theft (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u5176\u4ED6\u76D7\u7A83\u6570', - 'Theft from the person (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u4EBA\u8EAB\u76D7\u7A83\u6570', - 'Shoplifting (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u5546\u5E97\u76D7\u7A83\u6570', - 'Bicycle theft (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u81EA\u884C\u8F66\u76D7\u7A83\u6570', - 'Drugs (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u6BD2\u54C1\u72AF\u7F6A\u6570', - 'Possession of weapons (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u975E\u6CD5\u6301\u6709\u6B66\u5668\u6570', - 'Public order (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u6270\u4E71\u516C\u5171\u79E9\u5E8F\u6570', - 'Other crime (avg/yr)': '\u8BE5\u5730\u533A\u5E74\u5747\u5176\u4ED6\u72AF\u7F6A\u6570', - 'Median age': '\u5F53\u5730\u4EBA\u53E3\u7684\u4E2D\u4F4D\u5E74\u9F84', - '% White': '\u767D\u4EBA\u4EBA\u53E3\u6BD4\u4F8B', - '% South Asian': '\u5357\u4E9A\u88D4\u4EBA\u53E3\u6BD4\u4F8B', - '% Black': '\u9ED1\u4EBA\u4EBA\u53E3\u6BD4\u4F8B', - '% East Asian': '\u4E1C\u4E9A\u88D4\u4EBA\u53E3\u6BD4\u4F8B', - '% Mixed': '\u6DF7\u8840\u6216\u591A\u65CF\u88D4\u4EBA\u53E3\u6BD4\u4F8B', - '% Other': '\u5176\u4ED6\u65CF\u88D4\u4EBA\u53E3\u6BD4\u4F8B', - 'Distance to nearest park (km)': '\u5230\u6700\u8FD1\u516C\u56ED\u6216\u7EFF\u5730\u7684\u8DDD\u79BB', - 'Number of parks within 2km': '2\u516C\u91CC\u5185\u516C\u56ED\u548C\u7EFF\u5730\u6570\u91CF', - 'Number of restaurants within 2km': '2\u516C\u91CC\u5185\u9910\u5385\u548C\u5496\u5561\u9986\u6570\u91CF', - 'Number of grocery shops and supermarkets within 2km': '2\u516C\u91CC\u5185\u98DF\u54C1\u5E97\u548C\u8D85\u5E02\u6570\u91CF', - 'Noise (dB)': '\u8BE5\u90AE\u7F16\u7684\u9053\u8DEF\u566A\u97F3\u6C34\u5E73\uFF08\u5206\u8D1D\uFF0CLden\uFF09', - 'Max available download speed (Mbps)': '\u8BE5\u90AE\u7F16\u53EF\u7528\u7684\u6700\u5927\u5BBD\u5E26\u4E0B\u8F7D\u901F\u5EA6', + 'Listing status': '该房产是历史销售、当前在售还是出租', + 'Property type': '房产类型:独立式、半独立式、联排、公寓或其他', + 'Leasehold/Freehold': '该房产是租赁产权还是永久产权', + 'Last known price': 'Land Registry记录的最近一次售价', + 'Estimated current price': '经通胀调整后的当前估计价值', + 'Asking price': '当前在售房产的挂牌价', + 'Price per sqm': '售价除以总建筑面积', + 'Est. price per sqm': '估计当前价格除以总建筑面积', + 'Asking price per sqm': '挂牌价除以总建筑面积', + 'Estimated monthly rent': '当地私人租赁的中位月租', + 'Asking rent (monthly)': '当前出租房产的挂牌月租', + 'Total floor area (sqm)': 'EPC评估的室内建筑面积', + 'Number of bedrooms & living rooms': 'EPC评估的宜居房间数', + 'Bedrooms': '在线房源中的卧室数量', + 'Bathrooms': '在线房源中的浴室数量', + 'Construction year': 'EPC评估的建造年份', + 'Date of last transaction': 'Land Registry记录的最近一次销售日期', + 'Listing date': '房产首次在线上市的日期', + 'Former council house': '该房产是否曾被记录为公共住房', + 'Current energy rating': '当前EPC能效评级(A = 最佳,G = 最差)', + 'Potential energy rating': '实施所有建议改进后的潜在EPC评级', + 'Interior height (m)': 'EPC评估的平均层高', + 'Distance to nearest train or tube station (km)': '到最近火车或地铁站的距离', + 'Good+ primary schools within 2km': 'Ofsted评为良好或优秀的2公里内小学', + 'Good+ secondary schools within 2km': 'Ofsted评为良好或优秀的2公里内中学', + 'Good+ primary schools within 5km': 'Ofsted评为良好或优秀的5公里内小学', + 'Good+ secondary schools within 5km': 'Ofsted评为良好或优秀的5公里内中学', + 'Education, Skills and Training Score': '当地教育质量得分(越高越好)', + 'Income Score (rate)': '收入贫困率,反向指标(越高越不贫困)', + 'Employment Score (rate)': '就业贫困率,反向指标(越高越不贫困)', + 'Health Deprivation and Disability Score': '健康与残障得分(越高健康状况越好)', + 'Living Environment Score': '室内外环境质量(越高越好)', + 'Indoors Sub-domain Score': '住房质量和状况(越高越好)', + 'Outdoors Sub-domain Score': '空气质量和道路安全(越高越好)', + 'Serious crime per 1k residents (avg/yr)': '每千人每年严重犯罪率', + 'Minor crime per 1k residents (avg/yr)': '每千人每年轻微犯罪率', + 'Serious crime (avg/yr)': '严重犯罪类别年度总计', + 'Minor crime (avg/yr)': '轻微犯罪类别年度总计', + 'Violence and sexual offences (avg/yr)': '该地区年均暴力和性犯罪数', + 'Burglary (avg/yr)': '该地区年均入室盗窃数', + 'Robbery (avg/yr)': '该地区年均抢劫数', + 'Vehicle crime (avg/yr)': '该地区年均车辆犯罪数', + 'Anti-social behaviour (avg/yr)': '该地区年均反社会行为数', + 'Criminal damage and arson (avg/yr)': '该地区年均刑事毁坏和纵火数', + 'Other theft (avg/yr)': '该地区年均其他盗窃数', + 'Theft from the person (avg/yr)': '该地区年均人身盗窃数', + 'Shoplifting (avg/yr)': '该地区年均商店盗窃数', + 'Bicycle theft (avg/yr)': '该地区年均自行车盗窃数', + 'Drugs (avg/yr)': '该地区年均毒品犯罪数', + 'Possession of weapons (avg/yr)': '该地区年均非法持有武器数', + 'Public order (avg/yr)': '该地区年均扰乱公共秩序数', + 'Other crime (avg/yr)': '该地区年均其他犯罪数', + 'Median age': '当地人口的中位年龄', + '% White': '白人人口比例', + '% South Asian': '南亚裔人口比例', + '% Black': '黑人人口比例', + '% East Asian': '东亚裔人口比例', + '% Mixed': '混血或多族裔人口比例', + '% Other': '其他族裔人口比例', + 'Distance to nearest park (km)': '到最近公园或绿地的距离', + 'Number of parks within 2km': '2公里内公园和绿地数量', + 'Number of restaurants within 2km': '2公里内餐厅和咖啡馆数量', + 'Number of grocery shops and supermarkets within 2km': '2公里内食品店和超市数量', + 'Noise (dB)': '该邮编的道路噪音水平(分贝,Lden)', + 'Max available download speed (Mbps)': '该邮编可用的最大宽带下载速度', }, hu: { - 'Listing status': 'Az ingatlan kor\u00E1bbi elad\u00E1sb\u00F3l sz\u00E1rmazik, jelenleg elad\u00F3 vagy kiad\u00F3', - 'Property type': 'Ingatlant\u00EDpus: k\u00FCl\u00F6n\u00E1ll\u00F3, ikerh\u00E1z, sorh\u00E1z, lak\u00E1s vagy egy\u00E9b', - 'Leasehold/Freehold': 'Az ingatlan b\u00E9rleti jog\u00FA vagy teljes tulajdon\u00FA', - 'Last known price': 'A Land Registry-ben r\u00F6gz\u00EDtett utols\u00F3 elad\u00E1si \u00E1r', - 'Estimated current price': 'Infl\u00E1ci\u00F3val korrig\u00E1lt becs\u00FClt jelenlegi \u00E9rt\u00E9k', - 'Asking price': 'A jelenleg elad\u00E1sra k\u00EDn\u00E1lt ingatlanok ir\u00E1ny\u00E1ra', - 'Price per sqm': 'Elad\u00E1si \u00E1r osztva az \u00F6sszes alapter\u00FClettel', - 'Est. price per sqm': 'Becs\u00FClt jelenlegi \u00E1r osztva az \u00F6sszes alapter\u00FClettel', - 'Asking price per sqm': 'Ir\u00E1ny\u00E1r osztva az \u00F6sszes alapter\u00FClettel', - 'Estimated monthly rent': 'A k\u00F6rny\u00E9k medi\u00E1n havi mag\u00E1nb\u00E9rleti d\u00EDja', - 'Asking rent (monthly)': 'A kiad\u00F3 ingatlanok hirdetett havi b\u00E9rleti d\u00EDja', - 'Total floor area (sqm)': 'Az EPC felm\u00E9r\u00E9sb\u0151l sz\u00E1rmaz\u00F3 bels\u0151 alapter\u00FClet', - 'Number of bedrooms & living rooms': 'Lak\u00F3szob\u00E1k sz\u00E1ma az EPC felm\u00E9r\u00E9s alapj\u00E1n', - 'Bedrooms': 'H\u00E1l\u00F3szob\u00E1k sz\u00E1ma az online hirdet\u00E9s szerint', - 'Bathrooms': 'F\u00FCrd\u0151szob\u00E1k sz\u00E1ma az online hirdet\u00E9s szerint', - 'Construction year': 'Becs\u00FClt \u00E9p\u00EDt\u00E9si \u00E9v az EPC alapj\u00E1n', - 'Date of last transaction': 'Az utols\u00F3 elad\u00E1s d\u00E1tuma a Land Registry szerint', - 'Listing date': 'Az ingatlan els\u0151 online megjelen\u00E9s\u00E9nek d\u00E1tuma', - 'Former council house': 'Az ingatlan szerepelt-e valaha \u00F6nkorm\u00E1nyzati lak\u00E1sk\u00E9nt', - 'Current energy rating': 'Jelenlegi EPC energiabesorol\u00E1s (A = legjobb, G = legrosszabb)', - 'Potential energy rating': 'Potenci\u00E1lis EPC besorol\u00E1s az \u00F6sszes javasolt fejleszt\u00E9s elv\u00E9gz\u00E9se ut\u00E1n', - 'Interior height (m)': '\u00C1tlagos belmagass\u00E1g az EPC felm\u00E9r\u00E9s alapj\u00E1n', - 'Distance to nearest train or tube station (km)': 'T\u00E1vols\u00E1g a legk\u00F6zelebbi vas\u00FAt- vagy metr\u00F3\u00E1llom\u00E1sig', - 'Train or tube stations within 1km': 'Vas\u00FAt- vagy metr\u00F3\u00E1llom\u00E1sok sz\u00E1ma 1 km-en bel\u00FCl', - 'Good+ primary schools within 2km': 'Ofsted \u00E1ltal J\u00F3 vagy Kiv\u00E1l\u00F3 min\u0151s\u00EDt\u00E9s\u0171 \u00E1ltal\u00E1nos iskol\u00E1k 2 km-en bel\u00FCl', - 'Good+ secondary schools within 2km': 'Ofsted \u00E1ltal J\u00F3 vagy Kiv\u00E1l\u00F3 min\u0151s\u00EDt\u00E9s\u0171 k\u00F6z\u00E9piskol\u00E1k 2 km-en bel\u00FCl', - 'Good+ primary schools within 5km': 'Ofsted \u00E1ltal J\u00F3 vagy Kiv\u00E1l\u00F3 min\u0151s\u00EDt\u00E9s\u0171 \u00E1ltal\u00E1nos iskol\u00E1k 5 km-en bel\u00FCl', - 'Good+ secondary schools within 5km': 'Ofsted \u00E1ltal J\u00F3 vagy Kiv\u00E1l\u00F3 min\u0151s\u00EDt\u00E9s\u0171 k\u00F6z\u00E9piskol\u00E1k 5 km-en bel\u00FCl', - 'Education, Skills and Training Score': 'A k\u00F6rny\u00E9k oktat\u00E1si min\u0151s\u00E9gi pontsz\u00E1ma (magasabb = jobb)', - 'Income Score (rate)': 'J\u00F6vedelmi depriv\u00E1ci\u00F3s r\u00E1ta, invert\u00E1lva (magasabb = kev\u00E9sb\u00E9 h\u00E1tr\u00E1nyos)', - 'Employment Score (rate)': 'Foglalkoztat\u00E1si depriv\u00E1ci\u00F3s r\u00E1ta, invert\u00E1lva (magasabb = kev\u00E9sb\u00E9 h\u00E1tr\u00E1nyos)', - 'Health Deprivation and Disability Score': 'Eg\u00E9szs\u00E9g\u00FCgyi \u00E9s fogyat\u00E9koss\u00E1gi pontsz\u00E1m (magasabb = jobb eredm\u00E9nyek)', - 'Living Environment Score': 'Bels\u0151 \u00E9s k\u00FCls\u0151 k\u00F6rnyezet min\u0151s\u00E9ge (magasabb = jobb)', - 'Indoors Sub-domain Score': 'Lak\u00E1smin\u0151s\u00E9g \u00E9s \u00E1llapot (magasabb = jobb)', - 'Outdoors Sub-domain Score': 'Leveg\u0151min\u0151s\u00E9g \u00E9s k\u00F6zleked\u00E9sbiztons\u00E1g (magasabb = jobb)', - 'Serious crime per 1k residents (avg/yr)': 'S\u00FAlyos b\u0171ncselekm\u00E9nyek ar\u00E1nya 1000 lakosra \u00E9vente', - 'Minor crime per 1k residents (avg/yr)': 'Kisebb b\u0171ncselekm\u00E9nyek ar\u00E1nya 1000 lakosra \u00E9vente', - 'Serious crime (avg/yr)': 'S\u00FAlyos b\u0171ncselekm\u00E9nyi kateg\u00F3ri\u00E1k \u00E9ves \u00F6sszes\u00EDt\u00E9se', - 'Minor crime (avg/yr)': 'Kisebb b\u0171ncselekm\u00E9nyi kateg\u00F3ri\u00E1k \u00E9ves \u00F6sszes\u00EDt\u00E9se', - 'Violence and sexual offences (avg/yr)': 'Er\u0151szakos \u00E9s szexu\u00E1lis b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Burglary (avg/yr)': 'Bet\u00F6r\u00E9sek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Robbery (avg/yr)': 'Rabl\u00E1sok \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Vehicle crime (avg/yr)': 'G\u00E9pj\u00E1rm\u0171vel kapcsolatos b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Anti-social behaviour (avg/yr)': 'K\u00F6z\u00F6ss\u00E9gellenes magatart\u00E1s \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Criminal damage and arson (avg/yr)': 'Rong\u00E1l\u00E1s \u00E9s gy\u00FAjtogat\u00E1s \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Other theft (avg/yr)': 'Egy\u00E9b lop\u00E1sok \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Theft from the person (avg/yr)': 'Szem\u00E9lyek elleni lop\u00E1sok \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Shoplifting (avg/yr)': 'Bolti lop\u00E1sok \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Bicycle theft (avg/yr)': 'Ker\u00E9kp\u00E1rlop\u00E1sok \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Drugs (avg/yr)': 'K\u00E1b\u00EDt\u00F3szerrel kapcsolatos b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Possession of weapons (avg/yr)': 'Fegyvertart\u00E1ssal kapcsolatos b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Public order (avg/yr)': 'K\u00F6zrend elleni b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Other crime (avg/yr)': 'Egy\u00E9b b\u0171ncselekm\u00E9nyek \u00E9ves \u00E1tlaga a k\u00F6rny\u00E9ken', - 'Median age': 'A helyi lakoss\u00E1g medi\u00E1n \u00E9letkora', - '% White': 'A feh\u00E9rk\u00E9nt azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - '% South Asian': 'A d\u00E9l-\u00E1zsiaiként azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - '% Black': 'A feketek\u00E9nt azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - '% East Asian': 'A kelet-\u00E1zsiaiként azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - '% Mixed': 'A vegyes vagy t\u00F6bb etnikai csoporthoz tartoz\u00F3k\u00E9nt azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - '% Other': 'Az egy\u00E9b etnikai csoportba tartoz\u00F3k\u00E9nt azonos\u00EDtott lakoss\u00E1g ar\u00E1nya', - 'Distance to nearest park (km)': 'T\u00E1vols\u00E1g a legk\u00F6zelebbi parkig vagy z\u00F6ldter\u00FCletig', - 'Number of parks within 2km': 'Parkok \u00E9s z\u00F6ldter\u00FCletek sz\u00E1ma 2 km-en bel\u00FCl', - 'Number of restaurants within 2km': '\u00C9ttermek \u00E9s k\u00E1v\u00E9z\u00F3k sz\u00E1ma 2 km-en bel\u00FCl', - 'Number of grocery shops and supermarkets within 2km': '\u00C9lelmiszerboltok \u00E9s szupermarketek sz\u00E1ma 2 km-en bel\u00FCl', - 'Noise (dB)': 'K\u00F6z\u00FAti zajszint az ir\u00E1ny\u00EDt\u00F3sz\u00E1mn\u00E1l decibelben (Lden)', - 'Max available download speed (Mbps)': 'Az ir\u00E1ny\u00EDt\u00F3sz\u00E1mn\u00E1l el\u00E9rhet\u0151 maxim\u00E1lis sz\u00E9less\u00E1v\u00FA let\u00F6lt\u00E9si sebess\u00E9g', + 'Listing status': 'Az ingatlan korábbi eladásból származik, jelenleg eladó vagy kiadó', + 'Property type': 'Ingatlantípus: különálló, ikerház, sorház, lakás vagy egyéb', + 'Leasehold/Freehold': 'Az ingatlan bérleti jogú vagy teljes tulajdonú', + 'Last known price': 'A Land Registry-ben rögzített utolsó eladási ár', + 'Estimated current price': 'Inflációval korrigált becsült jelenlegi érték', + 'Asking price': 'A jelenleg eladásra kínált ingatlanok irányára', + 'Price per sqm': 'Eladási ár osztva az összes alapterülettel', + 'Est. price per sqm': 'Becsült jelenlegi ár osztva az összes alapterülettel', + 'Asking price per sqm': 'Irányár osztva az összes alapterülettel', + 'Estimated monthly rent': 'A környék medián havi magánbérleti díja', + 'Asking rent (monthly)': 'A kiadó ingatlanok hirdetett havi bérleti díja', + 'Total floor area (sqm)': 'Az EPC felmérésből származó belső alapterület', + 'Number of bedrooms & living rooms': 'Lakószobák száma az EPC felmérés alapján', + 'Bedrooms': 'Hálószobák száma az online hirdetés szerint', + 'Bathrooms': 'Fürdőszobák száma az online hirdetés szerint', + 'Construction year': 'Becsült építési év az EPC alapján', + 'Date of last transaction': 'Az utolsó eladás dátuma a Land Registry szerint', + 'Listing date': 'Az ingatlan első online megjelenésének dátuma', + 'Former council house': 'Az ingatlan szerepelt-e valaha önkormányzati lakásként', + 'Current energy rating': 'Jelenlegi EPC energiabesorolás (A = legjobb, G = legrosszabb)', + 'Potential energy rating': 'Potenciális EPC besorolás az összes javasolt fejlesztés elvégzése után', + 'Interior height (m)': 'Átlagos belmagasság az EPC felmérés alapján', + 'Distance to nearest train or tube station (km)': 'Távolság a legközelebbi vasút- vagy metróállomásig', + 'Good+ primary schools within 2km': 'Ofsted által Jó vagy Kiváló minősítésű általános iskolák 2 km-en belül', + 'Good+ secondary schools within 2km': 'Ofsted által Jó vagy Kiváló minősítésű középiskolák 2 km-en belül', + 'Good+ primary schools within 5km': 'Ofsted által Jó vagy Kiváló minősítésű általános iskolák 5 km-en belül', + 'Good+ secondary schools within 5km': 'Ofsted által Jó vagy Kiváló minősítésű középiskolák 5 km-en belül', + 'Education, Skills and Training Score': 'A környék oktatási minőségi pontszáma (magasabb = jobb)', + 'Income Score (rate)': 'Jövedelmi deprivációs ráta, invertálva (magasabb = kevésbé hátrányos)', + 'Employment Score (rate)': 'Foglalkoztatási deprivációs ráta, invertálva (magasabb = kevésbé hátrányos)', + 'Health Deprivation and Disability Score': 'Egészségügyi és fogyatékossági pontszám (magasabb = jobb eredmények)', + 'Living Environment Score': 'Belső és külső környezet minősége (magasabb = jobb)', + 'Indoors Sub-domain Score': 'Lakásminőség és állapot (magasabb = jobb)', + 'Outdoors Sub-domain Score': 'Levegőminőség és közlekedésbiztonság (magasabb = jobb)', + 'Serious crime per 1k residents (avg/yr)': 'Súlyos bűncselekmények aránya 1000 lakosra évente', + 'Minor crime per 1k residents (avg/yr)': 'Kisebb bűncselekmények aránya 1000 lakosra évente', + 'Serious crime (avg/yr)': 'Súlyos bűncselekményi kategóriák éves összesítése', + 'Minor crime (avg/yr)': 'Kisebb bűncselekményi kategóriák éves összesítése', + 'Violence and sexual offences (avg/yr)': 'Erőszakos és szexuális bűncselekmények éves átlaga a környéken', + 'Burglary (avg/yr)': 'Betörések éves átlaga a környéken', + 'Robbery (avg/yr)': 'Rablások éves átlaga a környéken', + 'Vehicle crime (avg/yr)': 'Gépjárművel kapcsolatos bűncselekmények éves átlaga a környéken', + 'Anti-social behaviour (avg/yr)': 'Közösségellenes magatartás éves átlaga a környéken', + 'Criminal damage and arson (avg/yr)': 'Rongálás és gyújtogatás éves átlaga a környéken', + 'Other theft (avg/yr)': 'Egyéb lopások éves átlaga a környéken', + 'Theft from the person (avg/yr)': 'Személyek elleni lopások éves átlaga a környéken', + 'Shoplifting (avg/yr)': 'Bolti lopások éves átlaga a környéken', + 'Bicycle theft (avg/yr)': 'Kerékpárlopások éves átlaga a környéken', + 'Drugs (avg/yr)': 'Kábítószerrel kapcsolatos bűncselekmények éves átlaga a környéken', + 'Possession of weapons (avg/yr)': 'Fegyvertartással kapcsolatos bűncselekmények éves átlaga a környéken', + 'Public order (avg/yr)': 'Közrend elleni bűncselekmények éves átlaga a környéken', + 'Other crime (avg/yr)': 'Egyéb bűncselekmények éves átlaga a környéken', + 'Median age': 'A helyi lakosság medián életkora', + '% White': 'A fehérként azonosított lakosság aránya', + '% South Asian': 'A dél-ázsiaiként azonosított lakosság aránya', + '% Black': 'A feketeként azonosított lakosság aránya', + '% East Asian': 'A kelet-ázsiaiként azonosított lakosság aránya', + '% Mixed': 'A vegyes vagy több etnikai csoporthoz tartozóként azonosított lakosság aránya', + '% Other': 'Az egyéb etnikai csoportba tartozóként azonosított lakosság aránya', + 'Distance to nearest park (km)': 'Távolság a legközelebbi parkig vagy zöldterületig', + 'Number of parks within 2km': 'Parkok és zöldterületek száma 2 km-en belül', + 'Number of restaurants within 2km': 'Éttermek és kávézók száma 2 km-en belül', + 'Number of grocery shops and supermarkets within 2km': 'Élelmiszerboltok és szupermarketek száma 2 km-en belül', + 'Noise (dB)': 'Közúti zajszint az irányítószámnál decibelben (Lden)', + 'Max available download speed (Mbps)': 'Az irányítószámnál elérhető maximális szélessávú letöltési sebesség', }, }; diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index 05e5e12..c9fe142 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -7,11 +7,11 @@ import hu from './locales/hu'; import zh from './locales/zh'; export const SUPPORTED_LANGUAGES = [ - { code: 'en', label: 'English', flag: '\uD83C\uDDEC\uD83C\uDDE7' }, - { code: 'fr', label: 'Fran\u00E7ais', flag: '\uD83C\uDDEB\uD83C\uDDF7' }, - { code: 'de', label: 'Deutsch', flag: '\uD83C\uDDE9\uD83C\uDDEA' }, - { code: 'hu', label: 'Magyar', flag: '\uD83C\uDDED\uD83C\uDDFA' }, - { code: 'zh', label: '\u4E2D\u6587', flag: '\uD83C\uDDE8\uD83C\uDDF3' }, + { code: 'en', label: 'English', flag: '\uD83C\uDDEC\uD83C\uDDE7' }, + { code: 'fr', label: 'Fran\u00E7ais', flag: '\uD83C\uDDEB\uD83C\uDDF7' }, + { code: 'de', label: 'Deutsch', flag: '\uD83C\uDDE9\uD83C\uDDEA' }, + { code: 'hu', label: 'Magyar', flag: '\uD83C\uDDED\uD83C\uDDFA' }, + { code: 'zh', label: '\u4E2D\u6587', flag: '\uD83C\uDDE8\uD83C\uDDF3' }, ] as const; export type LanguageCode = (typeof SUPPORTED_LANGUAGES)[number]['code']; @@ -19,37 +19,37 @@ export type LanguageCode = (typeof SUPPORTED_LANGUAGES)[number]['code']; const supportedCodes: Set = new Set(SUPPORTED_LANGUAGES.map((l) => l.code)); function detectLanguage(): string { - // 1. Explicit user choice (persisted from the language dropdown) - const stored = localStorage.getItem('language'); - if (stored && supportedCodes.has(stored)) return stored; + // 1. Explicit user choice (persisted from the language dropdown) + const stored = localStorage.getItem('language'); + if (stored && supportedCodes.has(stored)) return stored; - // 2. Browser preference (navigator.languages falls back to navigator.language) - for (const tag of navigator.languages ?? [navigator.language]) { - // Match full tag first (e.g. "zh-CN" → "zh"), then just the prefix - const lower = tag.toLowerCase(); - if (supportedCodes.has(lower)) return lower; - const prefix = lower.split('-')[0]; - if (supportedCodes.has(prefix)) return prefix; - } + // 2. Browser preference (navigator.languages falls back to navigator.language) + for (const tag of navigator.languages ?? [navigator.language]) { + // Match full tag first (e.g. "zh-CN" → "zh"), then just the prefix + const lower = tag.toLowerCase(); + if (supportedCodes.has(lower)) return lower; + const prefix = lower.split('-')[0]; + if (supportedCodes.has(prefix)) return prefix; + } - return 'en'; + return 'en'; } const initialLang = detectLanguage(); i18n.use(initReactI18next).init({ - resources: { - en: { translation: en }, - fr: { translation: fr }, - de: { translation: de }, - hu: { translation: hu }, - zh: { translation: zh }, - }, - lng: initialLang, - fallbackLng: 'en', - interpolation: { - escapeValue: false, // React already escapes - }, + resources: { + en: { translation: en }, + fr: { translation: fr }, + de: { translation: de }, + hu: { translation: hu }, + zh: { translation: zh }, + }, + lng: initialLang, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // React already escapes + }, }); /** @@ -57,8 +57,8 @@ i18n.use(initReactI18next).init({ * Bypasses the strict type checking on t() for dynamic key construction. */ export function tDynamic(key: string): string { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (i18n.t as any)(key); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (i18n.t as any)(key); } export default i18n; diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts index fbdd8f3..a34eee8 100644 --- a/frontend/src/i18n/locales/de.ts +++ b/frontend/src/i18n/locales/de.ts @@ -5,9 +5,9 @@ const de: Translations = { common: { save: 'Speichern', cancel: 'Abbrechen', - close: 'Schlie\u00DFen', - delete: 'L\u00F6schen', - open: '\u00D6ffnen', + close: 'Schließen', + delete: 'Löschen', + open: 'Öffnen', share: 'Teilen', copy: 'Kopieren', copied: 'Kopiert!', @@ -25,10 +25,10 @@ const de: Translations = { area: 'Gebiet', properties: 'Immobilien', postcode: 'Postleitzahl', - noAreaSelected: 'Kein Gebiet ausgew\u00E4hlt', + noAreaSelected: 'Kein Gebiet ausgewählt', noAreaSelectedDesc: - 'Klicke auf ein farbiges Gebiet auf der Karte, um Kriminalit\u00E4t, Schulen, Preise und mehr zu sehen', - clickForDetails: 'F\u00FCr Details klicken', + 'Klicke auf ein farbiges Gebiet auf der Karte, um Kriminalität, Schulen, Preise und mehr zu sehen', + clickForDetails: 'Für Details klicken', property: 'Immobilie', propertiesPlural: 'Immobilien', }, @@ -36,7 +36,7 @@ const de: Translations = { // ── Header / Nav ─────────────────────────────────── header: { appName: 'Perfect Postcode', - dashboard: '\u00DCbersicht', + dashboard: 'Übersicht', learn: 'Infos', pricing: 'Preise', inviteFriends: 'Freunde einladen', @@ -47,8 +47,8 @@ const de: Translations = { exportLabel: 'Exportieren', exporting: 'Wird exportiert...', exportToExcel: 'Als Excel exportieren', - openMenu: 'Men\u00FC \u00F6ffnen', - closeMenu: 'Men\u00FC schlie\u00DFen', + openMenu: 'Menü öffnen', + closeMenu: 'Menü schließen', }, // ── User Menu ────────────────────────────────────── @@ -63,7 +63,7 @@ const de: Translations = { // ── Mobile Menu ──────────────────────────────────── mobileMenu: { - menu: 'Men\u00FC', + menu: 'Menü', home: 'Startseite', }, @@ -71,9 +71,9 @@ const de: Translations = { auth: { logIn: 'Anmelden', createAccount: 'Konto erstellen', - resetPassword: 'Passwort zur\u00FCcksetzen', + resetPassword: 'Passwort zurücksetzen', valueProp: - 'Speichere Suchen, merke dir Immobilien und mach dort weiter, wo du aufgeh\u00F6rt hast.', + 'Speichere Suchen, merke dir Immobilien und mach dort weiter, wo du aufgehört hast.', continueWithGoogle: 'Weiter mit Google', email: 'E-Mail', emailPlaceholder: 'du@beispiel.de', @@ -81,24 +81,24 @@ const de: Translations = { passwordPlaceholderRegister: 'Mind. 8 Zeichen', passwordPlaceholderLogin: 'Dein Passwort', forgotPassword: 'Passwort vergessen?', - resetSent: 'Pr\u00FCfe deine E-Mails f\u00FCr einen Link zum Zur\u00FCcksetzen.', + resetSent: 'Prüfe deine E-Mails für einen Link zum Zurücksetzen.', pleaseWait: 'Bitte warten...', - sendResetLink: 'Link zum Zur\u00FCcksetzen senden', - backToLogin: 'Zur\u00FCck zur Anmeldung', + sendResetLink: 'Link zum Zurücksetzen senden', + backToLogin: 'Zurück zur Anmeldung', }, // ── Upgrade Modal ────────────────────────────────── upgrade: { title: 'Ganz England entdecken', description: - 'Du erkundest gerade das Demogebiet. Erhalte lebenslangen Zugang zu jeder Postleitzahl, jedem Filter, jedem Viertel. Eine Zahlung, f\u00FCr immer.', + 'Du erkundest gerade das Demogebiet. Erhalte lebenslangen Zugang zu jeder Postleitzahl, jedem Filter, jedem Viertel. Eine Zahlung, für immer.', free: 'Kostenlos', once: '/einmalig', - freeForEarly: 'Kostenlos f\u00FCr Fr\u00FChnutzer. Keine Kreditkarte erforderlich.', - oneTimePayment: 'Einmalzahlung. Lebenslanger Zugang. 30 Tage Geld-zur\u00FCck-Garantie.', + freeForEarly: 'Kostenlos für Frühnutzer. Keine Kreditkarte erforderlich.', + oneTimePayment: 'Einmalzahlung. Lebenslanger Zugang. 30 Tage Geld-zurück-Garantie.', redirecting: 'Weiterleitung...', claimFreeAccess: 'Kostenlosen Zugang sichern', - upgradeFor: 'Upgrade f\u00FCr {{price}}', + upgradeFor: 'Upgrade für {{price}}', registerAndUpgrade: 'Registrieren & Upgraden', alreadyHaveAccount: 'Bereits ein Konto? Anmelden', continueWithDemo: 'Mit Demo fortfahren', @@ -128,83 +128,88 @@ const de: Translations = { // ── Filters ──────────────────────────────────────── filters: { activeFilters: 'Aktive Filter', - addFilter: 'Filter hinzuf\u00FCgen', + addFilter: 'Filter hinzufügen', historical: 'Historisch', buy: 'Kaufen', rent: 'Mieten', findingPerfectPostcode: 'Die perfekte Postleitzahl finden', addFiltersHint: - 'F\u00FCge unten Filter hinzu, um die Karte auf Gebiete einzugrenzen, die deinen Kriterien entsprechen', + 'Füge unten Filter hinzu, um die Karte auf Gebiete einzugrenzen, die deinen Kriterien entsprechen', upgradePrompt: - 'Sieh Kriminalit\u00E4t, Schulen, L\u00E4rm, Breitband und 50+ weitere Filter f\u00FCr ganz England.', + 'Sieh Kriminalität, Schulen, Lärm, Breitband und 50+ weitere Filter für ganz England.', oneTimeLifetime: 'Einmalzahlung, lebenslanger Zugang.', upgradeToFullMap: 'Zur Vollversion upgraden', chooseFilters: - 'W\u00E4hle die Filter, die dir wichtig sind. Die Karte aktualisiert sich sofort.', + 'Wähle die Filter, die dir wichtig sind. Die Karte aktualisiert sich sofort.', searchFeatures: 'Filter durchsuchen...', noMatchingFeatures: 'Keine passenden Filter', tryDifferentSearch: 'Versuche einen anderen Suchbegriff', allFeaturesActive: 'Alle Filter sind aktiv', - removeFilterHint: 'Entferne einen Filter, um verf\u00FCgbare Merkmale zu sehen', + removeFilterHint: 'Entferne einen Filter, um verfügbare Merkmale zu sehen', featureInfo: 'Filterinfo', replayTutorial: 'Interaktives Tutorial erneut abspielen', + clearAll: 'Alle löschen', + clearAllTitle: 'Alle Filter löschen?', + clearAllSavePrompt: 'Möchtest du deine aktuellen Filter vor dem Löschen speichern?', + saveAndClear: 'Speichern & löschen', + clearWithoutSaving: 'Ohne Speichern löschen', }, // ── Philosophy Popup ─────────────────────────────── philosophy: { intro: - 'Beginne mit deinen Muss-Kriterien, dann f\u00FCge Kann-Kriterien hinzu. Die Karte grenzt sich ein, wenn du Filter hinzuf\u00FCgst. Die verbleibenden Gebiete sind deine besten Treffer.', + 'Beginne mit deinen Muss-Kriterien, dann füge Kann-Kriterien hinzu. Die Karte grenzt sich ein, wenn du Filter hinzufügst. Die verbleibenden Gebiete sind deine besten Treffer.', step1Title: 'Budget und Grundlagen', - step1Desc: '(Preisrahmen, Wohnfl\u00E4che, Immobilientyp)', + step1Desc: '(Preisrahmen, Wohnfläche, Immobilientyp)', step2Title: 'Pendelweg', - step2Desc: '(Fahrzeit zum Arbeitsplatz mit Auto, Fahrrad oder \u00D6PNV)', + step2Desc: '(Fahrzeit zum Arbeitsplatz mit Auto, Fahrrad oder ÖPNV)', step3Title: 'Sicherheit', - step3Desc: '(Kriminalit\u00E4tsraten, L\u00E4rmpegel, Bodenstabilit\u00E4t)', + step3Desc: '(Kriminalitätsraten, Lärmpegel, Bodenstabilität)', step4Title: 'Schulen', step4Desc: '(nahe gelegene Schulen mit Ofsted-Bewertung Gut oder Hervorragend)', step5Title: 'Lebensstil', step5Desc: '(Restaurants, Parks, Breitbandgeschwindigkeit)', step6Title: 'Energie', - step6Desc: '(EPC-Bewertungen, D\u00E4mmung, Heizkosten)', - tip: 'Tipp: Wenn nichts passt, lockere eine Bedingung nach der anderen, um zu sehen, welcher Kompromiss die meisten Optionen er\u00F6ffnet.', + step6Desc: '(EPC-Bewertungen, Dämmung, Heizkosten)', + tip: 'Tipp: Wenn nichts passt, lockere eine Bedingung nach der anderen, um zu sehen, welcher Kompromiss die meisten Optionen eröffnet.', }, // ── Travel Time ──────────────────────────────────── travel: { travelTime: 'Reisezeit ({{mode}})', maxTime: 'Maximale Zeit', - selectDestination: 'Ziel ausw\u00E4hlen...', + selectDestination: 'Ziel auswählen...', bestCase: 'Bestfall', - bestCaseTitle: 'Bestm\u00F6gliche Reisezeit', + bestCaseTitle: 'Bestmögliche Reisezeit', bestCaseDesc: - 'Verwendet die schnellste realistische Reisezeit (bei guter Abfahrtsplanung und guten Anschl\u00FCssen). Standard ist der Median, der eine typische Fahrt unabh\u00E4ngig vom Abfahrtszeitpunkt darstellt.', + 'Verwendet die schnellste realistische Reisezeit (bei guter Abfahrtsplanung und guten Anschlüssen). Standard ist der Median, der eine typische Fahrt unabhängig vom Abfahrtszeitpunkt darstellt.', previewOnMap: 'Auf Karte anzeigen', stopPreviewing: 'Vorschau beenden', removeTravelTime: 'Reisezeit entfernen', - addTravelTime: '{{mode}}-Reisezeit hinzuf\u00FCgen', - clearDestination: 'Ziel l\u00F6schen', + addTravelTime: '{{mode}}-Reisezeit hinzufügen', + clearDestination: 'Ziel löschen', typeToFilter: 'Tippen zum Filtern...', noDestinations: 'Keine Ziele gefunden', modeCar: 'Auto', modeBicycle: 'Fahrrad', - modeWalking: 'Zu Fu\u00DF', - modeTransit: '\u00D6PNV', - modeCarDesc: 'Fahrzeit \u00FCber die schnellste Stra\u00DFenroute', + modeWalking: 'Zu Fuß', + modeTransit: 'ÖPNV', + modeCarDesc: 'Fahrzeit über die schnellste Straßenroute', modeBicycleDesc: 'Radfahrzeit auf fahrradfreundlichen Strecken', - modeWalkingDesc: 'Gehzeit \u00FCber Fu\u00DFwege und B\u00FCrgersteige', + modeWalkingDesc: 'Gehzeit über Fußwege und Bürgersteige', modeTransitDesc: 'Reisezeit mit Bahn, U-Bahn und Bus', }, // ── Travel Time Info Popup ───────────────────────── travelInfo: { transitDesc: - ' mit \u00F6ffentlichen Verkehrsmitteln (Bus, Bahn, U-Bahn). Die Zeiten werden \u00FCber ein typisches Werktags-Morgenfenster berechnet.', + ' mit öffentlichen Verkehrsmitteln (Bus, Bahn, U-Bahn). Die Zeiten werden über ein typisches Werktags-Morgenfenster berechnet.', carDesc: - ' mit dem Auto, basierend auf typischen Stra\u00DFengeschwindigkeiten und dem Stra\u00DFennetz.', + ' mit dem Auto, basierend auf typischen Straßengeschwindigkeiten und dem Straßennetz.', bicycleDesc: ' mit dem Fahrrad, auf fahrradfreundlichen Strecken.', - walkingDesc: ' zu Fu\u00DF, \u00FCber Fu\u00DFwege und B\u00FCrgersteige.', + walkingDesc: ' zu Fuß, über Fußwege und Bürgersteige.', mainDesc: - 'Zeigt, wie lange es dauert, das ausgew\u00E4hlte Ziel von jedem Gebiet aus zu erreichen', + 'Zeigt, wie lange es dauert, das ausgewählte Ziel von jedem Gebiet aus zu erreichen', sliderHint: 'Verwende den Schieberegler, um deine maximale Pendelzeit festzulegen.', }, @@ -214,21 +219,26 @@ const de: Translations = { describeIdealArea: 'Beschreibe dein Wunschgebiet mit KI', aiSearch: 'KI-Suche', describeHint: 'beschreibe, wonach du suchst', - placeholder: 'z.\u00A0B. ruhige Gegend, unter \u00A3400k, nahe guten Schulen...', + placeholder: 'z. B. ruhige Gegend, unter £400k, nahe guten Schulen...', example1: 'Sichere Gegend nahe guten Schulen', - example2: '30 Min. Pendelweg zu Kings Cross, unter \u00A3500k', + example2: '30 Min. Pendelweg zu Kings Cross, unter £500k', example3: 'Ruhiges Dorf, 3 Schlafzimmer, schnelles Breitband', analysing: 'Anfrage wird analysiert...', searchingDestinations: 'Ziele werden gesucht...', generatingFilters: 'Filter werden generiert...', refiningResults: 'Ergebnisse werden verfeinert...', weeklyLimitReached: - 'Du hast das w\u00F6chentliche KI-Nutzungslimit erreicht. Es wird n\u00E4chste Woche automatisch zur\u00FCckgesetzt.', + 'Du hast das wöchentliche KI-Nutzungslimit erreicht. Es wird nächste Woche automatisch zurückgesetzt.', }, // ── Map Legend ───────────────────────────────────── mapLegend: { - clearColourView: 'Farbansicht zur\u00FCcksetzen', + clearColourView: 'Farbansicht zurücksetzen', + historicalMatches: 'Historische Immobilientreffer', + propertiesForSale: 'Immobilien zum Verkauf', + propertiesForRent: 'Immobilien zur Miete', + numberOfProperties: 'Anzahl der Immobilien', + previewing: 'Vorschau von \u201c{{name}}\u201d', }, // ── Properties Pane ──────────────────────────────── @@ -236,12 +246,12 @@ const de: Translations = { unknownAddress: 'Unbekannte Adresse', unsaveProperty: 'Immobilie nicht mehr merken', saveProperty: 'Immobilie merken', - lastSold: 'Letzter Verkauf: \u00A3{{price}}', + lastSold: 'Letzter Verkauf: £{{price}}', estValue: 'Gesch. Wert:', type: 'Typ:', builtForm: 'Bauweise:', tenure: 'Besitzart:', - floorArea: 'Wohnfl\u00E4che:', + floorArea: 'Wohnfläche:', bedrooms: 'Schlafzimmer:', bathrooms: 'Badezimmer:', rooms: 'Zimmer:', @@ -253,34 +263,34 @@ const de: Translations = { renovations: 'Renovierungen', viewExternalListing: 'Externes Inserat ansehen', perMonth: '/Monat', - perSqm: '/m\u00B2', + perSqm: '/m²', searchPlaceholder: 'Nach Adresse oder Postleitzahl suchen...', propertyData: 'Immobiliendaten', propertyDataDesc: - 'Preise stammen vom HM Land Registry (was K\u00E4ufer tats\u00E4chlich bezahlt haben). Wohnfl\u00E4che, Energiebewertungen, Baujahr und Besitzart stammen aus offiziellen EPC-Gutachten. Beide Quellen werden nach Adresse innerhalb jeder Postleitzahl abgeglichen.', + 'Preise stammen vom HM Land Registry (was Käufer tatsächlich bezahlt haben). Wohnfläche, Energiebewertungen, Baujahr und Besitzart stammen aus offiziellen EPC-Gutachten. Beide Quellen werden nach Adresse innerhalb jeder Postleitzahl abgeglichen.', }, // ── Area Pane ────────────────────────────────────── areaPane: { areaStatistics: 'Gebietsstatistiken', - statsFor: 'Statistiken f\u00FCr alle Immobilien in diesem {{type}}', + statsFor: 'Statistiken für alle Immobilien in diesem {{type}}', matchingFilters: ', die allen aktiven Filtern entsprechen', viewProperties: '{{count}} Immobilien ansehen', priceHistory: 'Preisentwicklung', journeysFrom: 'Verbindungen ab {{label}}', to: 'Nach {{destination}}', - noJourneyData: 'Keine Verbindungsdaten verf\u00FCgbar', + noJourneyData: 'Keine Verbindungsdaten verfügbar', viewOnGoogleMaps: 'Auf Google Maps ansehen', - walk: 'Zu Fu\u00DF', + walk: 'Zu Fuß', cycle: 'Fahrrad', }, // ── Histogram Legend ─────────────────────────────── histogramLegend: { - tealBars: 'T\u00FCrkise Balken', - tealBarsDesc: 'zeigen die Verteilung im ausgew\u00E4hlten Gebiet', + tealBars: 'Türkise Balken', + tealBarsDesc: 'zeigen die Verteilung im ausgewählten Gebiet', greyBars: 'Graue Balken', - greyBarsDesc: 'zeigen die Gesamtverteilung \u00FCber alle Gebiete', + greyBarsDesc: 'zeigen die Gesamtverteilung über alle Gebiete', dashedLine: 'Gestrichelte Linie', dashedLineDesc: 'zeigt den landesweiten Durchschnitt', }, @@ -293,9 +303,9 @@ const de: Translations = { // ── POI Pane ─────────────────────────────────────── poiPane: { pois: 'POIs', - pointsOfInterest: 'Sehensw\u00FCrdigkeiten & Einrichtungen', + pointsOfInterest: 'Sehenswürdigkeiten & Einrichtungen', poiDescription: - 'Daten von OpenStreetMap. Umfasst Haltestellen, Gesch\u00E4fte, Restaurants, Gesundheitseinrichtungen, Freizeit und mehr. Regelm\u00E4\u00DFig aktualisiert mit vollst\u00E4ndiger Kategorieabdeckung.', + 'Daten von OpenStreetMap. Umfasst Haltestellen, Geschäfte, Restaurants, Gesundheitseinrichtungen, Freizeit und mehr. Regelmäßig aktualisiert mit vollständiger Kategorieabdeckung.', searchCategories: 'Kategorien durchsuchen...', dataSourceInfo: 'Datenquelleninfo', }, @@ -319,7 +329,7 @@ const de: Translations = { // ── Mobile Drawer ────────────────────────────────── mobileDrawer: { - closeDrawer: 'Schublade schlie\u00DFen', + closeDrawer: 'Schublade schließen', }, // ── Home Page ────────────────────────────────────── @@ -328,9 +338,9 @@ const de: Translations = { heroTitle2: 'Wert', heroTitle3: 'Minimale Kompromisse.', heroSubtitle: - 'Auf Immobiliensuche? Mach aus deiner gr\u00F6\u00DFten Investition deine kl\u00FCgste Entscheidung.', + 'Auf Immobiliensuche? Mach aus deiner größten Investition deine klügste Entscheidung.', heroDescription: - 'So viele M\u00F6glichkeiten \u2014 die richtige Wahl kann \u00FCberw\u00E4ltigend sein. Unsere interaktive Karte macht es einfach: W\u00E4hle deine Muss-Kriterien und sieh sofort die passenden Gebiete.', + 'So viele Möglichkeiten — die richtige Wahl kann überwältigend sein. Unsere interaktive Karte macht es einfach: Wähle deine Muss-Kriterien und sieh sofort die passenden Gebiete.', exploreTheMap: 'Karte entdecken', seeTheDifference: 'Den Unterschied sehen', statProperties: 'Immobilien', @@ -339,201 +349,201 @@ const de: Translations = { statPostcodeInEngland: 'Postleitzahl in England', ourPhilosophy: 'Unsere Philosophie', philosophyP1: - 'Auf Rightmove w\u00E4hlt man zuerst ein Gebiet und hofft, dass es gut ist. Am Ende vergleicht man Kriminalit\u00E4tsstatistiken, Schulberichte und Breitband-Checker in einem Dutzend Tabs, eine Postleitzahl nach der anderen.', + 'Auf Rightmove wählt man zuerst ein Gebiet und hofft, dass es gut ist. Am Ende vergleicht man Kriminalitätsstatistiken, Schulberichte und Breitband-Checker in einem Dutzend Tabs, eine Postleitzahl nach der anderen.', philosophyP2: 'Wir drehen das um. Sag uns, was du brauchst (Budget, Pendelweg, Schulen, Sicherheit), und wir zeigen dir jedes Gebiet in England, das passt. Kein Raten. Keine verschwendeten Besichtigungen.', howToUseIt: 'So funktioniert es', howStep1Title: 'Lege deine Muss-Kriterien fest', - howStep1Desc: 'Budget, Pendelweg, Schulen \u2014 die Karte zeigt nur, was passt.', + howStep1Desc: 'Budget, Pendelweg, Schulen — die Karte zeigt nur, was passt.', howStep2Title: 'Entdecke Gebiete und versteckte Perlen', howStep2Desc: 'Zoom rein, schau dir Details und Kann-Kriterien an.', howStep3Title: 'Einzelne Postleitzahlen erkunden', howStep3Desc: - 'Sieh einzelne Immobilien, Verkaufspreise, Wohnfl\u00E4chen und vergleiche.', + 'Sieh einzelne Immobilien, Verkaufspreise, Wohnflächen und vergleiche.', howStep4Title: 'Engere Auswahl mit Zuversicht', howStep4Desc: - 'Jedes Gebiet auf deiner Liste erf\u00FCllt deine tats\u00E4chlichen Kriterien \u2014 nicht nur, was diese Woche inseriert war.', + 'Jedes Gebiet auf deiner Liste erfüllt deine tatsächlichen Kriterien — nicht nur, was diese Woche inseriert war.', othersVs: 'Andere vs', listingPortals: 'Immobilienportale', - checkMyPostcode: '\u201EMeine Postleitzahl pr\u00FCfen\u201C', + checkMyPostcode: '„Meine Postleitzahl prüfen“', areaGuides: 'Gebietsratgeber', - compSearchWithout: 'Suchen, ohne zuerst ein Gebiet auszuw\u00E4hlen', - compSearchWithoutSub: '(starte mit Bed\u00FCrfnissen, nicht mit einem Ort)', + compSearchWithout: 'Suchen, ohne zuerst ein Gebiet auszuwählen', + compSearchWithoutSub: '(starte mit Bedürfnissen, nicht mit einem Ort)', compAreaData: 'Gebietsdaten', - compAreaDataSub: '(Kriminalit\u00E4t, Schulen, L\u00E4rm, Breitband)', + compAreaDataSub: '(Kriminalität, Schulen, Lärm, Breitband)', compPropertyData: 'Immobilienspezifische Daten', - compPropertyDataSub: '(Preis, EPC, Wohnfl\u00E4che)', + compPropertyDataSub: '(Preis, EPC, Wohnfläche)', compFilters: '56 kombinierbare Filter an einem Ort', compFiltersSub: '(alle Einblicke, eine interaktive Karte)', ctaTitle: - 'Mach aus deiner gr\u00F6\u00DFten Investition deine kl\u00FCgste\u00A0Entscheidung.', + 'Mach aus deiner größten Investition deine klügste Entscheidung.', ctaDescription: - 'Das verdient die richtigen Werkzeuge \u2014 \u00FCberlass es nicht dem Zufall.', + 'Das verdient die richtigen Werkzeuge — überlass es nicht dem Zufall.', }, // ── Pricing Page ─────────────────────────────────── pricingPage: { - title: 'Fr\u00FChzugangspreis', + title: 'Frühzugangspreis', subtitle: - 'Einmal zahlen, f\u00FCr immer nutzen. Je fr\u00FCher du dabei bist, desto weniger zahlst du.', + 'Einmal zahlen, für immer nutzen. Je früher du dabei bist, desto weniger zahlst du.', costContext: - 'Ein Hauskauf kostet \u00A310.000+ an Grunderwerbsteuer, \u00A31.500 an Anwaltsgeb\u00FChren, \u00A3500 f\u00FCr ein Gutachten. W\u00E4hlst du das falsche Gebiet, steckst du mit einem langen Pendelweg, schlechten Schulen oder einer Stra\u00DFe fest, von der du nichts wusstest.', - lessThanSurvey: 'Weniger als ein Hausgutachten. Deutlich n\u00FCtzlicher.', + 'Ein Hauskauf kostet £10.000+ an Grunderwerbsteuer, £1.500 an Anwaltsgebühren, £500 für ein Gutachten. Wählst du das falsche Gebiet, steckst du mit einem langen Pendelweg, schlechten Schulen oder einer Straße fest, von der du nichts wusstest.', + lessThanSurvey: 'Weniger als ein Hausgutachten. Deutlich nützlicher.', currentTier: 'Aktuelle Stufe', firstNUsers: 'Erste {{count}} Nutzer', everyoneAfter: 'Alle danach', - nextNUsers: 'N\u00E4chste {{count}} Nutzer', + nextNUsers: 'Nächste {{count}} Nutzer', lifetime: '/lebenslang', spotsRemaining: '{{count}} Platz verbleibend', - spotsRemainingPlural: '{{count}} Pl\u00E4tze verbleibend', + spotsRemainingPlural: '{{count}} Plätze verbleibend', filled: 'Vergeben', - openDashboard: '\u00DCbersicht \u00F6ffnen', + openDashboard: 'Übersicht öffnen', getStarted: 'Jetzt starten', - getStartedPrice: 'Jetzt starten \u2014 {{price}}', + getStartedPrice: 'Jetzt starten — {{price}}', noCreditCard: 'Keine Kreditkarte erforderlich', - moneyBackGuarantee: '30 Tage Geld-zur\u00FCck-Garantie', + moneyBackGuarantee: '30 Tage Geld-zurück-Garantie', soldOut: 'Ausverkauft', - upcoming: 'Demn\u00E4chst', + upcoming: 'Demnächst', failedToLoad: - 'Preise konnten nicht geladen werden. Bitte sp\u00E4ter erneut versuchen.', - feat1: '56 Datenebenen f\u00FCr ganz England', + 'Preise konnten nicht geladen werden. Bitte später erneut versuchen.', + feat1: '56 Datenebenen für ganz England', feat2: 'Jede Postleitzahl bewertet und filterbar', feat3: 'Unbegrenztes Erkunden der Karte und Exporte', feat4: 'Mehrere Jahrzehnte historischer Preisdaten', - feat5: 'Kriminalit\u00E4t, Schulen, Verkehr, Breitband und mehr', - feat6: 'Alle zuk\u00FCnftigen Datenaktualisierungen inklusive', + feat5: 'Kriminalität, Schulen, Verkehr, Breitband und mehr', + feat6: 'Alle zukünftigen Datenaktualisierungen inklusive', }, // ── Learn Page ───────────────────────────────────── learnPage: { - faq: 'H\u00E4ufige Fragen', + faq: 'Häufige Fragen', dataSources: 'Datenquellen', support: 'Support', - dataSourcesIntro: 'Diese Anwendung kombiniert {{count}} offene Datens\u00E4tze zu Immobilienpreisen, Energieeffizienz, Verkehr, Demografie, Kriminalit\u00E4t, Umwelt und mehr.', - faqIntro: 'Ob Sie kaufen, mieten oder einfach nur st\u00F6bern \u2013 so hilft Ihnen Perfect Postcode, das richtige Gebiet zu finden.', + dataSourcesIntro: 'Diese Anwendung kombiniert {{count}} offene Datensätze zu Immobilienpreisen, Energieeffizienz, Verkehr, Demografie, Kriminalität, Umwelt und mehr.', + faqIntro: 'Ob Sie kaufen, mieten oder einfach nur stöbern – so hilft Ihnen Perfect Postcode, das richtige Gebiet zu finden.', supportIntro: 'Haben Sie eine Frage? Schauen Sie in unsere FAQ oder kontaktieren Sie uns direkt.', source: 'Quelle:', - optOut: 'Widerspruch gegen \u00F6ffentliche Offenlegung', + optOut: 'Widerspruch gegen öffentliche Offenlegung', attribution: 'Quellenangaben', - attrLandRegistry: 'Enth\u00E4lt Daten des HM Land Registry \u00A9 Crown copyright and database right 2025.', - attrOgl: 'Enth\u00E4lt \u00F6ffentliche Informationen lizenziert unter der', + attrLandRegistry: 'Enthält Daten des HM Land Registry © Crown copyright and database right 2025.', + attrOgl: 'Enthält öffentliche Informationen lizenziert unter der', attrOglLink: 'Open Government Licence v3.0', - attrOs: 'Enth\u00E4lt OS-Daten \u00A9 Crown copyright and database rights 2025.', + attrOs: 'Enthält OS-Daten © Crown copyright and database rights 2025.', attrTfl: 'Betrieben mit TfL Open Data.', - attrOsm: 'Enth\u00E4lt Daten von', - attrOsmContrib: '\u00A9 OpenStreetMap contributors', - attrOsmLicense: 'verf\u00FCgbar unter der', + attrOsm: 'Enthält Daten von', + attrOsmContrib: '© OpenStreetMap contributors', + attrOsmLicense: 'verfügbar unter der', attrOsmLicenseLink: 'Open Data Commons Open Database License (ODbL)', // Data source names & descriptions dsPricePaidName: 'Price Paid Data', dsPricePaidOrigin: 'HM Land Registry', - dsPricePaidUse: 'Vollst\u00E4ndige historische Immobilien-Verkaufspreise f\u00FCr England.', + dsPricePaidUse: 'Vollständige historische Immobilien-Verkaufspreise für England.', dsEpcName: 'Energy Performance Certificates (EPC)', dsEpcOrigin: 'Ministry of Housing, Communities & Local Government', - dsEpcUse: 'Energieausweise f\u00FCr Wohngeb\u00E4ude mit Angaben zu Wohnfl\u00E4che, Zimmeranzahl, Baujahr, Energiebewertungen, Immobilientyp und Bauform. \u00DCber Adresse innerhalb jeder Postleitzahl mit Price-Paid-Daten verkn\u00FCpft. Eigent\u00FCmer k\u00F6nnen der \u00F6ffentlichen Offenlegung widersprechen.', + dsEpcUse: 'Energieausweise für Wohngebäude mit Angaben zu Wohnfläche, Zimmeranzahl, Baujahr, Energiebewertungen, Immobilientyp und Bauform. Über Adresse innerhalb jeder Postleitzahl mit Price-Paid-Daten verknüpft. Eigentümer können der öffentlichen Offenlegung widersprechen.', dsNsplName: 'National Statistics Postcode Lookup (NSPL)', dsNsplOrigin: 'ONS / ArcGIS', - dsNsplUse: 'Ordnet Postleitzahlen Koordinaten und statistischen Gebietscodes zu, um alle gebietsbezogenen Datens\u00E4tze mit einzelnen Immobilien zu verkn\u00FCpfen.', + dsNsplUse: 'Ordnet Postleitzahlen Koordinaten und statistischen Gebietscodes zu, um alle gebietsbezogenen Datensätze mit einzelnen Immobilien zu verknüpfen.', dsIodName: 'English Indices of Deprivation 2025', dsIodOrigin: 'Ministry of Housing, Communities & Local Government', - dsIodUse: 'Relative Benachteiligungswerte f\u00FCr Einkommen, Besch\u00E4ftigung, Bildung, Gesundheit, Kriminalit\u00E4t und Wohnumfeld f\u00FCr jedes Viertel in England.', - dsEthnicityName: 'Bev\u00F6lkerung nach Ethnie (Zensus 2021)', + dsIodUse: 'Relative Benachteiligungswerte für Einkommen, Beschäftigung, Bildung, Gesundheit, Kriminalität und Wohnumfeld für jedes Viertel in England.', + dsEthnicityName: 'Bevölkerung nach Ethnie (Zensus 2021)', dsEthnicityOrigin: 'ONS', - dsEthnicityUse: 'Bev\u00F6lkerungsanteile nach ethnischer Gruppe (s\u00FCdasiatisch, ostasiatisch, schwarz, gemischt, wei\u00DF, andere) pro Bezirk.', + dsEthnicityUse: 'Bevölkerungsanteile nach ethnischer Gruppe (südasiatisch, ostasiatisch, schwarz, gemischt, weiß, andere) pro Bezirk.', dsCrimeName: 'Street-level Crime Data', dsCrimeOrigin: 'data.police.uk', - dsCrimeUse: 'Kriminalit\u00E4tsdaten auf Stra\u00DFenebene von 2023 bis 2025, aggregiert als Jahresdurchschnitte nach LSOA und Deliktsart (Gewalt, Einbruch, antisoziales Verhalten, Drogen, Fahrzeugkriminalit\u00E4t usw.).', + dsCrimeUse: 'Kriminalitätsdaten auf Straßenebene von 2023 bis 2025, aggregiert als Jahresdurchschnitte nach LSOA und Deliktsart (Gewalt, Einbruch, antisoziales Verhalten, Drogen, Fahrzeugkriminalität usw.).', dsOsmName: 'OpenStreetMap POIs', dsOsmOrigin: 'OpenStreetMap contributors / Geofabrik', - dsOsmUse: 'Sehensw\u00FCrdigkeiten und Einrichtungen wie Gesch\u00E4fte, Restaurants, Gesundheitseinrichtungen, Freizeit, Tourismus und mehr in ganz Gro\u00DFbritannien.', + dsOsmUse: 'Sehenswürdigkeiten und Einrichtungen wie Geschäfte, Restaurants, Gesundheitseinrichtungen, Freizeit, Tourismus und mehr in ganz Großbritannien.', dsGreenspaceName: 'OS Open Greenspace', dsGreenspaceOrigin: 'Ordnance Survey', - dsGreenspaceUse: 'Offizielle Gr\u00FCnfl\u00E4chengrenzen f\u00FCr Gro\u00DFbritannien, einschlie\u00DFlich \u00F6ffentlicher Parks, G\u00E4rten, Sportpl\u00E4tze und Spielpl\u00E4tze. Polygon-Schwerpunkte werden f\u00FCr die Parkn\u00E4hez\u00E4hlung und Entfernungsberechnung zum n\u00E4chsten Park verwendet.', + dsGreenspaceUse: 'Offizielle Grünflächengrenzen für Großbritannien, einschließlich öffentlicher Parks, Gärten, Sportplätze und Spielplätze. Polygon-Schwerpunkte werden für die Parknähezählung und Entfernungsberechnung zum nächsten Park verwendet.', dsNaptanName: 'NaPTAN (Public Transport Stops)', dsNaptanOrigin: 'Department for Transport', - dsNaptanUse: 'Standorte von Bahnh\u00F6fen und Haltestellen f\u00FCr Bahn, Bus, U-Bahn/Stra\u00DFenbahn, F\u00E4hre und Flugh\u00E4fen in ganz England.', + dsNaptanUse: 'Standorte von Bahnhöfen und Haltestellen für Bahn, Bus, U-Bahn/Straßenbahn, Fähre und Flughäfen in ganz England.', dsNoiseName: 'Defra Noise Mapping', dsNoiseOrigin: 'Defra / Environment Agency', - dsNoiseUse: 'Stra\u00DFenl\u00E4rmpegel (24-Stunden-gewichteter Durchschnitt) aus der strategischen L\u00E4rmkartierung 2022, hochaufl\u00F6send modelliert und an jeder Postleitzahl abgetastet.', + dsNoiseUse: 'Straßenlärmpegel (24-Stunden-gewichteter Durchschnitt) aus der strategischen Lärmkartierung 2022, hochauflösend modelliert und an jeder Postleitzahl abgetastet.', dsOfstedName: 'Ofsted School Inspections', dsOfstedOrigin: 'Ofsted', - dsOfstedUse: 'Neueste Inspektionsergebnisse f\u00FCr staatlich finanzierte Schulen (Stand April 2025). Pro Postleitzahl gemittelt f\u00FCr einen lokalen Schulqualit\u00E4tswert (1=Hervorragend bis 4=Unzureichend).', + dsOfstedUse: 'Neueste Inspektionsergebnisse für staatlich finanzierte Schulen (Stand April 2025). Pro Postleitzahl gemittelt für einen lokalen Schulqualitätswert (1=Hervorragend bis 4=Unzureichend).', dsBroadbandName: 'Ofcom Broadband Performance', dsBroadbandOrigin: 'Ofcom', dsBroadbandUse: 'Festnetz-Breitbandabdeckung und maximale Download-Geschwindigkeiten nach Gebiet aus Ofcom Connected Nations 2025.', dsCouncilTaxName: 'Council Tax Levels 2025-26', dsCouncilTaxOrigin: 'Ministry of Housing, Communities & Local Government', - dsCouncilTaxUse: 'J\u00E4hrliche Council-Tax-S\u00E4tze f\u00FCr die Stufen A bis H f\u00FCr alle 296 Abrechnungsbeh\u00F6rden in England, f\u00FCr eine von zwei Erwachsenen bewohnte Immobilie. \u00DCber den Bezirkscode aus dem NSPL-Postleitzahlenverzeichnis mit Immobilien verkn\u00FCpft.', + dsCouncilTaxUse: 'Jährliche Council-Tax-Sätze für die Stufen A bis H für alle 296 Abrechnungsbehörden in England, für eine von zwei Erwachsenen bewohnte Immobilie. Über den Bezirkscode aus dem NSPL-Postleitzahlenverzeichnis mit Immobilien verknüpft.', dsRentalName: 'Private Rental Market Statistics', dsRentalOrigin: 'ONS / Valuation Office Agency', - dsRentalUse: 'Monatliche Medianmieten des privaten Mietmarkts nach Bezirk und Schlafzimmerkategorie (Okt. 2022 - Sept. 2023). \u00DCber Bezirkscode und gesch\u00E4tzte Schlafzimmeranzahl mit Immobilien verkn\u00FCpft.', + dsRentalUse: 'Monatliche Medianmieten des privaten Mietmarkts nach Bezirk und Schlafzimmerkategorie (Okt. 2022 - Sept. 2023). Über Bezirkscode und geschätzte Schlafzimmeranzahl mit Immobilien verknüpft.', // FAQ section titles faqFindingTitle: 'Ihr Gebiet finden', faqCommuteTitle: 'Pendelweg und Reisezeit', faqBudgetTitle: 'Budget und Preis-Leistung', faqSafetyTitle: 'Sicherheit und Nachbarschaft', faqFamiliesTitle: 'Familien und Schulen', - faqEnvironmentTitle: 'Umwelt und Lebensqualit\u00E4t', + faqEnvironmentTitle: 'Umwelt und Lebensqualität', faqWhyTitle: 'Warum Perfect Postcode', faqPricingTitle: 'Preise und Zugang', faqTipsTitle: 'Tipps und Tricks', // FAQ items — Finding Your Area - faqFinding1Q: 'Ich wei\u00DF nicht einmal, welche Gebiete ich mir ansehen soll. Kann mir das helfen?', - faqFinding1A: 'Genau daf\u00FCr ist es da. Legen Sie Ihre Filter fest (Budget, Pendelzeit, geringe Kriminalit\u00E4t, gute Schulen) und die Karte leuchtet auf, um Ihnen jedes Gebiet zu zeigen, das alle Kriterien erf\u00FCllt. Kein n\u00E4chtliches Googeln nach \u201Ebeste Wohngegenden bei Manchester\u201C mehr.', - faqFinding2Q: 'Ich ziehe irgendwohin, wo ich noch nie war. Wie fange ich \u00FCberhaupt an?', - faqFinding2A: 'Stellen Sie Ihre Filter f\u00FCr das ein, was Ihnen wichtig ist, und die Karte hebt sofort die passenden Gebiete hervor. Sie gehen von \u201EIch kenne keine einzige Stra\u00DFe\u201C zu einer Auswahlliste in wenigen Minuten.', - faqFinding3Q: 'Wie finde ich Gebiete, die alle meine Kriterien gleichzeitig erf\u00FCllen?', - faqFinding3A: 'Kombinieren Sie mehrere Filter (Kriminalit\u00E4t unter dem Durchschnitt, gute Schulen, Pendelweg unter 40 Minuten) und f\u00E4rben Sie die Karte nach Preis, um die Gebiete mit dem besten Preis-Leistungs-Verh\u00E4ltnis zu finden. Die Karte aktualisiert sich in Echtzeit, wenn Sie die Regler bewegen.', + faqFinding1Q: 'Ich weiß nicht einmal, welche Gebiete ich mir ansehen soll. Kann mir das helfen?', + faqFinding1A: 'Genau dafür ist es da. Legen Sie Ihre Filter fest (Budget, Pendelzeit, geringe Kriminalität, gute Schulen) und die Karte leuchtet auf, um Ihnen jedes Gebiet zu zeigen, das alle Kriterien erfüllt. Kein nächtliches Googeln nach „beste Wohngegenden bei Manchester“ mehr.', + faqFinding2Q: 'Ich ziehe irgendwohin, wo ich noch nie war. Wie fange ich überhaupt an?', + faqFinding2A: 'Stellen Sie Ihre Filter für das ein, was Ihnen wichtig ist, und die Karte hebt sofort die passenden Gebiete hervor. Sie gehen von „Ich kenne keine einzige Straße“ zu einer Auswahlliste in wenigen Minuten.', + faqFinding3Q: 'Wie finde ich Gebiete, die alle meine Kriterien gleichzeitig erfüllen?', + faqFinding3A: 'Kombinieren Sie mehrere Filter (Kriminalität unter dem Durchschnitt, gute Schulen, Pendelweg unter 40 Minuten) und färben Sie die Karte nach Preis, um die Gebiete mit dem besten Preis-Leistungs-Verhältnis zu finden. Die Karte aktualisiert sich in Echtzeit, wenn Sie die Regler bewegen.', // FAQ items — Commute and Travel - faqCommute1Q: 'Kann ich sehen, wie lange mein Pendelweg aus verschiedenen Gebieten tats\u00E4chlich dauern w\u00FCrde?', - faqCommute1A: 'Legen Sie Ihren Arbeitsplatz als Ziel fest und wir f\u00E4rben jede Postleitzahl nach Fahrzeit \u2013 ob mit Auto, Fahrrad oder \u00F6ffentlichen Verkehrsmitteln. Filtern Sie nach Ihrer maximalen Pendelzeit und der Rest verschwindet.', + faqCommute1Q: 'Kann ich sehen, wie lange mein Pendelweg aus verschiedenen Gebieten tatsächlich dauern würde?', + faqCommute1A: 'Legen Sie Ihren Arbeitsplatz als Ziel fest und wir färben jede Postleitzahl nach Fahrzeit – ob mit Auto, Fahrrad oder öffentlichen Verkehrsmitteln. Filtern Sie nach Ihrer maximalen Pendelzeit und der Rest verschwindet.', faqCommute2Q: 'Wie ist das besser als Google Maps?', - faqCommute2A: 'Google Maps zeigt Ihnen eine Fahrt auf einmal. Wir f\u00E4rben jede Postleitzahl in England nach Pendelzeit in einem Blick, sodass Sie Hunderte von Gebieten nebeneinander vergleichen k\u00F6nnen, anstatt sie einzeln zu suchen.', + faqCommute2A: 'Google Maps zeigt Ihnen eine Fahrt auf einmal. Wir färben jede Postleitzahl in England nach Pendelzeit in einem Blick, sodass Sie Hunderte von Gebieten nebeneinander vergleichen können, anstatt sie einzeln zu suchen.', // FAQ items — Budget and Value - faqBudget1Q: 'Wie finde ich Gebiete, in denen ich am meisten Wohnfl\u00E4che f\u00FCr mein Geld bekomme?', - faqBudget1A: 'Filtern Sie nach Preis pro m\u00B2 und Sie sehen sofort, welche Postleitzahlen am meisten Fl\u00E4che pro Pfund bieten. Kombinieren Sie es mit dem Energiebewertungsfilter, um Immobilien mit hohen Heizkosten zu vermeiden.', - faqBudget2Q: 'Wie stelle ich sicher, dass ein g\u00FCnstiges Gebiet nicht aus gutem Grund g\u00FCnstig ist?', - faqBudget2A: 'Legen Sie Benachteiligungswerte, Kriminalit\u00E4tsstatistiken, Schulbewertungen und Breitbandgeschwindigkeiten neben den Preis. Wenn eine Postleitzahl erschwinglich ist und bei allem, was z\u00E4hlt, gut abschneidet, haben Sie echten Wert gefunden \u2013 nicht nur einen niedrigen Preis mit Kompromissen, die Sie noch nicht bemerkt haben.', + faqBudget1Q: 'Wie finde ich Gebiete, in denen ich am meisten Wohnfläche für mein Geld bekomme?', + faqBudget1A: 'Filtern Sie nach Preis pro m² und Sie sehen sofort, welche Postleitzahlen am meisten Fläche pro Pfund bieten. Kombinieren Sie es mit dem Energiebewertungsfilter, um Immobilien mit hohen Heizkosten zu vermeiden.', + faqBudget2Q: 'Wie stelle ich sicher, dass ein günstiges Gebiet nicht aus gutem Grund günstig ist?', + faqBudget2A: 'Legen Sie Benachteiligungswerte, Kriminalitätsstatistiken, Schulbewertungen und Breitbandgeschwindigkeiten neben den Preis. Wenn eine Postleitzahl erschwinglich ist und bei allem, was zählt, gut abschneidet, haben Sie echten Wert gefunden – nicht nur einen niedrigen Preis mit Kompromissen, die Sie noch nicht bemerkt haben.', // FAQ items — Safety and Neighbourhood - faqSafety1Q: 'Wie kann ich pr\u00FCfen, ob ein Gebiet sicher ist, bevor ich dorthin ziehe?', - faqSafety1A: 'Wir \u00FCberlagern echte polizeilich erfasste Kriminalit\u00E4tsdaten, aufgeschl\u00FCsselt nach Art, \u00FCber jedes Viertel in England. Filtern Sie nach Gewaltkriminalit\u00E4t, Einbruch oder antisozialem Verhalten und sehen Sie sofort, welche Postleitzahlen die niedrigsten Zahlen haben.', - faqSafety2Q: 'Ich finde st\u00E4ndig Wohnungen, die online toll aussehen, aber dann stellt sich die Gegend als schwierig heraus.', - faqSafety2A: 'Genau daf\u00FCr gibt es dieses Tool. Kombinieren Sie Kriminalit\u00E4tsraten, L\u00E4rmpegel, Benachteiligungswerte, Pubs und Parks in der N\u00E4he sowie Breitbandgeschwindigkeiten auf einer Karte, damit Sie wissen, wie ein Viertel wirklich ist, bevor Sie eine Besichtigung buchen.', + faqSafety1Q: 'Wie kann ich prüfen, ob ein Gebiet sicher ist, bevor ich dorthin ziehe?', + faqSafety1A: 'Wir überlagern echte polizeilich erfasste Kriminalitätsdaten, aufgeschlüsselt nach Art, über jedes Viertel in England. Filtern Sie nach Gewaltkriminalität, Einbruch oder antisozialem Verhalten und sehen Sie sofort, welche Postleitzahlen die niedrigsten Zahlen haben.', + faqSafety2Q: 'Ich finde ständig Wohnungen, die online toll aussehen, aber dann stellt sich die Gegend als schwierig heraus.', + faqSafety2A: 'Genau dafür gibt es dieses Tool. Kombinieren Sie Kriminalitätsraten, Lärmpegel, Benachteiligungswerte, Pubs und Parks in der Nähe sowie Breitbandgeschwindigkeiten auf einer Karte, damit Sie wissen, wie ein Viertel wirklich ist, bevor Sie eine Besichtigung buchen.', // FAQ items — Families and Schools - faqFamilies1Q: 'Kann ich Gebiete mit guten Schulen UND geringer Kriminalit\u00E4t in einer Suche finden?', - faqFamilies1A: 'Ja. Kombinieren Sie Filter f\u00FCr Ofsted-Bewertungen, Kriminalit\u00E4tsraten, Parks und alles andere, was f\u00FCr Ihre Familie wichtig ist, und die Karte hebt nur die Gebiete hervor, die alles erf\u00FCllen. Kein Abgleich \u00FCber f\u00FCnf verschiedene Websites mehr.', - faqFamilies2Q: 'Woher wei\u00DF ich, ob ein Viertel Parks und Spielpl\u00E4tze in der N\u00E4he hat?', - faqFamilies2A: 'Schalten Sie die POI-Ebene f\u00FCr Parks und Gr\u00FCnfl\u00E4chen ein, um sie direkt auf der Karte zu sehen. Sie k\u00F6nnen auch nach der Anzahl der fu\u00DFl\u00E4ufig erreichbaren Parks pro Postleitzahl filtern.', + faqFamilies1Q: 'Kann ich Gebiete mit guten Schulen UND geringer Kriminalität in einer Suche finden?', + faqFamilies1A: 'Ja. Kombinieren Sie Filter für Ofsted-Bewertungen, Kriminalitätsraten, Parks und alles andere, was für Ihre Familie wichtig ist, und die Karte hebt nur die Gebiete hervor, die alles erfüllen. Kein Abgleich über fünf verschiedene Websites mehr.', + faqFamilies2Q: 'Woher weiß ich, ob ein Viertel Parks und Spielplätze in der Nähe hat?', + faqFamilies2A: 'Schalten Sie die POI-Ebene für Parks und Grünflächen ein, um sie direkt auf der Karte zu sehen. Sie können auch nach der Anzahl der fußläufig erreichbaren Parks pro Postleitzahl filtern.', // FAQ items — Environment and Quality of Life - faqEnv1Q: 'Kann ich energieeffiziente Wohnungen finden, die nicht an einer lauten Stra\u00DFe liegen?', - faqEnv1A: 'Filtern Sie nach EPC-Bewertung (A bis C), dann \u00FCberlagern Sie die Stra\u00DFenl\u00E4rmdaten, um alles \u00FCber Ihrem Schwellenwert auszuschlie\u00DFen. F\u00E4rben Sie nach einem der beiden Kriterien, um ruhige, effiziente Stra\u00DFen auf einen Blick zu erkennen.', + faqEnv1Q: 'Kann ich energieeffiziente Wohnungen finden, die nicht an einer lauten Straße liegen?', + faqEnv1A: 'Filtern Sie nach EPC-Bewertung (A bis C), dann überlagern Sie die Straßenlärmdaten, um alles über Ihrem Schwellenwert auszuschließen. Färben Sie nach einem der beiden Kriterien, um ruhige, effiziente Straßen auf einen Blick zu erkennen.', faqEnv2Q: 'Zeigt es Hochwasser- oder Senkungsrisiken?', - faqEnv2A: 'Wir integrieren Bodenstabilit\u00E4tsdaten, damit Sie vor dem Kauf auf Senkungen, Schrumpf-Quell-Tone und andere geologische Risiken pr\u00FCfen k\u00F6nnen. Schlie\u00DFen Sie Risikogebiete fr\u00FChzeitig aus.', + faqEnv2A: 'Wir integrieren Bodenstabilitätsdaten, damit Sie vor dem Kauf auf Senkungen, Schrumpf-Quell-Tone und andere geologische Risiken prüfen können. Schließen Sie Risikogebiete frühzeitig aus.', faqEnv3Q: 'Kann ich Gebiete mit schnellem Breitband finden, die wirklich ruhig sind?', - faqEnv3A: '\u00DCberlagern Sie den Breitbandfilter mit den Stra\u00DFenl\u00E4rmdaten, um Stra\u00DFen mit guter Anbindung und wenig Verkehrsl\u00E4rm zu finden. F\u00E4rben Sie nach einem der beiden Kriterien, um Gebiete auf einen Blick zu vergleichen.', + faqEnv3A: 'Überlagern Sie den Breitbandfilter mit den Straßenlärmdaten, um Straßen mit guter Anbindung und wenig Verkehrslärm zu finden. Färben Sie nach einem der beiden Kriterien, um Gebiete auf einen Blick zu vergleichen.', // FAQ items — Why Perfect Postcode - faqWhy1Q: 'Ich benutze bereits Rightmove. Was bringt mir das zus\u00E4tzlich?', - faqWhy1A: 'Rightmove zeigt Ihnen H\u00E4user. Wir zeigen Ihnen Gebiete. Kriminalit\u00E4tsraten, Schulbewertungen, Breitbandgeschwindigkeiten, L\u00E4rmpegel, Benachteiligungswerte und mehr \u2013 alles filterbar auf einer Karte. Sie k\u00F6nnen ein Viertel beurteilen, bevor Sie sich die Angebote ansehen.', + faqWhy1Q: 'Ich benutze bereits Rightmove. Was bringt mir das zusätzlich?', + faqWhy1A: 'Rightmove zeigt Ihnen Häuser. Wir zeigen Ihnen Gebiete. Kriminalitätsraten, Schulbewertungen, Breitbandgeschwindigkeiten, Lärmpegel, Benachteiligungswerte und mehr – alles filterbar auf einer Karte. Sie können ein Viertel beurteilen, bevor Sie sich die Angebote ansehen.', faqWhy2Q: 'Kann ich das nicht alles kostenlos selbst recherchieren?', - faqWhy2A: 'Sie k\u00F6nnten Polizeidaten, Ofsted-Berichte, EPC-Register, Land-Registry-Eintr\u00E4ge und ONS-Statistiken eine Postleitzahl nach der anderen abgleichen. Oder Sie haben alles filterbar und farbkodiert auf einer Karte in Sekunden.', - faqWhy3Q: 'Woher stammen die Daten tats\u00E4chlich?', - faqWhy3A: 'Jeder Datensatz stammt aus offiziellen britischen Regierungsquellen: Land Registry, EPC-Register, ONS, Ofsted, Ofcom, data.police.uk und Defra. Wir scrapen keine Makler und erfinden nichts. Sie k\u00F6nnen jeden Eintrag anhand der Originalquelle \u00FCberpr\u00FCfen.', + faqWhy2A: 'Sie könnten Polizeidaten, Ofsted-Berichte, EPC-Register, Land-Registry-Einträge und ONS-Statistiken eine Postleitzahl nach der anderen abgleichen. Oder Sie haben alles filterbar und farbkodiert auf einer Karte in Sekunden.', + faqWhy3Q: 'Woher stammen die Daten tatsächlich?', + faqWhy3A: 'Jeder Datensatz stammt aus offiziellen britischen Regierungsquellen: Land Registry, EPC-Register, ONS, Ofsted, Ofcom, data.police.uk und Defra. Wir scrapen keine Makler und erfinden nichts. Sie können jeden Eintrag anhand der Originalquelle überprüfen.', // FAQ items — Pricing and Access - faqPricing1Q: 'Lohnt es sich wirklich, f\u00FCr ein Immobilien-Suchtool zu bezahlen?', - faqPricing1A: 'Ein Hauskauf ist wahrscheinlich die gr\u00F6\u00DFte Anschaffung Ihres Lebens. Ein einziges Warnsignal zu erkennen (eine laute Stra\u00DFe, schlechtes Breitband, steigende Kriminalit\u00E4t) bevor Sie sich festlegen, k\u00F6nnte Ihnen Jahre des Bedauerns ersparen. Das kostet weniger als eine Tankf\u00FCllung.', + faqPricing1Q: 'Lohnt es sich wirklich, für ein Immobilien-Suchtool zu bezahlen?', + faqPricing1A: 'Ein Hauskauf ist wahrscheinlich die größte Anschaffung Ihres Lebens. Ein einziges Warnsignal zu erkennen (eine laute Straße, schlechtes Breitband, steigende Kriminalität) bevor Sie sich festlegen, könnte Ihnen Jahre des Bedauerns ersparen. Das kostet weniger als eine Tankfüllung.', faqPricing2Q: 'Ist das ein Abonnement?', - faqPricing2A: 'Nein. Einmalzahlung, Ihres f\u00FCr immer. Nutzen Sie es intensiv w\u00E4hrend Ihrer Suche, kommen Sie zur\u00FCck, wenn Sie neugierig auf ein neues Gebiet sind, und es ist immer noch da, falls Sie erneut umziehen.', + faqPricing2A: 'Nein. Einmalzahlung, Ihres für immer. Nutzen Sie es intensiv während Ihrer Suche, kommen Sie zurück, wenn Sie neugierig auf ein neues Gebiet sind, und es ist immer noch da, falls Sie erneut umziehen.', faqPricing3Q: 'Was kann ich mit der kostenlosen Version nutzen?', - faqPricing3A: 'Kostenlose Nutzer k\u00F6nnen alle Funktionen im Demogebiet erkunden (Innenstadt London, ungef\u00E4hr Zonen 1 bis 2). F\u00FCr den Zugang zu Daten f\u00FCr den Rest Englands ben\u00F6tigen Sie den lebenslangen Zugang.', - faqPricing4Q: 'Kann ich eine R\u00FCckerstattung erhalten?', - faqPricing4A: 'Selbstverst\u00E4ndlich. Wir bieten eine 30-Tage-Geld-zur\u00FCck-Garantie. Wenn Sie nicht zufrieden sind, schreiben Sie innerhalb von 30 Tagen an support@perfect-postcode.co.uk f\u00FCr eine vollst\u00E4ndige R\u00FCckerstattung.', + faqPricing3A: 'Kostenlose Nutzer können alle Funktionen im Demogebiet erkunden (Innenstadt London, ungefähr Zonen 1 bis 2). Für den Zugang zu Daten für den Rest Englands benötigen Sie den lebenslangen Zugang.', + faqPricing4Q: 'Kann ich eine Rückerstattung erhalten?', + faqPricing4A: 'Selbstverständlich. Wir bieten eine 30-Tage-Geld-zurück-Garantie. Wenn Sie nicht zufrieden sind, schreiben Sie innerhalb von 30 Tagen an support@perfect-postcode.co.uk für eine vollständige Rückerstattung.', // FAQ items — Tips and Tricks - faqTips1Q: 'Wie nutze ich den KI-Filter, anstatt Filter einzeln hinzuzuf\u00FCgen?', - faqTips1A: 'Beschreiben Sie, was Sie suchen, z.\u00A0B. \u201Eruhige Gegend nahe guten Schulen mit schnellem Breitband unter \u00A3400k\u201C, und die KI richtet alle relevanten Filter auf einmal ein. Passen Sie danach manuell an.', - faqTips2Q: 'Kann ich eine Suche speichern und sp\u00E4ter darauf zur\u00FCckkommen?', - faqTips2A: 'Klicken Sie auf Speichern und alles wird erfasst: Ihre Filter, die Zoomstufe und die angezeigte Datenebene. Machen Sie genau dort weiter, wo Sie aufgeh\u00F6rt haben, oder teilen Sie den Link mit Ihrem Partner.', + faqTips1Q: 'Wie nutze ich den KI-Filter, anstatt Filter einzeln hinzuzufügen?', + faqTips1A: 'Beschreiben Sie, was Sie suchen, z. B. „ruhige Gegend nahe guten Schulen mit schnellem Breitband unter £400k“, und die KI richtet alle relevanten Filter auf einmal ein. Passen Sie danach manuell an.', + faqTips2Q: 'Kann ich eine Suche speichern und später darauf zurückkommen?', + faqTips2A: 'Klicken Sie auf Speichern und alles wird erfasst: Ihre Filter, die Zoomstufe und die angezeigte Datenebene. Machen Sie genau dort weiter, wo Sie aufgehört haben, oder teilen Sie den Link mit Ihrem Partner.', faqTips3Q: 'Kann ich die angezeigten Daten exportieren?', - faqTips3A: 'Nutzen Sie den Export-Button, um die aktuell gefilterten Immobilien als Tabelle herunterzuladen. Der Export ber\u00FCcksichtigt alle aktiven Filter, sodass Sie genau die gew\u00FCnschten Daten erhalten.', + faqTips3A: 'Nutzen Sie den Export-Button, um die aktuell gefilterten Immobilien als Tabelle herunterzuladen. Der Export berücksichtigt alle aktiven Filter, sodass Sie genau die gewünschten Daten erhalten.', }, // ── Account Page ─────────────────────────────────── @@ -541,7 +551,7 @@ const de: Translations = { emailLabel: 'E-Mail', subscriptionLabel: 'Abonnement', upgrade: 'Upgraden', - redirecting: 'Weiterleitung\u2026', + redirecting: 'Weiterleitung…', receiveNewsletter: 'Newsletter-E-Mails erhalten', needHelp: 'Brauchst du Hilfe? Schreib uns an', responseTime: 'Wir antworten in der Regel innerhalb von 24 Stunden.', @@ -552,20 +562,20 @@ const de: Translations = { searches: 'Suchen', noSavedSearches: 'Noch keine gespeicherten Suchen', noSavedSearchesDesc: - 'Speichere deine Filter und Kartenansicht, um genau dort weiterzumachen, wo du aufgeh\u00F6rt hast.', + 'Speichere deine Filter und Kartenansicht, um genau dort weiterzumachen, wo du aufgehört hast.', noSavedProperties: 'Noch keine gespeicherten Immobilien', noSavedPropertiesDesc: - 'Merke dir Immobilien w\u00E4hrend du erkundest und erstelle deine Auswahlliste, ohne den \u00DCberblick zu verlieren.', - openPostcode: 'Postleitzahl \u00F6ffnen', + 'Merke dir Immobilien während du erkundest und erstelle deine Auswahlliste, ohne den Überblick zu verlieren.', + openPostcode: 'Postleitzahl öffnen', viewListing: 'Inserat ansehen', clickToRename: 'Klicken zum Umbenennen', notesPlaceholder: 'Notiere deine Gedanken...', - deleteSearch: 'Suche l\u00F6schen', + deleteSearch: 'Suche löschen', deleteSearchConfirm: - 'M\u00F6chtest du diese gespeicherte Suche wirklich l\u00F6schen? Dies kann nicht r\u00FCckg\u00E4ngig gemacht werden.', - deleteProperty: 'Immobilie l\u00F6schen', + 'Möchtest du diese gespeicherte Suche wirklich löschen? Dies kann nicht rückgängig gemacht werden.', + deleteProperty: 'Immobilie löschen', deletePropertyConfirm: - 'M\u00F6chtest du diese gespeicherte Immobilie wirklich l\u00F6schen? Dies kann nicht r\u00FCckg\u00E4ngig gemacht werden.', + 'Möchtest du diese gespeicherte Immobilie wirklich löschen? Dies kann nicht rückgängig gemacht werden.', bed: 'Schlafz.', epc: 'EPC', }, @@ -573,7 +583,7 @@ const de: Translations = { // ── Invites Page ─────────────────────────────────── invitesPage: { inviteLinksLicensed: - 'Einladungslinks sind f\u00FCr lizenzierte Nutzer verf\u00FCgbar.', + 'Einladungslinks sind für lizenzierte Nutzer verfügbar.', inviteAdminLabel: 'Freunde einladen (100% Rabatt)', inviteReferralLabel: 'Freunde einladen (30% Rabatt)', generateFreeInvite: 'Kostenlosen Einladungslink erstellen', @@ -586,7 +596,7 @@ const de: Translations = { link: 'Link', status: 'Status', created: 'Erstellt', - redeemed: 'Eingel\u00F6st', + redeemed: 'Eingelöst', pending: 'Ausstehend', }, @@ -604,21 +614,21 @@ const de: Translations = { 'Ein Freund hat 30% Rabatt auf lebenslangen Zugang mit dir geteilt.', exploreEvery: 'Entdecke jedes Viertel in England', propertyInfo: - 'Immobilienpreise, Energiebewertungen, Kriminalit\u00E4tsstatistiken, Schulbewertungen und mehr', - invalidInvite: 'Ung\u00FCltige Einladung', + 'Immobilienpreise, Energiebewertungen, Kriminalitätsstatistiken, Schulbewertungen und mehr', + invalidInvite: 'Ungültige Einladung', inviteAlreadyUsed: 'Einladung bereits verwendet', inviteAlreadyUsedDesc: - 'Dieser Einladungslink wurde bereits eingel\u00F6st.', - invalidInviteLink: 'Ung\u00FCltiger Einladungslink', + 'Dieser Einladungslink wurde bereits eingelöst.', + invalidInviteLink: 'Ungültiger Einladungslink', invalidInviteLinkDesc: - 'Dieser Einladungslink ist ung\u00FCltig oder abgelaufen.', + 'Dieser Einladungslink ist ungültig oder abgelaufen.', licenseActivated: 'Lizenz aktiviert!', fullAccessGranted: 'Du hast jetzt vollen Zugang zu Perfect Postcode.', activating: 'Wird aktiviert...', activateLicense: 'Lizenz aktivieren', - claimDiscount: 'Rabatt einl\u00F6sen', - registerToClaim: 'Registrieren zum Einl\u00F6sen', + claimDiscount: 'Rabatt einlösen', + registerToClaim: 'Registrieren zum Einlösen', youAlreadyHaveLicense: 'Du hast bereits eine Lizenz', accountHasFullAccess: 'Dein Konto hat bereits vollen Zugang.', failedToValidate: 'Einladungslink konnte nicht validiert werden', @@ -642,7 +652,7 @@ const de: Translations = { poiCategories: '{{count}} POI-Kategorien', travelDestination: '{{count}} Fahrziel', travelDestinations: '{{count}} Fahrziele', - propertiesMatch: '{{count}} Immobilien stimmen \u00FCberein', + propertiesMatch: '{{count}} Immobilien stimmen überein', setFilters: '{{count}} Filter setzen: {{list}}', noFiltersSet: 'Keine Filter gesetzt', toDestination: '{{mode}} nach {{label}} {{bounds}}', @@ -652,18 +662,18 @@ const de: Translations = { // ── Tutorial ────────────────────────────────────── tutorial: { - step1Title: 'Sagen Sie der Karte, was z\u00E4hlt', - step1Content: 'Legen Sie Ihr Budget, maximale Pendelzeit, Schulqualit\u00E4t und Kriminalit\u00E4tsschwelle fest. Was Ihnen wichtig ist. Nur qualifizierende Gebiete bleiben hervorgehoben. Nutzen Sie das Augensymbol, um nach beliebigem Merkmal einzuf\u00E4rben.', + step1Title: 'Sagen Sie der Karte, was zählt', + step1Content: 'Legen Sie Ihr Budget, maximale Pendelzeit, Schulqualität und Kriminalitätsschwelle fest. Was Ihnen wichtig ist. Nur qualifizierende Gebiete bleiben hervorgehoben. Nutzen Sie das Augensymbol, um nach beliebigem Merkmal einzufärben.', step2Title: 'Oder einfach beschreiben', - step2Content: 'Tippen Sie auf Deutsch ein, was Sie suchen, z.\u00A0B. \u201Eruhige Gegend nahe guter Schulen unter \u00A3400k\u201C, und wir richten die Filter f\u00FCr Sie ein.', + step2Content: 'Tippen Sie auf Deutsch ein, was Sie suchen, z. B. „ruhige Gegend nahe guter Schulen unter £400k“, und wir richten die Filter für Sie ein.', step3Title: 'Erkunden Sie, was es gibt', - step3Content: 'Schwenken und zoomen Sie durch England. Klicken Sie auf ein beliebiges farbiges Gebiet, um Kriminalit\u00E4t, Schulen, Preise, Breitband, L\u00E4rm und mehr zu sehen.', + step3Content: 'Schwenken und zoomen Sie durch England. Klicken Sie auf ein beliebiges farbiges Gebiet, um Kriminalität, Schulen, Preise, Breitband, Lärm und mehr zu sehen.', step4Title: 'Direkt zu einem Ort springen', step4Content: 'Suchen Sie nach einem Ort oder einer Postleitzahl, um sofort dorthin zu gelangen.', step5Title: 'Ins Detail gehen', - step5Content: 'Sehen Sie Gebietsstatistiken, Histogramme und einzelne Immobiliendaten: Preise, Wohnfl\u00E4che, Energiebewertungen und mehr.', - step6Title: 'Was ist in der N\u00E4he?', - step6Content: 'Blenden Sie Schulen, Gesch\u00E4fte, Bahnh\u00F6fe, Parks und Restaurants auf der Karte ein, um zu sehen, was erreichbar ist.', + step5Content: 'Sehen Sie Gebietsstatistiken, Histogramme und einzelne Immobiliendaten: Preise, Wohnfläche, Energiebewertungen und mehr.', + step6Title: 'Was ist in der Nähe?', + step6Content: 'Blenden Sie Schulen, Geschäfte, Bahnhöfe, Parks und Restaurants auf der Karte ein, um zu sehen, was erreichbar ist.', }, // ── Server-derived values ────────────────────────── @@ -675,7 +685,7 @@ const de: Translations = { 'Transport': 'Verkehr', 'Education': 'Bildung', 'Deprivation': 'Benachteiligung', - 'Crime': 'Kriminalit\u00E4t', + 'Crime': 'Kriminalität', 'Demographics': 'Demografie', 'Amenities': 'Infrastruktur', @@ -684,14 +694,14 @@ const de: Translations = { 'Property type': 'Immobilientyp', 'Leasehold/Freehold': 'Erbbaurecht/Volleigentum', 'Last known price': 'Letzter bekannter Preis', - 'Estimated current price': 'Gesch\u00E4tzter aktueller Preis', + 'Estimated current price': 'Geschätzter aktueller Preis', 'Asking price': 'Angebotspreis', - 'Price per sqm': 'Preis pro m\u00B2', - 'Est. price per sqm': 'Gesch. Preis pro m\u00B2', - 'Asking price per sqm': 'Angebotspreis pro m\u00B2', - 'Estimated monthly rent': 'Gesch\u00E4tzte Monatsmiete', + 'Price per sqm': 'Preis pro m²', + 'Est. price per sqm': 'Gesch. Preis pro m²', + 'Asking price per sqm': 'Angebotspreis pro m²', + 'Estimated monthly rent': 'Geschätzte Monatsmiete', 'Asking rent (monthly)': 'Angebotsmiete (monatlich)', - 'Total floor area (sqm)': 'Gesamtwohnfl\u00E4che (m\u00B2)', + 'Total floor area (sqm)': 'Gesamtwohnfläche (m²)', 'Number of bedrooms & living rooms': 'Anzahl Schlaf- & Wohnzimmer', 'Bedrooms': 'Schlafzimmer', 'Bathrooms': 'Badezimmer', @@ -701,26 +711,25 @@ const de: Translations = { 'Former council house': 'Ehemaliger Sozialbau', 'Current energy rating': 'Aktuelle Energiebewertung', 'Potential energy rating': 'Potenzielle Energiebewertung', - 'Interior height (m)': 'Raumh\u00F6he (m)', + 'Interior height (m)': 'Raumhöhe (m)', // ─ Feature names (Transport) ─ - 'Distance to nearest train or tube station (km)': 'Entfernung zum n\u00E4chsten Bahn- oder U-Bahnhof (km)', - 'Train or tube stations within 1km': 'Bahn- oder U-Bahnh\u00F6fe im Umkreis von 1 km', + 'Distance to nearest train or tube station (km)': 'Entfernung zum nächsten Bahn- oder U-Bahnhof (km)', // ─ Feature names (Education) ─ 'Good+ primary schools within 2km': 'Gute+ Grundschulen im Umkreis von 2 km', - 'Good+ secondary schools within 2km': 'Gute+ weiterf\u00FChrende Schulen im Umkreis von 2 km', + 'Good+ secondary schools within 2km': 'Gute+ weiterführende Schulen im Umkreis von 2 km', 'Good+ primary schools within 5km': 'Gute+ Grundschulen im Umkreis von 5 km', - 'Good+ secondary schools within 5km': 'Gute+ weiterf\u00FChrende Schulen im Umkreis von 5 km', - 'Education, Skills and Training Score': 'Score f\u00FCr Bildung, Kompetenzen und Ausbildung', + 'Good+ secondary schools within 5km': 'Gute+ weiterführende Schulen im Umkreis von 5 km', + 'Education, Skills and Training Score': 'Score für Bildung, Kompetenzen und Ausbildung', // ─ Feature names (Deprivation) ─ 'Income Score (rate)': 'Einkommensscore (Rate)', - 'Employment Score (rate)': 'Besch\u00E4ftigungsscore (Rate)', - 'Health Deprivation and Disability Score': 'Score f\u00FCr Gesundheit und Behinderung', + 'Employment Score (rate)': 'Beschäftigungsscore (Rate)', + 'Health Deprivation and Disability Score': 'Score für Gesundheit und Behinderung', 'Living Environment Score': 'Score der Wohnumgebung', - 'Indoors Sub-domain Score': 'Score der Wohnqualit\u00E4t (innen)', - 'Outdoors Sub-domain Score': 'Score der Umgebungsqualit\u00E4t (au\u00DFen)', + 'Indoors Sub-domain Score': 'Score der Wohnqualität (innen)', + 'Outdoors Sub-domain Score': 'Score der Umgebungsqualität (außen)', // ─ Feature names (Crime) ─ 'Serious crime per 1k residents (avg/yr)': 'Schwere Straftaten pro 1k Einwohner (Durchschn./Jahr)', @@ -728,36 +737,36 @@ const de: Translations = { 'Serious crime (avg/yr)': 'Schwere Straftaten (Durchschn./Jahr)', 'Minor crime (avg/yr)': 'Leichte Straftaten (Durchschn./Jahr)', 'Violence and sexual offences (avg/yr)': 'Gewalt- und Sexualdelikte (Durchschn./Jahr)', - 'Burglary (avg/yr)': 'Einbr\u00FCche (Durchschn./Jahr)', - 'Robbery (avg/yr)': 'Raub\u00FCberf\u00E4lle (Durchschn./Jahr)', - 'Vehicle crime (avg/yr)': 'Fahrzeugkriminalit\u00E4t (Durchschn./Jahr)', + 'Burglary (avg/yr)': 'Einbrüche (Durchschn./Jahr)', + 'Robbery (avg/yr)': 'Raubüberfälle (Durchschn./Jahr)', + 'Vehicle crime (avg/yr)': 'Fahrzeugkriminalität (Durchschn./Jahr)', 'Anti-social behaviour (avg/yr)': 'Antisoziales Verhalten (Durchschn./Jahr)', - 'Criminal damage and arson (avg/yr)': 'Sachbesch\u00E4digung und Brandstiftung (Durchschn./Jahr)', + 'Criminal damage and arson (avg/yr)': 'Sachbeschädigung und Brandstiftung (Durchschn./Jahr)', 'Other theft (avg/yr)': 'Sonstiger Diebstahl (Durchschn./Jahr)', 'Theft from the person (avg/yr)': 'Taschendiebstahl (Durchschn./Jahr)', 'Shoplifting (avg/yr)': 'Ladendiebstahl (Durchschn./Jahr)', 'Bicycle theft (avg/yr)': 'Fahrraddiebstahl (Durchschn./Jahr)', 'Drugs (avg/yr)': 'Drogendelikte (Durchschn./Jahr)', 'Possession of weapons (avg/yr)': 'Waffenbesitz (Durchschn./Jahr)', - 'Public order (avg/yr)': 'St\u00F6rung der \u00F6ffentlichen Ordnung (Durchschn./Jahr)', + 'Public order (avg/yr)': 'Störung der öffentlichen Ordnung (Durchschn./Jahr)', 'Other crime (avg/yr)': 'Sonstige Straftaten (Durchschn./Jahr)', // ─ Feature names (Demographics) ─ 'Median age': 'Medianalter', - '% White': '% Wei\u00DF', - '% South Asian': '% S\u00FCdasiatisch', + '% White': '% Weiß', + '% South Asian': '% Südasiatisch', '% Black': '% Schwarz', '% East Asian': '% Ostasiatisch', '% Mixed': '% Gemischt', '% Other': '% Sonstige', // ─ Feature names (Amenities) ─ - 'Distance to nearest park (km)': 'Entfernung zum n\u00E4chsten Park (km)', + 'Distance to nearest park (km)': 'Entfernung zum nächsten Park (km)', 'Number of parks within 2km': 'Anzahl Parks im Umkreis von 2 km', 'Number of restaurants within 2km': 'Anzahl Restaurants im Umkreis von 2 km', - 'Number of grocery shops and supermarkets within 2km': 'Anzahl Lebensmittelgesch\u00E4fte und Superm\u00E4rkte im Umkreis von 2 km', - 'Noise (dB)': 'L\u00E4rm (dB)', - 'Max available download speed (Mbps)': 'Max. verf\u00FCgbare Downloadgeschwindigkeit (Mbps)', + 'Number of grocery shops and supermarkets within 2km': 'Anzahl Lebensmittelgeschäfte und Supermärkte im Umkreis von 2 km', + 'Noise (dB)': 'Lärm (dB)', + 'Max available download speed (Mbps)': 'Max. verfügbare Downloadgeschwindigkeit (Mbps)', // ─ Enum values ─ @@ -765,7 +774,7 @@ const de: Translations = { 'For sale': 'Zum Verkauf', 'For rent': 'Zur Miete', 'Detached': 'Freistehend', - 'Semi-Detached': 'Doppelhaush\u00E4lfte', + 'Semi-Detached': 'Doppelhaushälfte', 'Terraced': 'Reihenhaus', 'Flats/Maisonettes': 'Wohnungen/Maisonetten', 'Other': 'Sonstige', @@ -780,25 +789,25 @@ const de: Translations = { 'Ethnic composition': 'Ethnische Zusammensetzung', // ─ POI group names ─ - 'Public Transport': '\u00D6ffentlicher Nahverkehr', + 'Public Transport': 'Öffentlicher Nahverkehr', 'Leisure': 'Freizeit', 'Health': 'Gesundheit', 'Emergency Services': 'Rettungsdienste', 'Groceries': 'Lebensmittel', - 'Local Businesses': 'Lokale Gesch\u00E4fte', + 'Local Businesses': 'Lokale Geschäfte', 'Culture': 'Kultur', 'Services': 'Dienstleistungen', - 'Shops': 'Gesch\u00E4fte', + 'Shops': 'Geschäfte', // ─ POI categories ─ 'Airport': 'Flughafen', - 'Ferry': 'F\u00E4hre', + 'Ferry': 'Fähre', 'Rail station': 'Bahnhof', 'Bus stop': 'Bushaltestelle', 'Bus station': 'Busbahnhof', 'Taxi rank': 'Taxistand', - 'Metro or Tram stop': 'U-Bahn- oder Stra\u00DFenbahnhaltestelle', - 'Caf\u00E9': 'Caf\u00E9', + 'Metro or Tram stop': 'U-Bahn- oder Straßenbahnhaltestelle', + 'Café': 'Café', 'Restaurant': 'Restaurant', 'Pub': 'Pub', 'Bar': 'Bar', @@ -812,12 +821,12 @@ const de: Translations = { 'Sports Centre': 'Sportzentrum', 'Entertainment': 'Unterhaltung', 'Supermarket': 'Supermarkt', - 'Convenience Store': 'Sp\u00E4tkauf', - 'Bakery': 'B\u00E4ckerei', - 'Butcher & Fishmonger': 'Metzgerei & Fischh\u00E4ndler', - 'Greengrocer': 'Gem\u00FCseh\u00E4ndler', - 'Off-Licence': 'Getr\u00E4nkeladen', - 'Deli & Specialty': 'Feinkost & Spezialit\u00E4ten', + 'Convenience Store': 'Spätkauf', + 'Bakery': 'Bäckerei', + 'Butcher & Fishmonger': 'Metzgerei & Fischhändler', + 'Greengrocer': 'Gemüsehändler', + 'Off-Licence': 'Getränkeladen', + 'Deli & Specialty': 'Feinkost & Spezialitäten', 'Fashion & Clothing': 'Mode & Bekleidung', 'Electronics': 'Elektronik', 'Charity Shop': 'Secondhand-Laden', @@ -826,18 +835,18 @@ const de: Translations = { 'Bookshop': 'Buchhandlung', 'Pet Shop': 'Tierhandlung', 'Sports & Outdoor': 'Sport & Outdoor', - 'Newsagent': 'Zeitungsh\u00E4ndler', + 'Newsagent': 'Zeitungshändler', 'Department Store': 'Kaufhaus', 'Gift & Hobby': 'Geschenke & Hobby', - 'Specialist Shop': 'Fachgesch\u00E4ft', + 'Specialist Shop': 'Fachgeschäft', 'Hairdresser & Beauty': 'Friseur & Kosmetik', 'Gym & Fitness': 'Fitnessstudio', - 'Dry Cleaner & Laundry': 'Reinigung & W\u00E4scherei', + 'Dry Cleaner & Laundry': 'Reinigung & Wäscherei', 'Car Services': 'Autoservice', 'Post Office': 'Postamt', 'Vet & Pet Care': 'Tierarzt & Tierpflege', 'Bank': 'Bank', - 'Travel Agent': 'Reiseb\u00FCro', + 'Travel Agent': 'Reisebüro', 'Police': 'Polizei', 'Fire Station': 'Feuerwache', 'Ambulance Station': 'Rettungswache', @@ -849,18 +858,18 @@ const de: Translations = { 'Physiotherapy': 'Physiotherapie', 'Counselling & Therapy': 'Beratung & Therapie', 'Care Home': 'Pflegeheim', - 'Medical & Mobility': 'Medizintechnik & Mobilit\u00E4t', + 'Medical & Mobility': 'Medizintechnik & Mobilität', 'Museum': 'Museum', 'Gallery': 'Galerie', 'Library': 'Bibliothek', - 'Place of Worship': 'Gebetsst\u00E4tte', + 'Place of Worship': 'Gebetsstätte', 'Arts Centre': 'Kunstzentrum', 'Zoo': 'Zoo', 'Tourist Attraction': 'Touristenattraktion', 'School': 'Schule', 'Hotel': 'Hotel', - 'Local Business': 'Lokales Gesch\u00E4ft', - 'Offices': 'B\u00FCros', + 'Local Business': 'Lokales Geschäft', + 'Offices': 'Büros', 'EV Charging': 'E-Ladestation', 'Fuel Station': 'Tankstelle', 'Community Centre': 'Gemeindezentrum', @@ -868,7 +877,7 @@ const de: Translations = { // ─ Suffixes (used in formatters) ─ '/mo': '/Monat', '/yr': '/Jahr', - ' sqm': ' m\u00B2', + ' sqm': ' m²', ' km': ' km', ' m': ' m', ' dB': ' dB', diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 182c568..4eb8821 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -139,6 +139,11 @@ const en = { removeFilterHint: 'Remove a filter to see available features', featureInfo: 'Feature info', replayTutorial: 'Replay interactive tutorial', + clearAll: 'Clear all', + clearAllTitle: 'Clear all filters?', + clearAllSavePrompt: 'Would you like to save your current filters before clearing?', + saveAndClear: 'Save & Clear', + clearWithoutSaving: 'Clear without saving', }, // ── Philosophy Popup ─────────────────────────────── @@ -199,9 +204,9 @@ const en = { describeIdealArea: 'Describe your ideal area with AI', aiSearch: 'AI Search', describeHint: "describe what you're looking for", - placeholder: 'e.g. quiet area, under \u00A3400k, near good schools...', + placeholder: 'e.g. quiet area, under £400k, near good schools...', example1: 'Safe area near good schools', - example2: '30 min commute to Kings Cross, under \u00A3500k', + example2: '30 min commute to Kings Cross, under £500k', example3: 'Quiet village, 3 bed, fast broadband', analysing: 'Analysing your query...', searchingDestinations: 'Searching for destinations...', @@ -213,6 +218,11 @@ const en = { // ── Map Legend ───────────────────────────────────── mapLegend: { clearColourView: 'Clear colour view', + historicalMatches: 'Historical property matches', + propertiesForSale: 'Properties for sale', + propertiesForRent: 'Properties for rent', + numberOfProperties: 'Number of properties', + previewing: 'Previewing \u201c{{name}}\u201d', }, // ── Properties Pane ──────────────────────────────── @@ -220,7 +230,7 @@ const en = { unknownAddress: 'Unknown Address', unsaveProperty: 'Unsave property', saveProperty: 'Save property', - lastSold: 'Last sold: \u00A3{{price}}', + lastSold: 'Last sold: £{{price}}', estValue: 'Est. value:', type: 'Type:', builtForm: 'Built form:', @@ -237,7 +247,7 @@ const en = { renovations: 'Renovations', viewExternalListing: 'View external listing', perMonth: '/mo', - perSqm: '/m\u00B2', + perSqm: '/m²', searchPlaceholder: 'Search by address or postcode...', propertyData: 'Property Data', propertyDataDesc: 'Prices come from HM Land Registry (what buyers actually paid). Floor area, energy ratings, construction year, and tenure come from official EPC surveys. Both sources are matched by address within each postcode.', @@ -322,16 +332,16 @@ const en = { philosophyP2: 'We flip that. Tell us what you need (budget, commute, schools, safety) and we show you every area in England that qualifies. No guesswork. No wasted viewings.', howToUseIt: 'How to use it', howStep1Title: 'Set your must-haves', - howStep1Desc: 'Budget, commute, schools \u2014 the map shows only what qualifies.', + howStep1Desc: 'Budget, commute, schools — the map shows only what qualifies.', howStep2Title: 'Explore areas and discover hidden gems', howStep2Desc: 'Zoom in, dig into details and nice to haves.', howStep3Title: 'Drill into postcodes', howStep3Desc: 'See individual properties, sale prices, floor area, and compare.', howStep4Title: 'Shortlist with confidence', - howStep4Desc: 'Every area on your list meets your actual criteria \u2014 not just what was listed that week.', + howStep4Desc: 'Every area on your list meets your actual criteria — not just what was listed that week.', othersVs: 'Others vs', listingPortals: 'Listing portals', - checkMyPostcode: '\u201CCheck my postcode\u201D', + checkMyPostcode: '“Check my postcode”', areaGuides: 'Area guides', compSearchWithout: 'Search without choosing an area first', compSearchWithoutSub: '(start with needs, not a location)', @@ -341,7 +351,7 @@ const en = { compPropertyDataSub: '(price, EPC, floor area)', compFilters: '56 combinable filters in one place', compFiltersSub: '(all insights, one interactive map)', - ctaTitle: 'Make your biggest investment your smartest\u00A0move.', + ctaTitle: 'Make your biggest investment your smartest move.', ctaDescription: "This deserves proper tools behind it, don't leave it to luck.", }, @@ -349,7 +359,7 @@ const en = { pricingPage: { title: 'Early access pricing', subtitle: 'Pay once, access forever. The earlier you join, the less you pay.', - costContext: "Buying a home costs \u00A310k+ in stamp duty, \u00A31,500 in solicitor fees, \u00A3500 for a survey. Get the wrong area and you're stuck with a long commute, bad schools, or a road you didn't know about.", + costContext: "Buying a home costs £10k+ in stamp duty, £1,500 in solicitor fees, £500 for a survey. Get the wrong area and you're stuck with a long commute, bad schools, or a road you didn't know about.", lessThanSurvey: 'Less than a home survey. Far more useful.', currentTier: 'Current tier', firstNUsers: 'First {{count}} users', @@ -386,13 +396,13 @@ const en = { source: 'Source:', optOut: 'Opt out of public disclosure', attribution: 'Attribution', - attrLandRegistry: 'Contains HM Land Registry data \u00A9 Crown copyright and database right 2025.', + attrLandRegistry: 'Contains HM Land Registry data © Crown copyright and database right 2025.', attrOgl: 'Contains public sector information licensed under the', attrOglLink: 'Open Government Licence v3.0', - attrOs: 'Contains OS data \u00A9 Crown copyright and database rights 2025.', + attrOs: 'Contains OS data © Crown copyright and database rights 2025.', attrTfl: 'Powered by TfL Open Data.', attrOsm: 'Contains data from', - attrOsmContrib: '\u00A9 OpenStreetMap contributors', + attrOsmContrib: '© OpenStreetMap contributors', attrOsmLicense: 'available under the', attrOsmLicenseLink: 'Open Data Commons Open Database License (ODbL)', // Data source names & descriptions @@ -497,12 +507,12 @@ const en = { faqPricing3Q: 'What can I access on the free tier?', faqPricing3A: 'Free users can explore all features within the demo area (inner London, roughly zones 1 to 2). To access data for the rest of England, you need lifetime access.', faqPricing4Q: 'Can I get a refund?', - faqPricing4A: 'Absolutely. We offer a 30-day money-back guarantee. If you\u2019re not satisfied, email support@perfect-postcode.co.uk within 30 days for a full refund.', + faqPricing4A: 'Absolutely. We offer a 30-day money-back guarantee. If you’re not satisfied, email support@perfect-postcode.co.uk within 30 days for a full refund.', // FAQ items — Tips and Tricks faqTips1Q: 'How do I use the AI filter instead of adding filters one by one?', - faqTips1A: 'Type what you want in plain English, something like "quiet area near good schools with fast broadband under \u00A3400k", and it\'ll set up all the relevant filters in one go. Tweak any of them manually afterwards.', + faqTips1A: 'Type what you want in plain English, something like "quiet area near good schools with fast broadband under £400k", and it\'ll set up all the relevant filters in one go. Tweak any of them manually afterwards.', faqTips2Q: 'Can I save a search and come back to it later?', - faqTips2A: 'Hit the save button and everything is captured: your filters, zoom level, and which data layer you\u2019re colouring by. Pick up exactly where you left off or share the link with your partner.', + faqTips2A: 'Hit the save button and everything is captured: your filters, zoom level, and which data layer you’re colouring by. Pick up exactly where you left off or share the link with your partner.', faqTips3Q: "Can I export the data I'm looking at?", faqTips3A: 'Use the export button to download the currently filtered properties as a spreadsheet. The export respects all your active filters, so you get exactly the data you want.', }, @@ -512,7 +522,7 @@ const en = { emailLabel: 'Email', subscriptionLabel: 'Subscription', upgrade: 'Upgrade', - redirecting: 'Redirecting\u2026', + redirecting: 'Redirecting…', receiveNewsletter: 'Receive newsletter emails', needHelp: 'Need help? Email us at', responseTime: 'We typically respond within 24 hours.', @@ -613,15 +623,15 @@ const en = { step1Title: 'Tell the map what matters', step1Content: 'Set your budget, commute limit, school quality, crime threshold. Whatever matters to you. Only areas that qualify stay lit. Use the eye icon to colour by any feature.', step2Title: 'Or just describe it', - step2Content: 'Type what you want in plain English, like "quiet area near good schools under \u00A3400k", and we\u2019ll set up the filters for you.', - step3Title: 'Explore what\u2019s out there', + step2Content: 'Type what you want in plain English, like "quiet area near good schools under £400k", and we’ll set up the filters for you.', + step3Title: 'Explore what’s out there', step3Content: 'Pan and zoom across England. Click any coloured area to see crime, schools, prices, broadband, noise, and more about that neighbourhood.', step4Title: 'Jump to a location', step4Content: 'Search for any place or postcode to fly straight there.', step5Title: 'Dig into the details', step5Content: 'See area statistics, histograms, and individual property records: prices, floor area, energy ratings, and more.', - step6Title: 'What\u2019s nearby?', - step6Content: 'Toggle schools, shops, stations, parks, and restaurants on the map to see what\u2019s within reach.', + step6Title: 'What’s nearby?', + step6Content: 'Toggle schools, shops, stations, parks, and restaurants on the map to see what’s within reach.', }, // ── Server-derived values ────────────────────────── @@ -663,7 +673,6 @@ const en = { // ─ Feature names (Transport) ─ 'Distance to nearest train or tube station (km)': 'Distance to nearest train or tube station (km)', - 'Train or tube stations within 1km': 'Train or tube stations within 1km', // ─ Feature names (Education) ─ 'Good+ primary schools within 2km': 'Good+ primary schools within 2km', @@ -756,7 +765,7 @@ const en = { 'Bus station': 'Bus station', 'Taxi rank': 'Taxi rank', 'Metro or Tram stop': 'Metro or Tram stop', - 'Caf\u00E9': 'Caf\u00E9', + 'Café': 'Café', 'Restaurant': 'Restaurant', 'Pub': 'Pub', 'Bar': 'Bar', diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts index 6716e06..837934a 100644 --- a/frontend/src/i18n/locales/fr.ts +++ b/frontend/src/i18n/locales/fr.ts @@ -10,27 +10,27 @@ const fr: Translations = { open: 'Ouvrir', share: 'Partager', copy: 'Copier', - copied: 'Copi\u00E9 !', - copiedToClipboard: 'Copi\u00E9 dans le presse-papiers', + copied: 'Copié !', + copiedToClipboard: 'Copié dans le presse-papiers', loading: 'Chargement...', loadMore: 'Charger plus', remaining: '{{count}} restant(s)', search: 'Rechercher', all: 'Tous', none: 'Aucun', - viewDataSource: 'Voir la source des donn\u00E9es', + viewDataSource: 'Voir la source des données', total: 'Total', min: 'min', or: 'ou', area: 'Zone', - properties: 'Propri\u00E9t\u00E9s', + properties: 'Propriétés', postcode: 'Code postal', - noAreaSelected: 'Aucune zone s\u00E9lectionn\u00E9e', + noAreaSelected: 'Aucune zone sélectionnée', noAreaSelectedDesc: - 'Cliquez sur une zone color\u00E9e de la carte pour voir la criminalit\u00E9, les \u00E9coles, les prix et plus encore', - clickForDetails: 'Cliquez pour les d\u00E9tails', - property: 'propri\u00E9t\u00E9', - propertiesPlural: 'propri\u00E9t\u00E9s', + 'Cliquez sur une zone colorée de la carte pour voir la criminalité, les écoles, les prix et plus encore', + clickForDetails: 'Cliquez pour les détails', + property: 'propriété', + propertiesPlural: 'propriétés', }, // ── Header / Nav ─────────────────────────────────── @@ -40,9 +40,9 @@ const fr: Translations = { learn: 'En savoir plus', pricing: 'Tarifs', inviteFriends: 'Inviter des amis', - saved: 'Enregistr\u00E9s', + saved: 'Enregistrés', logIn: 'Se connecter', - createAccount: 'Cr\u00E9er un compte', + createAccount: 'Créer un compte', sharing: 'Partage en cours...', exportLabel: 'Exporter', exporting: 'Exportation...', @@ -53,12 +53,12 @@ const fr: Translations = { // ── User Menu ────────────────────────────────────── userMenu: { - fullAccess: 'Acc\u00E8s complet', - demo: 'D\u00E9mo', - themeLight: 'Th\u00E8me : Clair', - themeDark: 'Th\u00E8me : Sombre', + fullAccess: 'Accès complet', + demo: 'Démo', + themeLight: 'Thème : Clair', + themeDark: 'Thème : Sombre', account: 'Compte', - logOut: 'Se d\u00E9connecter', + logOut: 'Se déconnecter', }, // ── Mobile Menu ──────────────────────────────────── @@ -70,48 +70,48 @@ const fr: Translations = { // ── Auth Modal ───────────────────────────────────── auth: { logIn: 'Se connecter', - createAccount: 'Cr\u00E9er un compte', - resetPassword: 'R\u00E9initialiser le mot de passe', + createAccount: 'Créer un compte', + resetPassword: 'Réinitialiser le mot de passe', valueProp: - 'Enregistrez vos recherches, ajoutez des propri\u00E9t\u00E9s en favoris et reprenez l\u00E0 o\u00F9 vous vous \u00E9tiez arr\u00EAt\u00E9.', + 'Enregistrez vos recherches, ajoutez des propriétés en favoris et reprenez là où vous vous étiez arrêté.', continueWithGoogle: 'Continuer avec Google', email: 'E-mail', emailPlaceholder: 'vous@exemple.com', password: 'Mot de passe', - passwordPlaceholderRegister: '8 caract\u00E8res minimum', + passwordPlaceholderRegister: '8 caractères minimum', passwordPlaceholderLogin: 'Votre mot de passe', - forgotPassword: 'Mot de passe oubli\u00E9 ?', - resetSent: 'V\u00E9rifiez votre bo\u00EEte e-mail pour le lien de r\u00E9initialisation.', + forgotPassword: 'Mot de passe oublié ?', + resetSent: 'Vérifiez votre boîte e-mail pour le lien de réinitialisation.', pleaseWait: 'Veuillez patienter...', - sendResetLink: 'Envoyer le lien de r\u00E9initialisation', - backToLogin: 'Retour \u00E0 la connexion', + sendResetLink: 'Envoyer le lien de réinitialisation', + backToLogin: 'Retour à la connexion', }, // ── Upgrade Modal ────────────────────────────────── upgrade: { - title: "D\u00E9couvrez toute l'Angleterre", + title: "Découvrez toute l'Angleterre", description: - "Vous explorez actuellement la zone de d\u00E9monstration. Obtenez un acc\u00E8s \u00E0 vie \u00E0 chaque code postal, chaque filtre, chaque quartier. Un seul paiement, pour toujours.", + "Vous explorez actuellement la zone de démonstration. Obtenez un accès à vie à chaque code postal, chaque filtre, chaque quartier. Un seul paiement, pour toujours.", free: 'Gratuit', once: '/unique', freeForEarly: 'Gratuit pour les premiers utilisateurs. Aucune carte bancaire requise.', oneTimePayment: - 'Paiement unique. Acc\u00E8s \u00E0 vie. Garantie satisfait ou rembours\u00E9 sous 30 jours.', + 'Paiement unique. Accès à vie. Garantie satisfait ou remboursé sous 30 jours.', redirecting: 'Redirection...', - claimFreeAccess: "R\u00E9clamer l'acc\u00E8s gratuit", - upgradeFor: 'Passer \u00E0 la version compl\u00E8te pour {{price}}', - registerAndUpgrade: "S'inscrire et passer \u00E0 la version compl\u00E8te", - alreadyHaveAccount: 'Vous avez d\u00E9j\u00E0 un compte ? Connectez-vous', - continueWithDemo: 'Continuer avec la d\u00E9mo', - checkoutFailed: '\u00C9chec du paiement', + claimFreeAccess: "Réclamer l'accès gratuit", + upgradeFor: 'Passer à la version complète pour {{price}}', + registerAndUpgrade: "S'inscrire et passer à la version complète", + alreadyHaveAccount: 'Vous avez déjà un compte ? Connectez-vous', + continueWithDemo: 'Continuer avec la démo', + checkoutFailed: 'Échec du paiement', }, // ── Save Search Modal ────────────────────────────── saveSearch: { title: 'Enregistrer la recherche', - saved: 'Recherche enregistr\u00E9e', - savedSuccess: 'Votre recherche a \u00E9t\u00E9 enregistr\u00E9e avec succ\u00E8s.', - viewSavedSearches: 'Voir les recherches enregistr\u00E9es', + saved: 'Recherche enregistrée', + savedSuccess: 'Votre recherche a été enregistrée avec succès.', + viewSavedSearches: 'Voir les recherches enregistrées', name: 'Nom', namePlaceholder: 'Ma recherche', saving: 'Enregistrement...', @@ -120,10 +120,10 @@ const fr: Translations = { // ── License Success ──────────────────────────────── licenseSuccess: { title: "C'est fait.", - subtitle: 'Votre acc\u00E8s \u00E0 vie est maintenant actif.', + subtitle: 'Votre accès à vie est maintenant actif.', description: - "Acc\u00E8s complet \u00E0 chaque fonctionnalit\u00E9, chaque code postal, dans toute l'Angleterre.", - startExploring: 'Commencer \u00E0 explorer', + "Accès complet à chaque fonctionnalité, chaque code postal, dans toute l'Angleterre.", + startExploring: 'Commencer à explorer', }, // ── Filters ──────────────────────────────────────── @@ -133,104 +133,114 @@ const fr: Translations = { historical: 'Historique', buy: 'Acheter', rent: 'Louer', - findingPerfectPostcode: 'Trouver le code postal id\u00E9al', + findingPerfectPostcode: 'Trouver le code postal idéal', addFiltersHint: - 'Ajoutez des filtres ci-dessous pour restreindre la carte aux zones correspondant \u00E0 vos crit\u00E8res', + 'Ajoutez des filtres ci-dessous pour restreindre la carte aux zones correspondant à vos critères', upgradePrompt: - "Voir la criminalit\u00E9, les \u00E9coles, le bruit, le d\u00E9bit internet et plus de 50 filtres dans toute l'Angleterre.", - oneTimeLifetime: 'Paiement unique, acc\u00E8s \u00E0 vie.', - upgradeToFullMap: 'Passer \u00E0 la carte compl\u00E8te', + "Voir la criminalité, les écoles, le bruit, le débit internet et plus de 50 filtres dans toute l'Angleterre.", + oneTimeLifetime: 'Paiement unique, accès à vie.', + upgradeToFullMap: 'Passer à la carte complète', chooseFilters: - 'Choisissez les filtres qui comptent pour vous. La carte se met \u00E0 jour en temps r\u00E9el.', - searchFeatures: 'Rechercher des crit\u00E8res...', - noMatchingFeatures: 'Aucun crit\u00E8re correspondant', + 'Choisissez les filtres qui comptent pour vous. La carte se met à jour en temps réel.', + searchFeatures: 'Rechercher des critères...', + noMatchingFeatures: 'Aucun critère correspondant', tryDifferentSearch: 'Essayez un autre terme de recherche', - allFeaturesActive: 'Tous les crit\u00E8res sont actifs', - removeFilterHint: 'Supprimez un filtre pour voir les crit\u00E8res disponibles', - featureInfo: 'Informations sur le crit\u00E8re', + allFeaturesActive: 'Tous les critères sont actifs', + removeFilterHint: 'Supprimez un filtre pour voir les critères disponibles', + featureInfo: 'Informations sur le critère', replayTutorial: 'Rejouer le tutoriel interactif', + clearAll: 'Tout effacer', + clearAllTitle: 'Effacer tous les filtres ?', + clearAllSavePrompt: 'Souhaitez-vous sauvegarder vos filtres actuels avant de les effacer ?', + saveAndClear: 'Sauvegarder et effacer', + clearWithoutSaving: 'Effacer sans sauvegarder', }, // ── Philosophy Popup ─────────────────────────────── philosophy: { intro: - "Commencez par vos crit\u00E8res indispensables, puis ajoutez les crit\u00E8res souhait\u00E9s. La carte se r\u00E9duit au fur et \u00E0 mesure que vous ajoutez des filtres. Les zones restantes sont vos meilleures correspondances.", + "Commencez par vos critères indispensables, puis ajoutez les critères souhaités. La carte se réduit au fur et à mesure que vous ajoutez des filtres. Les zones restantes sont vos meilleures correspondances.", step1Title: 'Budget et fondamentaux', step1Desc: '(fourchette de prix, surface, type de bien)', step2Title: 'Trajet', - step2Desc: '(temps de trajet vers votre lieu de travail en voiture, v\u00E9lo ou transports)', - step3Title: 'S\u00E9curit\u00E9', - step3Desc: '(taux de criminalit\u00E9, niveaux de bruit, stabilit\u00E9 du sol)', - step4Title: '\u00C9coles', - step4Desc: '(proximit\u00E9 d\u2019\u00E9coles not\u00E9es Bien ou Excellent par Ofsted)', + step2Desc: '(temps de trajet vers votre lieu de travail en voiture, vélo ou transports)', + step3Title: 'Sécurité', + step3Desc: '(taux de criminalité, niveaux de bruit, stabilité du sol)', + step4Title: 'Écoles', + step4Desc: '(proximité d’écoles notées Bien ou Excellent par Ofsted)', step5Title: 'Cadre de vie', - step5Desc: '(restaurants, parcs, d\u00E9bit internet)', - step6Title: '\u00C9nergie', - step6Desc: '(classements DPE, isolation, co\u00FBts de chauffage)', - tip: "Astuce : si rien ne correspond, assouplissez un crit\u00E8re \u00E0 la fois pour voir quel compromis ouvre le plus d'options.", + step5Desc: '(restaurants, parcs, débit internet)', + step6Title: 'Énergie', + step6Desc: '(classements DPE, isolation, coûts de chauffage)', + tip: "Astuce : si rien ne correspond, assouplissez un critère à la fois pour voir quel compromis ouvre le plus d'options.", }, // ── Travel Time ──────────────────────────────────── travel: { travelTime: 'Temps de trajet ({{mode}})', maxTime: 'Temps maximum', - selectDestination: 'S\u00E9lectionner une destination...', + selectDestination: 'Sélectionner une destination...', bestCase: 'Meilleur cas', bestCaseTitle: 'Meilleur temps de trajet', bestCaseDesc: - "Utilise le temps de trajet r\u00E9aliste le plus rapide (si vous partez au bon moment et avez de bonnes correspondances). Par d\u00E9faut, la m\u00E9diane est utilis\u00E9e, repr\u00E9sentant un trajet typique quelle que soit l'heure de d\u00E9part.", - previewOnMap: 'Aper\u00E7u sur la carte', - stopPreviewing: "Arr\u00EAter l'aper\u00E7u", + "Utilise le temps de trajet réaliste le plus rapide (si vous partez au bon moment et avez de bonnes correspondances). Par défaut, la médiane est utilisée, représentant un trajet typique quelle que soit l'heure de départ.", + previewOnMap: 'Aperçu sur la carte', + stopPreviewing: "Arrêter l'aperçu", removeTravelTime: 'Supprimer le temps de trajet', addTravelTime: 'Ajouter le temps de trajet en {{mode}}', clearDestination: 'Effacer la destination', typeToFilter: 'Tapez pour filtrer...', - noDestinations: 'Aucune destination trouv\u00E9e', + noDestinations: 'Aucune destination trouvée', modeCar: 'Voiture', - modeBicycle: 'V\u00E9lo', + modeBicycle: 'Vélo', modeWalking: 'Marche', modeTransit: 'Transports', - modeCarDesc: 'Temps de conduite via l\u2019itin\u00E9raire routier le plus rapide', - modeBicycleDesc: 'Temps de trajet \u00E0 v\u00E9lo via des itin\u00E9raires cyclables', - modeWalkingDesc: 'Temps de marche le long des chemins pi\u00E9tons et trottoirs', - modeTransitDesc: 'Temps de trajet en train, m\u00E9tro et bus', + modeCarDesc: 'Temps de conduite via l’itinéraire routier le plus rapide', + modeBicycleDesc: 'Temps de trajet à vélo via des itinéraires cyclables', + modeWalkingDesc: 'Temps de marche le long des chemins piétons et trottoirs', + modeTransitDesc: 'Temps de trajet en train, métro et bus', }, // ── Travel Time Info Popup ───────────────────────── travelInfo: { transitDesc: - ' en transports en commun (bus, train, m\u00E9tro). Les temps sont calcul\u00E9s sur une fen\u00EAtre typique d\u2019un matin de semaine.', + ' en transports en commun (bus, train, métro). Les temps sont calculés sur une fenêtre typique d’un matin de semaine.', carDesc: - ' en voiture, bas\u00E9 sur les vitesses de circulation habituelles et le r\u00E9seau routier.', - bicycleDesc: ' \u00E0 v\u00E9lo, via des itin\u00E9raires adapt\u00E9s aux cyclistes.', - walkingDesc: ' \u00E0 pied, via les chemins pi\u00E9tons et trottoirs.', + ' en voiture, basé sur les vitesses de circulation habituelles et le réseau routier.', + bicycleDesc: ' à vélo, via des itinéraires adaptés aux cyclistes.', + walkingDesc: ' à pied, via les chemins piétons et trottoirs.', mainDesc: - 'Affiche le temps n\u00E9cessaire pour atteindre la destination s\u00E9lectionn\u00E9e depuis chaque zone', + 'Affiche le temps nécessaire pour atteindre la destination sélectionnée depuis chaque zone', sliderHint: - 'Utilisez le curseur pour d\u00E9finir votre temps de trajet maximum.', + 'Utilisez le curseur pour définir votre temps de trajet maximum.', }, // ── AI Filter ────────────────────────────────────── aiFilter: { - describeIdealArea: 'D\u00E9crivez votre zone id\u00E9ale avec l\u2019IA', + describeIdealArea: 'Décrivez votre zone idéale avec l’IA', aiSearch: 'Recherche IA', - describeHint: 'd\u00E9crivez ce que vous recherchez', + describeHint: 'décrivez ce que vous recherchez', placeholder: - 'ex. quartier calme, moins de \u00A3400k, pr\u00E8s de bonnes \u00E9coles...', - example1: 'Quartier s\u00FBr pr\u00E8s de bonnes \u00E9coles', - example2: '30 min de trajet jusqu\u2019\u00E0 Kings Cross, moins de \u00A3500k', - example3: 'Village tranquille, 3 chambres, d\u00E9bit internet rapide', - analysing: 'Analyse de votre requ\u00EAte...', + 'ex. quartier calme, moins de £400k, près de bonnes écoles...', + example1: 'Quartier sûr près de bonnes écoles', + example2: '30 min de trajet jusqu’à Kings Cross, moins de £500k', + example3: 'Village tranquille, 3 chambres, débit internet rapide', + analysing: 'Analyse de votre requête...', searchingDestinations: 'Recherche de destinations...', - generatingFilters: 'G\u00E9n\u00E9ration des filtres...', - refiningResults: 'Affinage des r\u00E9sultats...', + generatingFilters: 'Génération des filtres...', + refiningResults: 'Affinage des résultats...', weeklyLimitReached: - 'Vous avez atteint la limite hebdomadaire d\u2019utilisation de l\u2019IA. Elle se r\u00E9initialisera automatiquement la semaine prochaine.', + 'Vous avez atteint la limite hebdomadaire d’utilisation de l’IA. Elle se réinitialisera automatiquement la semaine prochaine.', }, // ── Map Legend ───────────────────────────────────── mapLegend: { clearColourView: 'Effacer la vue en couleur', + historicalMatches: 'Correspondances immobilières historiques', + propertiesForSale: 'Propriétés à vendre', + propertiesForRent: 'Propriétés à louer', + numberOfProperties: 'Nombre de propriétés', + previewing: 'Aperçu de \u201c{{name}}\u201d', }, // ── Properties Pane ──────────────────────────────── @@ -238,52 +248,52 @@ const fr: Translations = { unknownAddress: 'Adresse inconnue', unsaveProperty: 'Retirer des favoris', saveProperty: 'Ajouter aux favoris', - lastSold: 'Derni\u00E8re vente : \u00A3{{price}}', - estValue: 'Valeur estim\u00E9e :', + lastSold: 'Dernière vente : £{{price}}', + estValue: 'Valeur estimée :', type: 'Type :', - builtForm: 'Forme du b\u00E2ti :', - tenure: 'R\u00E9gime foncier :', + builtForm: 'Forme du bâti :', + tenure: 'Régime foncier :', floorArea: 'Surface :', bedrooms: 'Chambres :', bathrooms: 'Salles de bain :', - rooms: 'Pi\u00E8ces :', + rooms: 'Pièces :', built: 'Construction :', epcRating: 'Classement DPE :', epcPotential: 'Potentiel DPE :', listed: 'Mise en vente :', - keyFeatures: 'Caract\u00E9ristiques cl\u00E9s', - renovations: 'R\u00E9novations', - viewExternalListing: 'Voir l\u2019annonce externe', + keyFeatures: 'Caractéristiques clés', + renovations: 'Rénovations', + viewExternalListing: 'Voir l’annonce externe', perMonth: '/mois', - perSqm: '/m\u00B2', + perSqm: '/m²', searchPlaceholder: 'Rechercher par adresse ou code postal...', - propertyData: 'Donn\u00E9es immobili\u00E8res', + propertyData: 'Données immobilières', propertyDataDesc: - 'Les prix proviennent du HM Land Registry (ce que les acheteurs ont r\u00E9ellement pay\u00E9). La surface, les classements \u00E9nerg\u00E9tiques, l\u2019ann\u00E9e de construction et le r\u00E9gime foncier proviennent des diagnostics DPE officiels. Les deux sources sont reli\u00E9es par adresse au sein de chaque code postal.', + 'Les prix proviennent du HM Land Registry (ce que les acheteurs ont réellement payé). La surface, les classements énergétiques, l’année de construction et le régime foncier proviennent des diagnostics DPE officiels. Les deux sources sont reliées par adresse au sein de chaque code postal.', }, // ── Area Pane ────────────────────────────────────── areaPane: { areaStatistics: 'Statistiques de la zone', - statsFor: 'Statistiques pour toutes les propri\u00E9t\u00E9s de ce/cette {{type}}', - matchingFilters: ' correspondant \u00E0 tous les filtres actifs', - viewProperties: 'Voir {{count}} propri\u00E9t\u00E9s', + statsFor: 'Statistiques pour toutes les propriétés de ce/cette {{type}}', + matchingFilters: ' correspondant à tous les filtres actifs', + viewProperties: 'Voir {{count}} propriétés', priceHistory: 'Historique des prix', journeysFrom: 'Trajets depuis {{label}}', to: 'Vers {{destination}}', - noJourneyData: 'Aucune donn\u00E9e de trajet disponible', + noJourneyData: 'Aucune donnée de trajet disponible', viewOnGoogleMaps: 'Voir sur Google Maps', walk: 'Marche', - cycle: 'V\u00E9lo', + cycle: 'Vélo', }, // ── Histogram Legend ─────────────────────────────── histogramLegend: { tealBars: 'Barres turquoise', - tealBarsDesc: 'montrent la distribution dans cette zone s\u00E9lectionn\u00E9e', + tealBarsDesc: 'montrent la distribution dans cette zone sélectionnée', greyBars: 'Barres grises', greyBarsDesc: 'montrent la distribution globale dans toutes les zones', - dashedLine: 'Ligne pointill\u00E9e', + dashedLine: 'Ligne pointillée', dashedLineDesc: 'indique la moyenne nationale', }, @@ -295,10 +305,10 @@ const fr: Translations = { // ── POI Pane ─────────────────────────────────────── poiPane: { pois: 'POI', - pointsOfInterest: "Points d'int\u00E9r\u00EAt", + pointsOfInterest: "Points d'intérêt", poiDescription: - "Donn\u00E9es issues d'OpenStreetMap. Couvre les arr\u00EAts de transport, commerces, restaurants, \u00E9tablissements de sant\u00E9, loisirs et plus encore. Mise \u00E0 jour r\u00E9guli\u00E8re avec une couverture compl\u00E8te des cat\u00E9gories.", - searchCategories: 'Rechercher des cat\u00E9gories...', + "Données issues d'OpenStreetMap. Couvre les arrêts de transport, commerces, restaurants, établissements de santé, loisirs et plus encore. Mise à jour régulière avec une couverture complète des catégories.", + searchCategories: 'Rechercher des catégories...', dataSourceInfo: 'Informations sur la source', }, @@ -312,11 +322,11 @@ const fr: Translations = { locationSearch: { placeholder: 'Rechercher des lieux ou codes postaux...', postcodeNotFound: 'Code postal introuvable', - lookupFailed: '\u00C9chec de la recherche', + lookupFailed: 'Échec de la recherche', searchLabel: 'Rechercher des lieux ou codes postaux', - locateMe: 'Aller \u00E0 ma position', - geolocationUnsupported: 'La g\u00E9olocalisation n\'est pas prise en charge par votre navigateur', - geolocationFailed: 'Impossible de d\u00E9terminer votre position', + locateMe: 'Aller à ma position', + geolocationUnsupported: 'La géolocalisation n\'est pas prise en charge par votre navigateur', + geolocationFailed: 'Impossible de déterminer votre position', }, // ── Mobile Drawer ────────────────────────────────── @@ -330,63 +340,63 @@ const fr: Translations = { heroTitle2: 'Maximale', heroTitle3: 'Compromis Minimum.', heroSubtitle: - 'Vous cherchez un bien ? Faites de votre plus gros investissement votre meilleure d\u00E9cision.', + 'Vous cherchez un bien ? Faites de votre plus gros investissement votre meilleure décision.', heroDescription: - "Tant d'options \u2014 choisir la bonne peut sembler d\u00E9courageant. Notre carte interactive simplifie tout : s\u00E9lectionnez vos crit\u00E8res et voyez instantan\u00E9ment les zones qui correspondent.", + "Tant d'options — choisir la bonne peut sembler décourageant. Notre carte interactive simplifie tout : sélectionnez vos critères et voyez instantanément les zones qui correspondent.", exploreTheMap: 'Explorer la carte', - seeTheDifference: 'Voir la diff\u00E9rence', - statProperties: 'propri\u00E9t\u00E9s', + seeTheDifference: 'Voir la différence', + statProperties: 'propriétés', statFilters: 'filtres', statEvery: 'Chaque', statPostcodeInEngland: "code postal d'Angleterre", ourPhilosophy: 'Notre philosophie', philosophyP1: - "Sur Rightmove, vous choisissez d'abord une zone, puis vous esp\u00E9rez qu'elle convient. Vous finissez par croiser statistiques de criminalit\u00E9, rapports scolaires et tests de d\u00E9bit sur une dizaine d'onglets, un code postal \u00E0 la fois.", + "Sur Rightmove, vous choisissez d'abord une zone, puis vous espérez qu'elle convient. Vous finissez par croiser statistiques de criminalité, rapports scolaires et tests de débit sur une dizaine d'onglets, un code postal à la fois.", philosophyP2: - "Nous inversons la logique. Dites-nous ce qu'il vous faut (budget, trajet, \u00E9coles, s\u00E9curit\u00E9) et nous vous montrons chaque zone d'Angleterre qui correspond. Plus de devinettes. Plus de visites inutiles.", - howToUseIt: 'Comment l\u2019utiliser', - howStep1Title: 'D\u00E9finissez vos indispensables', + "Nous inversons la logique. Dites-nous ce qu'il vous faut (budget, trajet, écoles, sécurité) et nous vous montrons chaque zone d'Angleterre qui correspond. Plus de devinettes. Plus de visites inutiles.", + howToUseIt: 'Comment l’utiliser', + howStep1Title: 'Définissez vos indispensables', howStep1Desc: - 'Budget, trajet, \u00E9coles \u2014 la carte n\u2019affiche que ce qui correspond.', - howStep2Title: 'Explorez les zones et d\u00E9couvrez des p\u00E9pites cach\u00E9es', - howStep2Desc: 'Zoomez, examinez les d\u00E9tails et les crit\u00E8res secondaires.', + 'Budget, trajet, écoles — la carte n’affiche que ce qui correspond.', + howStep2Title: 'Explorez les zones et découvrez des pépites cachées', + howStep2Desc: 'Zoomez, examinez les détails et les critères secondaires.', howStep3Title: 'Plongez dans les codes postaux', howStep3Desc: - 'Consultez les propri\u00E9t\u00E9s individuelles, les prix de vente, la surface et comparez.', - howStep4Title: 'Constituez votre s\u00E9lection en toute confiance', + 'Consultez les propriétés individuelles, les prix de vente, la surface et comparez.', + howStep4Title: 'Constituez votre sélection en toute confiance', howStep4Desc: - 'Chaque zone de votre liste r\u00E9pond \u00E0 vos vrais crit\u00E8res \u2014 pas seulement \u00E0 ce qui \u00E9tait en vente cette semaine-l\u00E0.', + 'Chaque zone de votre liste répond à vos vrais critères — pas seulement à ce qui était en vente cette semaine-là.', othersVs: 'Les autres vs', listingPortals: "Portails d'annonces", - checkMyPostcode: '\u00AB V\u00E9rifier mon code postal \u00BB', + checkMyPostcode: '« Vérifier mon code postal »', areaGuides: 'Guides de quartier', compSearchWithout: "Rechercher sans d'abord choisir une zone", compSearchWithoutSub: "(partir de ses besoins, pas d'un lieu)", - compAreaData: 'Donn\u00E9es de la zone', - compAreaDataSub: '(criminalit\u00E9, \u00E9coles, bruit, d\u00E9bit internet)', - compPropertyData: 'Donn\u00E9es par propri\u00E9t\u00E9', + compAreaData: 'Données de la zone', + compAreaDataSub: '(criminalité, écoles, bruit, débit internet)', + compPropertyData: 'Données par propriété', compPropertyDataSub: '(prix, DPE, surface)', compFilters: '56 filtres combinables en un seul endroit', compFiltersSub: '(toutes les informations, une seule carte interactive)', ctaTitle: - 'Faites de votre plus gros investissement votre meilleure\u00A0d\u00E9cision.', + 'Faites de votre plus gros investissement votre meilleure décision.', ctaDescription: - 'Un tel enjeu m\u00E9rite de vrais outils, ne laissez pas la chance d\u00E9cider.', + 'Un tel enjeu mérite de vrais outils, ne laissez pas la chance décider.', }, // ── Pricing Page ─────────────────────────────────── pricingPage: { title: 'Tarifs early access', subtitle: - "Payez une fois, acc\u00E9dez pour toujours. Plus vous rejoignez t\u00F4t, moins vous payez.", + "Payez une fois, accédez pour toujours. Plus vous rejoignez tôt, moins vous payez.", costContext: - "L'achat d'un bien co\u00FBte plus de \u00A310 000 en droits de mutation, \u00A31 500 en frais de notaire, \u00A3500 pour une expertise. Choisissez le mauvais quartier et vous vous retrouvez avec un long trajet, de mauvaises \u00E9coles ou une route dont vous ignoriez l'existence.", - lessThanSurvey: "Moins cher qu'une expertise immobili\u00E8re. Bien plus utile.", + "L'achat d'un bien coûte plus de £10 000 en droits de mutation, £1 500 en frais de notaire, £500 pour une expertise. Choisissez le mauvais quartier et vous vous retrouvez avec un long trajet, de mauvaises écoles ou une route dont vous ignoriez l'existence.", + lessThanSurvey: "Moins cher qu'une expertise immobilière. Bien plus utile.", currentTier: 'Palier actuel', firstNUsers: '{{count}} premiers utilisateurs', everyoneAfter: 'Tous les suivants', nextNUsers: '{{count}} utilisateurs suivants', - lifetime: '/\u00E0 vie', + lifetime: '/à vie', spotsRemaining: '{{count}} place restante', spotsRemainingPlural: '{{count}} places restantes', filled: 'Complet', @@ -394,37 +404,37 @@ const fr: Translations = { getStarted: 'Commencer', getStartedPrice: 'Commencer - {{price}}', noCreditCard: 'Aucune carte bancaire requise', - moneyBackGuarantee: 'Garantie satisfait ou rembours\u00E9 sous 30 jours', - soldOut: '\u00C9puis\u00E9', - upcoming: '\u00C0 venir', + moneyBackGuarantee: 'Garantie satisfait ou remboursé sous 30 jours', + soldOut: 'Épuisé', + upcoming: 'À venir', failedToLoad: - '\u00C9chec du chargement des tarifs. Veuillez r\u00E9essayer plus tard.', - feat1: "56 couches de donn\u00E9es \u00E0 travers l'Angleterre", - feat2: 'Chaque code postal not\u00E9 et filtrable', - feat3: 'Exploration de la carte et exportations illimit\u00E9es', - feat4: 'Plusieurs d\u00E9cennies de donn\u00E9es historiques de prix', - feat5: 'Criminalit\u00E9, \u00E9coles, transports, d\u00E9bit internet et plus', - feat6: 'Toutes les futures mises \u00E0 jour de donn\u00E9es incluses', + 'Échec du chargement des tarifs. Veuillez réessayer plus tard.', + feat1: "56 couches de données à travers l'Angleterre", + feat2: 'Chaque code postal noté et filtrable', + feat3: 'Exploration de la carte et exportations illimitées', + feat4: 'Plusieurs décennies de données historiques de prix', + feat5: 'Criminalité, écoles, transports, débit internet et plus', + feat6: 'Toutes les futures mises à jour de données incluses', }, // ── Learn Page ───────────────────────────────────── learnPage: { faq: 'FAQ', - dataSources: 'Sources de donn\u00E9es', + dataSources: 'Sources de données', support: 'Assistance', - dataSourcesIntro: 'Cette application combine {{count}} jeux de donn\u00E9es ouverts couvrant les prix immobiliers, la performance \u00E9nerg\u00E9tique, les transports, la d\u00E9mographie, la criminalit\u00E9, l\u2019environnement et plus encore.', - faqIntro: 'Que vous achetiez, louiez ou exploriez simplement, voici comment Perfect Postcode vous aide \u00E0 trouver le bon quartier.', + dataSourcesIntro: 'Cette application combine {{count}} jeux de données ouverts couvrant les prix immobiliers, la performance énergétique, les transports, la démographie, la criminalité, l’environnement et plus encore.', + faqIntro: 'Que vous achetiez, louiez ou exploriez simplement, voici comment Perfect Postcode vous aide à trouver le bon quartier.', supportIntro: 'Vous avez une question ? Consultez notre FAQ ou contactez-nous directement.', source: 'Source :', optOut: 'Retrait de la divulgation publique', attribution: 'Attribution', - attrLandRegistry: 'Contient des donn\u00E9es du HM Land Registry \u00A9 Crown copyright and database right 2025.', + attrLandRegistry: 'Contient des données du HM Land Registry © Crown copyright and database right 2025.', attrOgl: 'Contient des informations du secteur public sous licence', attrOglLink: 'Open Government Licence v3.0', - attrOs: 'Contient des donn\u00E9es OS \u00A9 Crown copyright and database rights 2025.', - attrTfl: 'Propuls\u00E9 par TfL Open Data.', - attrOsm: 'Contient des donn\u00E9es de', - attrOsmContrib: '\u00A9 OpenStreetMap contributors', + attrOs: 'Contient des données OS © Crown copyright and database rights 2025.', + attrTfl: 'Propulsé par TfL Open Data.', + attrOsm: 'Contient des données de', + attrOsmContrib: '© OpenStreetMap contributors', attrOsmLicense: 'disponibles sous la', attrOsmLicenseLink: 'Open Data Commons Open Database License (ODbL)', // Data source names & descriptions @@ -433,142 +443,142 @@ const fr: Translations = { dsPricePaidUse: 'Historique complet des prix de vente immobiliers en Angleterre.', dsEpcName: 'Energy Performance Certificates (EPC)', dsEpcOrigin: 'Ministry of Housing, Communities & Local Government', - dsEpcUse: 'Certificats de performance \u00E9nerg\u00E9tique domestiques fournissant la surface, le nombre de pi\u00E8ces, l\u2019ann\u00E9e de construction, les classements \u00E9nerg\u00E9tiques, le type de bien et la forme du b\u00E2ti. Associ\u00E9s aux donn\u00E9es Price Paid par adresse au sein de chaque code postal. Les propri\u00E9taires peuvent demander le retrait de la divulgation publique.', + dsEpcUse: 'Certificats de performance énergétique domestiques fournissant la surface, le nombre de pièces, l’année de construction, les classements énergétiques, le type de bien et la forme du bâti. Associés aux données Price Paid par adresse au sein de chaque code postal. Les propriétaires peuvent demander le retrait de la divulgation publique.', dsNsplName: 'National Statistics Postcode Lookup (NSPL)', dsNsplOrigin: 'ONS / ArcGIS', - dsNsplUse: 'Associe les codes postaux aux coordonn\u00E9es et aux codes de zones statistiques, utilis\u00E9 pour relier tous les jeux de donn\u00E9es au niveau de la zone aux propri\u00E9t\u00E9s individuelles.', + dsNsplUse: 'Associe les codes postaux aux coordonnées et aux codes de zones statistiques, utilisé pour relier tous les jeux de données au niveau de la zone aux propriétés individuelles.', dsIodName: 'English Indices of Deprivation 2025', dsIodOrigin: 'Ministry of Housing, Communities & Local Government', - dsIodUse: 'Scores de d\u00E9faveur relative couvrant le revenu, l\u2019emploi, l\u2019\u00E9ducation, la sant\u00E9, la criminalit\u00E9 et le cadre de vie pour chaque quartier d\u2019Angleterre.', + dsIodUse: 'Scores de défaveur relative couvrant le revenu, l’emploi, l’éducation, la santé, la criminalité et le cadre de vie pour chaque quartier d’Angleterre.', dsEthnicityName: 'Population par ethnie (recensement 2021)', dsEthnicityOrigin: 'ONS', - dsEthnicityUse: 'Pourcentages de population par groupe ethnique (sud-asiatique, est-asiatique, noir, mixte, blanc, autre) par autorit\u00E9 locale.', + dsEthnicityUse: 'Pourcentages de population par groupe ethnique (sud-asiatique, est-asiatique, noir, mixte, blanc, autre) par autorité locale.', dsCrimeName: 'Street-level Crime Data', dsCrimeOrigin: 'data.police.uk', - dsCrimeUse: 'Donn\u00E9es de criminalit\u00E9 de proximit\u00E9 de 2023 \u00E0 2025, agr\u00E9g\u00E9es en moyennes annuelles par LSOA et type d\u2019infraction (violences, cambriolages, troubles \u00E0 l\u2019ordre public, stup\u00E9fiants, vols de v\u00E9hicules, etc.).', + dsCrimeUse: 'Données de criminalité de proximité de 2023 à 2025, agrégées en moyennes annuelles par LSOA et type d’infraction (violences, cambriolages, troubles à l’ordre public, stupéfiants, vols de véhicules, etc.).', dsOsmName: 'OpenStreetMap POIs', dsOsmOrigin: 'OpenStreetMap contributors / Geofabrik', - dsOsmUse: 'Points d\u2019int\u00E9r\u00EAt couvrant commerces, restaurants, sant\u00E9, loisirs, tourisme et plus \u00E0 travers la Grande-Bretagne.', + dsOsmUse: 'Points d’intérêt couvrant commerces, restaurants, santé, loisirs, tourisme et plus à travers la Grande-Bretagne.', dsGreenspaceName: 'OS Open Greenspace', dsGreenspaceOrigin: 'Ordnance Survey', - dsGreenspaceUse: 'Limites officielles des espaces verts de Grande-Bretagne, incluant parcs publics, jardins, terrains de sport et aires de jeux. Les centro\u00EFdes des polygones sont utilis\u00E9s pour le comptage de proximit\u00E9 des parcs et le calcul de la distance au parc le plus proche.', + dsGreenspaceUse: 'Limites officielles des espaces verts de Grande-Bretagne, incluant parcs publics, jardins, terrains de sport et aires de jeux. Les centroïdes des polygones sont utilisés pour le comptage de proximité des parcs et le calcul de la distance au parc le plus proche.', dsNaptanName: 'NaPTAN (Public Transport Stops)', dsNaptanOrigin: 'Department for Transport', - dsNaptanUse: 'Emplacements des gares et arr\u00EAts pour le rail, le bus, le m\u00E9tro/tramway, le ferry et les a\u00E9roports \u00E0 travers l\u2019Angleterre.', + dsNaptanUse: 'Emplacements des gares et arrêts pour le rail, le bus, le métro/tramway, le ferry et les aéroports à travers l’Angleterre.', dsNoiseName: 'Defra Noise Mapping', dsNoiseOrigin: 'Defra / Environment Agency', - dsNoiseUse: 'Niveaux de bruit routier (moyenne pond\u00E9r\u00E9e sur 24 heures) issus de la cartographie strat\u00E9gique du bruit de 2022, mod\u00E9lis\u00E9s \u00E0 haute r\u00E9solution et \u00E9chantillonn\u00E9s \u00E0 chaque code postal.', + dsNoiseUse: 'Niveaux de bruit routier (moyenne pondérée sur 24 heures) issus de la cartographie stratégique du bruit de 2022, modélisés à haute résolution et échantillonnés à chaque code postal.', dsOfstedName: 'Ofsted School Inspections', dsOfstedOrigin: 'Ofsted', - dsOfstedUse: 'Derniers r\u00E9sultats d\u2019inspection des \u00E9coles publiques (avril 2025). Moyenn\u00E9s par code postal pour donner un score de qualit\u00E9 scolaire local (1=Excellent \u00E0 4=Insuffisant).', + dsOfstedUse: 'Derniers résultats d’inspection des écoles publiques (avril 2025). Moyennés par code postal pour donner un score de qualité scolaire local (1=Excellent à 4=Insuffisant).', dsBroadbandName: 'Ofcom Broadband Performance', dsBroadbandOrigin: 'Ofcom', - dsBroadbandUse: 'Couverture haut d\u00E9bit fixe et d\u00E9bits de t\u00E9l\u00E9chargement maximum par zone, issus de Ofcom Connected Nations 2025.', + dsBroadbandUse: 'Couverture haut débit fixe et débits de téléchargement maximum par zone, issus de Ofcom Connected Nations 2025.', dsCouncilTaxName: 'Council Tax Levels 2025-26', dsCouncilTaxOrigin: 'Ministry of Housing, Communities & Local Government', - dsCouncilTaxUse: 'Taux annuels de taxe d\u2019habitation pour les tranches A \u00E0 H pour les 296 autorit\u00E9s de facturation d\u2019Angleterre, pour un logement occup\u00E9 par deux adultes. Reli\u00E9s aux propri\u00E9t\u00E9s via le code d\u2019autorit\u00E9 locale du r\u00E9pertoire de codes postaux NSPL.', + dsCouncilTaxUse: 'Taux annuels de taxe d’habitation pour les tranches A à H pour les 296 autorités de facturation d’Angleterre, pour un logement occupé par deux adultes. Reliés aux propriétés via le code d’autorité locale du répertoire de codes postaux NSPL.', dsRentalName: 'Private Rental Market Statistics', dsRentalOrigin: 'ONS / Valuation Office Agency', - dsRentalUse: 'Loyers mensuels m\u00E9dians du march\u00E9 locatif priv\u00E9 par autorit\u00E9 locale et cat\u00E9gorie de chambres (oct. 2022 - sept. 2023). Reli\u00E9s aux propri\u00E9t\u00E9s via le code d\u2019autorit\u00E9 locale et le nombre estim\u00E9 de chambres.', + dsRentalUse: 'Loyers mensuels médians du marché locatif privé par autorité locale et catégorie de chambres (oct. 2022 - sept. 2023). Reliés aux propriétés via le code d’autorité locale et le nombre estimé de chambres.', // FAQ section titles faqFindingTitle: 'Trouver votre quartier', - faqCommuteTitle: 'Trajet et d\u00E9placements', - faqBudgetTitle: 'Budget et rapport qualit\u00E9-prix', - faqSafetyTitle: 'S\u00E9curit\u00E9 et voisinage', - faqFamiliesTitle: 'Familles et \u00E9coles', - faqEnvironmentTitle: 'Environnement et qualit\u00E9 de vie', + faqCommuteTitle: 'Trajet et déplacements', + faqBudgetTitle: 'Budget et rapport qualité-prix', + faqSafetyTitle: 'Sécurité et voisinage', + faqFamiliesTitle: 'Familles et écoles', + faqEnvironmentTitle: 'Environnement et qualité de vie', faqWhyTitle: 'Pourquoi Perfect Postcode', - faqPricingTitle: 'Tarifs et acc\u00E8s', + faqPricingTitle: 'Tarifs et accès', faqTipsTitle: 'Astuces', // FAQ items — Finding Your Area - faqFinding1Q: 'Je ne sais m\u00EAme pas quelles zones regarder. Est-ce que \u00E7a peut m\u2019aider ?', - faqFinding1A: 'C\u2019est exactement \u00E0 \u00E7a que \u00E7a sert. D\u00E9finissez vos filtres (budget, temps de trajet, faible criminalit\u00E9, bonnes \u00E9coles) et la carte s\u2019illumine pour montrer chaque zone qui coche toutes les cases. Fini de chercher \u00AB meilleures zones pour vivre pr\u00E8s de Manchester \u00BB \u00E0 minuit.', - faqFinding2Q: 'Je d\u00E9m\u00E9nage dans un endroit que je ne connais pas du tout. Par o\u00F9 commencer ?', - faqFinding2A: 'D\u00E9finissez vos filtres pour ce qui compte et la carte met instantan\u00E9ment en \u00E9vidence les zones qui correspondent. Vous passez de \u00AB je ne connais pas une seule rue \u00BB \u00E0 une s\u00E9lection en quelques minutes.', + faqFinding1Q: 'Je ne sais même pas quelles zones regarder. Est-ce que ça peut m’aider ?', + faqFinding1A: 'C’est exactement à ça que ça sert. Définissez vos filtres (budget, temps de trajet, faible criminalité, bonnes écoles) et la carte s’illumine pour montrer chaque zone qui coche toutes les cases. Fini de chercher « meilleures zones pour vivre près de Manchester » à minuit.', + faqFinding2Q: 'Je déménage dans un endroit que je ne connais pas du tout. Par où commencer ?', + faqFinding2A: 'Définissez vos filtres pour ce qui compte et la carte met instantanément en évidence les zones qui correspondent. Vous passez de « je ne connais pas une seule rue » à une sélection en quelques minutes.', faqFinding3Q: 'Comment trouver des zones qui cochent toutes mes cases en une seule fois ?', - faqFinding3A: 'Empilez plusieurs filtres (criminalit\u00E9 sous la moyenne, bonnes \u00E9coles, trajet de moins de 40 minutes) puis colorez la carte par prix pour rep\u00E9rer les zones au meilleur rapport qualit\u00E9-prix. La carte se met \u00E0 jour en temps r\u00E9el quand vous bougez les curseurs.', + faqFinding3A: 'Empilez plusieurs filtres (criminalité sous la moyenne, bonnes écoles, trajet de moins de 40 minutes) puis colorez la carte par prix pour repérer les zones au meilleur rapport qualité-prix. La carte se met à jour en temps réel quand vous bougez les curseurs.', // FAQ items — Commute and Travel - faqCommute1Q: 'Puis-je voir combien de temps durerait mon trajet depuis diff\u00E9rentes zones ?', - faqCommute1A: 'D\u00E9finissez votre lieu de travail comme destination et nous colorons chaque code postal par temps de trajet, que ce soit en voiture, \u00E0 v\u00E9lo ou en transports en commun. Filtrez par votre trajet maximum et le reste dispara\u00EEt.', - faqCommute2Q: 'En quoi c\u2019est mieux que Google Maps ?', - faqCommute2A: 'Google Maps vous montre un trajet \u00E0 la fois. Nous colorons chaque code postal d\u2019Angleterre par temps de trajet en une seule vue, pour que vous puissiez comparer des centaines de zones c\u00F4te \u00E0 c\u00F4te au lieu de les chercher une par une.', + faqCommute1Q: 'Puis-je voir combien de temps durerait mon trajet depuis différentes zones ?', + faqCommute1A: 'Définissez votre lieu de travail comme destination et nous colorons chaque code postal par temps de trajet, que ce soit en voiture, à vélo ou en transports en commun. Filtrez par votre trajet maximum et le reste disparaît.', + faqCommute2Q: 'En quoi c’est mieux que Google Maps ?', + faqCommute2A: 'Google Maps vous montre un trajet à la fois. Nous colorons chaque code postal d’Angleterre par temps de trajet en une seule vue, pour que vous puissiez comparer des centaines de zones côte à côte au lieu de les chercher une par une.', // FAQ items — Budget and Value - faqBudget1Q: 'Comment trouver les zones o\u00F9 j\u2019ai le plus d\u2019espace pour mon argent ?', - faqBudget1A: 'Filtrez par prix au m\u00B2 et vous verrez instantan\u00E9ment quels codes postaux offrent le plus d\u2019espace par livre. Combinez avec le filtre de classement \u00E9nerg\u00E9tique pour \u00E9viter les biens aux co\u00FBts de chauffage \u00E9lev\u00E9s.', - faqBudget2Q: 'Comment m\u2019assurer qu\u2019une zone bon march\u00E9 ne l\u2019est pas pour de mauvaises raisons ?', - faqBudget2A: 'Superposez les scores de d\u00E9faveur, les statistiques de criminalit\u00E9, les notes des \u00E9coles et les d\u00E9bits internet \u00E0 c\u00F4t\u00E9 du prix. Si un code postal est abordable et obtient de bons scores sur tout ce qui compte, vous avez trouv\u00E9 une vraie bonne affaire, pas juste un prix bas avec des compromis que vous n\u2019avez pas encore rep\u00E9r\u00E9s.', + faqBudget1Q: 'Comment trouver les zones où j’ai le plus d’espace pour mon argent ?', + faqBudget1A: 'Filtrez par prix au m² et vous verrez instantanément quels codes postaux offrent le plus d’espace par livre. Combinez avec le filtre de classement énergétique pour éviter les biens aux coûts de chauffage élevés.', + faqBudget2Q: 'Comment m’assurer qu’une zone bon marché ne l’est pas pour de mauvaises raisons ?', + faqBudget2A: 'Superposez les scores de défaveur, les statistiques de criminalité, les notes des écoles et les débits internet à côté du prix. Si un code postal est abordable et obtient de bons scores sur tout ce qui compte, vous avez trouvé une vraie bonne affaire, pas juste un prix bas avec des compromis que vous n’avez pas encore repérés.', // FAQ items — Safety and Neighbourhood - faqSafety1Q: 'Comment v\u00E9rifier si une zone est s\u00FBre avant d\u2019y d\u00E9m\u00E9nager ?', - faqSafety1A: 'Nous superposons les donn\u00E9es r\u00E9elles de criminalit\u00E9 enregistr\u00E9es par la police, ventil\u00E9es par type, sur chaque quartier d\u2019Angleterre. Filtrez par criminalit\u00E9 violente, cambriolages ou troubles \u00E0 l\u2019ordre public et voyez instantan\u00E9ment quels codes postaux ont les chiffres les plus bas.', - faqSafety2Q: 'Je trouve sans cesse des appartements superbes en ligne, puis le quartier s\u2019av\u00E8re difficile.', - faqSafety2A: 'C\u2019est exactement pour \u00E7a que cet outil existe. Empilez taux de criminalit\u00E9, niveaux de bruit, scores de d\u00E9faveur, pubs et parcs \u00E0 proximit\u00E9, et d\u00E9bits internet, le tout sur une seule carte, pour savoir \u00E0 quoi ressemble vraiment un quartier avant de r\u00E9server une visite.', + faqSafety1Q: 'Comment vérifier si une zone est sûre avant d’y déménager ?', + faqSafety1A: 'Nous superposons les données réelles de criminalité enregistrées par la police, ventilées par type, sur chaque quartier d’Angleterre. Filtrez par criminalité violente, cambriolages ou troubles à l’ordre public et voyez instantanément quels codes postaux ont les chiffres les plus bas.', + faqSafety2Q: 'Je trouve sans cesse des appartements superbes en ligne, puis le quartier s’avère difficile.', + faqSafety2A: 'C’est exactement pour ça que cet outil existe. Empilez taux de criminalité, niveaux de bruit, scores de défaveur, pubs et parcs à proximité, et débits internet, le tout sur une seule carte, pour savoir à quoi ressemble vraiment un quartier avant de réserver une visite.', // FAQ items — Families and Schools - faqFamilies1Q: 'Puis-je trouver des zones avec de bonnes \u00E9coles ET peu de criminalit\u00E9 en une seule recherche ?', - faqFamilies1A: 'Oui. Empilez les filtres pour les notes Ofsted, les taux de criminalit\u00E9, les parcs et tout ce qui compte pour votre famille, et la carte ne met en \u00E9vidence que les zones qui cochent toutes les cases. Fini de croiser cinq sites diff\u00E9rents.', - faqFamilies2Q: 'Comment savoir si un quartier a des parcs et des aires de jeux \u00E0 proximit\u00E9 ?', - faqFamilies2A: 'Activez la couche de POI parcs et espaces verts pour les voir directement sur la carte. Vous pouvez aussi filtrer par le nombre de parcs accessibles \u00E0 pied depuis chaque code postal.', + faqFamilies1Q: 'Puis-je trouver des zones avec de bonnes écoles ET peu de criminalité en une seule recherche ?', + faqFamilies1A: 'Oui. Empilez les filtres pour les notes Ofsted, les taux de criminalité, les parcs et tout ce qui compte pour votre famille, et la carte ne met en évidence que les zones qui cochent toutes les cases. Fini de croiser cinq sites différents.', + faqFamilies2Q: 'Comment savoir si un quartier a des parcs et des aires de jeux à proximité ?', + faqFamilies2A: 'Activez la couche de POI parcs et espaces verts pour les voir directement sur la carte. Vous pouvez aussi filtrer par le nombre de parcs accessibles à pied depuis chaque code postal.', // FAQ items — Environment and Quality of Life - faqEnv1Q: 'Puis-je trouver des logements \u00E9conomes en \u00E9nergie qui ne sont pas sur une route bruyante ?', - faqEnv1A: 'Filtrez par classement EPC (A \u00E0 C), puis superposez les donn\u00E9es de bruit routier pour exclure tout ce qui d\u00E9passe votre seuil. Colorez par l\u2019un ou l\u2019autre crit\u00E8re pour rep\u00E9rer les rues calmes et \u00E9conomes d\u2019un coup d\u2019\u0153il.', - faqEnv2Q: 'Est-ce que \u00E7a montre le risque d\u2019inondation ou d\u2019affaissement ?', - faqEnv2A: 'Nous incluons des donn\u00E9es de stabilit\u00E9 du sol pour que vous puissiez v\u00E9rifier les risques d\u2019affaissement, de retrait-gonflement des argiles et d\u2019autres al\u00E9as g\u00E9ologiques avant de vous engager. Excluez les zones \u00E0 risque d\u00E8s le d\u00E9part.', - faqEnv3Q: 'Puis-je trouver des zones avec un bon d\u00E9bit internet qui soient aussi calmes ?', - faqEnv3A: 'Superposez le filtre de d\u00E9bit internet avec les donn\u00E9es de bruit routier pour trouver des rues avec une bonne connectivit\u00E9 et peu de bruit. Colorez par l\u2019un ou l\u2019autre crit\u00E8re pour comparer les zones d\u2019un coup d\u2019\u0153il.', + faqEnv1Q: 'Puis-je trouver des logements économes en énergie qui ne sont pas sur une route bruyante ?', + faqEnv1A: 'Filtrez par classement EPC (A à C), puis superposez les données de bruit routier pour exclure tout ce qui dépasse votre seuil. Colorez par l’un ou l’autre critère pour repérer les rues calmes et économes d’un coup d’œil.', + faqEnv2Q: 'Est-ce que ça montre le risque d’inondation ou d’affaissement ?', + faqEnv2A: 'Nous incluons des données de stabilité du sol pour que vous puissiez vérifier les risques d’affaissement, de retrait-gonflement des argiles et d’autres aléas géologiques avant de vous engager. Excluez les zones à risque dès le départ.', + faqEnv3Q: 'Puis-je trouver des zones avec un bon débit internet qui soient aussi calmes ?', + faqEnv3A: 'Superposez le filtre de débit internet avec les données de bruit routier pour trouver des rues avec une bonne connectivité et peu de bruit. Colorez par l’un ou l’autre critère pour comparer les zones d’un coup d’œil.', // FAQ items — Why Perfect Postcode - faqWhy1Q: 'J\u2019utilise d\u00E9j\u00E0 Rightmove. Qu\u2019est-ce que \u00E7a apporte de plus ?', - faqWhy1A: 'Rightmove vous montre des maisons. Nous vous montrons des quartiers. Taux de criminalit\u00E9, notes des \u00E9coles, d\u00E9bits internet, niveaux de bruit, scores de d\u00E9faveur et plus, tout filtrable sur une seule carte. Vous pouvez juger un quartier avant m\u00EAme de regarder les annonces.', - faqWhy2Q: 'Je ne peux pas simplement faire ces recherches gratuitement moi-m\u00EAme ?', - faqWhy2A: 'Vous pourriez croiser les donn\u00E9es polici\u00E8res, les rapports Ofsted, les registres EPC, les archives du Land Registry et les statistiques ONS un code postal \u00E0 la fois. Ou vous pouvez avoir le tout filtrable et color\u00E9 sur une seule carte en quelques secondes.', - faqWhy3Q: 'D\u2019o\u00F9 viennent r\u00E9ellement les donn\u00E9es ?', - faqWhy3A: 'Chaque jeu de donn\u00E9es provient de sources officielles du gouvernement britannique : Land Registry, le registre EPC, ONS, Ofsted, Ofcom, data.police.uk et Defra. Nous ne scrapons pas les agents immobiliers et n\u2019inventons rien. Vous pouvez v\u00E9rifier chaque donn\u00E9e aupr\u00E8s de la source originale.', + faqWhy1Q: 'J’utilise déjà Rightmove. Qu’est-ce que ça apporte de plus ?', + faqWhy1A: 'Rightmove vous montre des maisons. Nous vous montrons des quartiers. Taux de criminalité, notes des écoles, débits internet, niveaux de bruit, scores de défaveur et plus, tout filtrable sur une seule carte. Vous pouvez juger un quartier avant même de regarder les annonces.', + faqWhy2Q: 'Je ne peux pas simplement faire ces recherches gratuitement moi-même ?', + faqWhy2A: 'Vous pourriez croiser les données policières, les rapports Ofsted, les registres EPC, les archives du Land Registry et les statistiques ONS un code postal à la fois. Ou vous pouvez avoir le tout filtrable et coloré sur une seule carte en quelques secondes.', + faqWhy3Q: 'D’où viennent réellement les données ?', + faqWhy3A: 'Chaque jeu de données provient de sources officielles du gouvernement britannique : Land Registry, le registre EPC, ONS, Ofsted, Ofcom, data.police.uk et Defra. Nous ne scrapons pas les agents immobiliers et n’inventons rien. Vous pouvez vérifier chaque donnée auprès de la source originale.', // FAQ items — Pricing and Access - faqPricing1Q: 'Est-ce que \u00E7a vaut vraiment le coup de payer pour un outil de recherche immobili\u00E8re ?', - faqPricing1A: 'L\u2019achat d\u2019un logement est probablement le plus gros achat de votre vie. Rep\u00E9rer un seul signal d\u2019alerte (une route bruyante, un mauvais d\u00E9bit, une criminalit\u00E9 en hausse) avant de vous engager pourrait vous \u00E9pargner des ann\u00E9es de regrets. \u00C7a co\u00FBte moins qu\u2019un plein d\u2019essence.', + faqPricing1Q: 'Est-ce que ça vaut vraiment le coup de payer pour un outil de recherche immobilière ?', + faqPricing1A: 'L’achat d’un logement est probablement le plus gros achat de votre vie. Repérer un seul signal d’alerte (une route bruyante, un mauvais débit, une criminalité en hausse) avant de vous engager pourrait vous épargner des années de regrets. Ça coûte moins qu’un plein d’essence.', faqPricing2Q: 'Est-ce un abonnement ?', - faqPricing2A: 'Non. Paiement unique, \u00E0 vous pour toujours. Utilisez-le intensivement pendant votre recherche, revenez quand vous \u00EAtes curieux d\u2019une nouvelle zone, et c\u2019est toujours l\u00E0 si vous d\u00E9m\u00E9nagez \u00E0 nouveau.', + faqPricing2A: 'Non. Paiement unique, à vous pour toujours. Utilisez-le intensivement pendant votre recherche, revenez quand vous êtes curieux d’une nouvelle zone, et c’est toujours là si vous déménagez à nouveau.', faqPricing3Q: 'Que puis-je faire avec la version gratuite ?', - faqPricing3A: 'Les utilisateurs gratuits peuvent explorer toutes les fonctionnalit\u00E9s dans la zone de d\u00E9monstration (centre de Londres, approximativement zones 1 \u00E0 2). Pour acc\u00E9der aux donn\u00E9es du reste de l\u2019Angleterre, il faut l\u2019acc\u00E8s \u00E0 vie.', + faqPricing3A: 'Les utilisateurs gratuits peuvent explorer toutes les fonctionnalités dans la zone de démonstration (centre de Londres, approximativement zones 1 à 2). Pour accéder aux données du reste de l’Angleterre, il faut l’accès à vie.', faqPricing4Q: 'Puis-je obtenir un remboursement ?', - faqPricing4A: 'Absolument. Nous offrons une garantie satisfait ou rembours\u00E9 sous 30 jours. Si vous n\u2019\u00EAtes pas satisfait, envoyez un e-mail \u00E0 support@perfect-postcode.co.uk dans les 30 jours pour un remboursement int\u00E9gral.', + faqPricing4A: 'Absolument. Nous offrons une garantie satisfait ou remboursé sous 30 jours. Si vous n’êtes pas satisfait, envoyez un e-mail à support@perfect-postcode.co.uk dans les 30 jours pour un remboursement intégral.', // FAQ items — Tips and Tricks - faqTips1Q: 'Comment utiliser le filtre IA au lieu d\u2019ajouter les filtres un par un ?', - faqTips1A: 'Tapez ce que vous voulez en langage courant, par exemple \u00AB quartier calme pr\u00E8s de bonnes \u00E9coles avec bon d\u00E9bit internet \u00E0 moins de \u00A3400k \u00BB, et il configurera tous les filtres pertinents d\u2019un coup. Ajustez ensuite manuellement si n\u00E9cessaire.', + faqTips1Q: 'Comment utiliser le filtre IA au lieu d’ajouter les filtres un par un ?', + faqTips1A: 'Tapez ce que vous voulez en langage courant, par exemple « quartier calme près de bonnes écoles avec bon débit internet à moins de £400k », et il configurera tous les filtres pertinents d’un coup. Ajustez ensuite manuellement si nécessaire.', faqTips2Q: 'Puis-je enregistrer une recherche et y revenir plus tard ?', - faqTips2A: 'Cliquez sur le bouton d\u2019enregistrement et tout est captur\u00E9 : vos filtres, le niveau de zoom et la couche de donn\u00E9es affich\u00E9e. Reprenez exactement o\u00F9 vous en \u00E9tiez ou partagez le lien avec votre conjoint.', - faqTips3Q: 'Puis-je exporter les donn\u00E9es que je consulte ?', - faqTips3A: 'Utilisez le bouton d\u2019exportation pour t\u00E9l\u00E9charger les propri\u00E9t\u00E9s filtr\u00E9es sous forme de tableur. L\u2019export respecte tous vos filtres actifs, vous obtenez donc exactement les donn\u00E9es souhait\u00E9es.', + faqTips2A: 'Cliquez sur le bouton d’enregistrement et tout est capturé : vos filtres, le niveau de zoom et la couche de données affichée. Reprenez exactement où vous en étiez ou partagez le lien avec votre conjoint.', + faqTips3Q: 'Puis-je exporter les données que je consulte ?', + faqTips3A: 'Utilisez le bouton d’exportation pour télécharger les propriétés filtrées sous forme de tableur. L’export respecte tous vos filtres actifs, vous obtenez donc exactement les données souhaitées.', }, // ── Account Page ─────────────────────────────────── accountPage: { emailLabel: 'E-mail', subscriptionLabel: 'Abonnement', - upgrade: 'Passer \u00E0 la version compl\u00E8te', - redirecting: 'Redirection\u2026', + upgrade: 'Passer à la version complète', + redirecting: 'Redirection…', receiveNewsletter: 'Recevoir les e-mails de la newsletter', - needHelp: 'Besoin d\u2019aide ? \u00C9crivez-nous \u00E0', - responseTime: 'Nous r\u00E9pondons g\u00E9n\u00E9ralement sous 24 heures.', + needHelp: 'Besoin d’aide ? Écrivez-nous à', + responseTime: 'Nous répondons généralement sous 24 heures.', }, // ── Saved Page ───────────────────────────────────── savedPage: { searches: 'Recherches', - noSavedSearches: 'Aucune recherche enregistr\u00E9e', + noSavedSearches: 'Aucune recherche enregistrée', noSavedSearchesDesc: - 'Enregistrez vos filtres et la vue de la carte pour reprendre exactement l\u00E0 o\u00F9 vous vous \u00E9tiez arr\u00EAt\u00E9.', - noSavedProperties: 'Aucune propri\u00E9t\u00E9 enregistr\u00E9e', + 'Enregistrez vos filtres et la vue de la carte pour reprendre exactement là où vous vous étiez arrêté.', + noSavedProperties: 'Aucune propriété enregistrée', noSavedPropertiesDesc: - 'Ajoutez des propri\u00E9t\u00E9s en favoris au fil de votre exploration et constituez votre s\u00E9lection sans rien perdre de vue.', + 'Ajoutez des propriétés en favoris au fil de votre exploration et constituez votre sélection sans rien perdre de vue.', openPostcode: 'Ouvrir le code postal', - viewListing: 'Voir l\u2019annonce', + viewListing: 'Voir l’annonce', clickToRename: 'Cliquez pour renommer', notesPlaceholder: 'Notez vos impressions...', deleteSearch: 'Supprimer la recherche', deleteSearchConfirm: - '\u00CAtes-vous s\u00FBr de vouloir supprimer cette recherche enregistr\u00E9e ? Cette action est irr\u00E9versible.', - deleteProperty: 'Supprimer la propri\u00E9t\u00E9', + 'Êtes-vous sûr de vouloir supprimer cette recherche enregistrée ? Cette action est irréversible.', + deleteProperty: 'Supprimer la propriété', deletePropertyConfirm: - '\u00CAtes-vous s\u00FBr de vouloir supprimer cette propri\u00E9t\u00E9 enregistr\u00E9e ? Cette action est irr\u00E9versible.', + 'Êtes-vous sûr de vouloir supprimer cette propriété enregistrée ? Cette action est irréversible.', bed: 'ch.', epc: 'DPE', }, @@ -576,77 +586,77 @@ const fr: Translations = { // ── Invites Page ─────────────────────────────────── invitesPage: { inviteLinksLicensed: - "Les liens d'invitation sont disponibles pour les utilisateurs licenci\u00E9s.", - inviteAdminLabel: 'Inviter des amis (100% de r\u00E9duction)', - inviteReferralLabel: 'Inviter des amis (30% de r\u00E9duction)', - generateFreeInvite: "G\u00E9n\u00E9rer un lien d'invitation gratuit", - generateReferralLink: 'G\u00E9n\u00E9rer un lien de parrainage', + "Les liens d'invitation sont disponibles pour les utilisateurs licenciés.", + inviteAdminLabel: 'Inviter des amis (100% de réduction)', + inviteReferralLabel: 'Inviter des amis (30% de réduction)', + generateFreeInvite: "Générer un lien d'invitation gratuit", + generateReferralLink: 'Générer un lien de parrainage', copyInviteLink: "Copier le lien d'invitation", - adminInvitesTitle: 'Invitations admin (100% de r\u00E9duction)', - referralInvitesTitle: 'Invitations de parrainage (30% de r\u00E9duction)', + adminInvitesTitle: 'Invitations admin (100% de réduction)', + referralInvitesTitle: 'Invitations de parrainage (30% de réduction)', yourInviteLinks: "Vos liens d'invitation", - noInvitesYet: "Aucune invitation g\u00E9n\u00E9r\u00E9e pour l'instant", + noInvitesYet: "Aucune invitation générée pour l'instant", link: 'Lien', status: 'Statut', - created: 'Cr\u00E9\u00E9', - redeemed: 'Utilis\u00E9', + created: 'Créé', + redeemed: 'Utilisé', pending: 'En attente', }, // ── Invite Page ──────────────────────────────────── invitePage: { - youreInvited: 'Vous \u00EAtes invit\u00E9 !', - specialOffer: 'Offre sp\u00E9ciale !', + youreInvited: 'Vous êtes invité !', + specialOffer: 'Offre spéciale !', invitedByFree: - '{{name}} vous invite \u00E0 obtenir un acc\u00E8s \u00E0 vie gratuit.', + '{{name}} vous invite à obtenir un accès à vie gratuit.', invitedByDiscount: - "{{name}} vous fait b\u00E9n\u00E9ficier d'une r\u00E9duction de 30% sur l'acc\u00E8s \u00E0 vie.", + "{{name}} vous fait bénéficier d'une réduction de 30% sur l'accès à vie.", genericFreeInvite: - 'Vous avez \u00E9t\u00E9 invit\u00E9 \u00E0 obtenir un acc\u00E8s \u00E0 vie gratuit.', + 'Vous avez été invité à obtenir un accès à vie gratuit.', genericDiscount: - "Un ami vous fait b\u00E9n\u00E9ficier d'une r\u00E9duction de 30% sur l'acc\u00E8s \u00E0 vie.", + "Un ami vous fait bénéficier d'une réduction de 30% sur l'accès à vie.", exploreEvery: "Explorez chaque quartier d'Angleterre", propertyInfo: - "Prix immobiliers, classements \u00E9nerg\u00E9tiques, statistiques de criminalit\u00E9, notes des \u00E9coles et plus encore", + "Prix immobiliers, classements énergétiques, statistiques de criminalité, notes des écoles et plus encore", invalidInvite: 'Invitation invalide', - inviteAlreadyUsed: 'Invitation d\u00E9j\u00E0 utilis\u00E9e', - inviteAlreadyUsedDesc: "Ce lien d'invitation a d\u00E9j\u00E0 \u00E9t\u00E9 utilis\u00E9.", + inviteAlreadyUsed: 'Invitation déjà utilisée', + inviteAlreadyUsedDesc: "Ce lien d'invitation a déjà été utilisé.", invalidInviteLink: "Lien d'invitation invalide", invalidInviteLinkDesc: - "Ce lien d'invitation est invalide ou a expir\u00E9.", - licenseActivated: 'Licence activ\u00E9e !', + "Ce lien d'invitation est invalide ou a expiré.", + licenseActivated: 'Licence activée !', fullAccessGranted: - 'Vous avez d\u00E9sormais un acc\u00E8s complet \u00E0 Perfect Postcode.', + 'Vous avez désormais un accès complet à Perfect Postcode.', activating: 'Activation...', activateLicense: 'Activer la licence', - claimDiscount: 'R\u00E9clamer la r\u00E9duction', - registerToClaim: "S'inscrire pour r\u00E9clamer", - youAlreadyHaveLicense: 'Vous avez d\u00E9j\u00E0 une licence', - accountHasFullAccess: 'Votre compte dispose d\u00E9j\u00E0 d\u2019un acc\u00E8s complet.', - failedToValidate: "\u00C9chec de la validation du lien d'invitation", + claimDiscount: 'Réclamer la réduction', + registerToClaim: "S'inscrire pour réclamer", + youAlreadyHaveLicense: 'Vous avez déjà une licence', + accountHasFullAccess: 'Votre compte dispose déjà d’un accès complet.', + failedToValidate: "Échec de la validation du lien d'invitation", }, // ── Map Page ─────────────────────────────────────── mapPage: { unsavedProperty: 'Retirer', - savedProperty: 'Enregistr\u00E9', + savedProperty: 'Enregistré', }, // ── Format / Time ────────────────────────────────── format: { - justNow: '\u00E0 l\u2019instant', + justNow: 'à l’instant', minutesAgo: 'il y a {{count}} min', hoursAgo: 'il y a {{count}} h', daysAgo: 'il y a {{count}} j', nFilters: '{{count}} filtres', noFilters: 'Aucun filtre', - poiCategory: '{{count}} cat\u00E9gorie de POI', - poiCategories: '{{count}} cat\u00E9gories de POI', + poiCategory: '{{count}} catégorie de POI', + poiCategories: '{{count}} catégories de POI', travelDestination: '{{count}} destination de temps de trajet', travelDestinations: '{{count}} destinations de temps de trajet', - propertiesMatch: '{{count}} propri\u00E9t\u00E9s correspondent', - setFilters: 'D\u00E9finir {{count}} filtre(s)\u00A0: {{list}}', - noFiltersSet: 'Aucun filtre d\u00E9fini', + propertiesMatch: '{{count}} propriétés correspondent', + setFilters: 'Définir {{count}} filtre(s) : {{list}}', + noFiltersSet: 'Aucun filtre défini', toDestination: '{{mode}} vers {{label}} {{bounds}}', lessThanMin: '< {{max}} min', moreThanMin: '> {{min}} min', @@ -654,18 +664,18 @@ const fr: Translations = { // ── Tutorial ────────────────────────────────────── tutorial: { - step1Title: 'Dites \u00E0 la carte ce qui compte', - step1Content: 'D\u00E9finissez votre budget, temps de trajet maximum, qualit\u00E9 des \u00E9coles, seuil de criminalit\u00E9. Ce qui compte pour vous. Seules les zones qui correspondent restent \u00E9clair\u00E9es. Utilisez l\u2019ic\u00F4ne \u0153il pour colorier par n\u2019importe quel crit\u00E8re.', - step2Title: 'Ou d\u00E9crivez simplement', - step2Content: 'Tapez ce que vous voulez en fran\u00E7ais, par exemple \u00AB\u00A0quartier calme pr\u00E8s de bonnes \u00E9coles sous \u00A3400k\u00A0\u00BB, et nous configurerons les filtres pour vous.', + step1Title: 'Dites à la carte ce qui compte', + step1Content: 'Définissez votre budget, temps de trajet maximum, qualité des écoles, seuil de criminalité. Ce qui compte pour vous. Seules les zones qui correspondent restent éclairées. Utilisez l’icône œil pour colorier par n’importe quel critère.', + step2Title: 'Ou décrivez simplement', + step2Content: 'Tapez ce que vous voulez en français, par exemple « quartier calme près de bonnes écoles sous £400k », et nous configurerons les filtres pour vous.', step3Title: 'Explorez ce qui existe', - step3Content: 'Naviguez et zoomez \u00E0 travers l\u2019Angleterre. Cliquez sur n\u2019importe quelle zone color\u00E9e pour voir la criminalit\u00E9, les \u00E9coles, les prix, le haut d\u00E9bit, le bruit et plus encore.', - step4Title: 'Allez directement \u00E0 un lieu', - step4Content: 'Recherchez n\u2019importe quel lieu ou code postal pour vous y rendre instantan\u00E9ment.', - step5Title: 'Examinez les d\u00E9tails', - step5Content: 'Consultez les statistiques de zone, histogrammes et fiches individuelles\u00A0: prix, surface, performances \u00E9nerg\u00E9tiques et plus.', - step6Title: 'Qu\u2019y a-t-il \u00E0 proximit\u00E9\u00A0?', - step6Content: 'Activez les \u00E9coles, commerces, gares, parcs et restaurants sur la carte pour voir ce qui est \u00E0 port\u00E9e.', + step3Content: 'Naviguez et zoomez à travers l’Angleterre. Cliquez sur n’importe quelle zone colorée pour voir la criminalité, les écoles, les prix, le haut débit, le bruit et plus encore.', + step4Title: 'Allez directement à un lieu', + step4Content: 'Recherchez n’importe quel lieu ou code postal pour vous y rendre instantanément.', + step5Title: 'Examinez les détails', + step5Content: 'Consultez les statistiques de zone, histogrammes et fiches individuelles : prix, surface, performances énergétiques et plus.', + step6Title: 'Qu’y a-t-il à proximité ?', + step6Content: 'Activez les écoles, commerces, gares, parcs et restaurants sur la carte pour voir ce qui est à portée.', }, // ── Server-derived values ────────────────────────── @@ -673,93 +683,92 @@ const fr: Translations = { // The English keys MUST match exactly what the API returns. server: { // ─ Feature group names ─ - 'Properties': 'Propri\u00E9t\u00E9s', + 'Properties': 'Propriétés', 'Transport': 'Transports', - 'Education': '\u00C9ducation', - 'Deprivation': 'Pr\u00E9carit\u00E9', - 'Crime': 'Criminalit\u00E9', - 'Demographics': 'D\u00E9mographie', - 'Amenities': 'Commodit\u00E9s', + 'Education': 'Éducation', + 'Deprivation': 'Précarité', + 'Crime': 'Criminalité', + 'Demographics': 'Démographie', + 'Amenities': 'Commodités', // ─ Feature names (Properties) ─ - 'Listing status': 'Statut de l\u2019annonce', + 'Listing status': 'Statut de l’annonce', 'Property type': 'Type de bien', - 'Leasehold/Freehold': 'Bail/Pleine propri\u00E9t\u00E9', + 'Leasehold/Freehold': 'Bail/Pleine propriété', 'Last known price': 'Dernier prix connu', - 'Estimated current price': 'Prix actuel estim\u00E9', - 'Asking price': 'Prix demand\u00E9', - 'Price per sqm': 'Prix au m\u00B2', - 'Est. price per sqm': 'Prix estim\u00E9 au m\u00B2', - 'Asking price per sqm': 'Prix demand\u00E9 au m\u00B2', - 'Estimated monthly rent': 'Loyer mensuel estim\u00E9', - 'Asking rent (monthly)': 'Loyer demand\u00E9 (mensuel)', - 'Total floor area (sqm)': 'Surface totale (m\u00B2)', - 'Number of bedrooms & living rooms': 'Nombre de chambres et s\u00E9jours', + 'Estimated current price': 'Prix actuel estimé', + 'Asking price': 'Prix demandé', + 'Price per sqm': 'Prix au m²', + 'Est. price per sqm': 'Prix estimé au m²', + 'Asking price per sqm': 'Prix demandé au m²', + 'Estimated monthly rent': 'Loyer mensuel estimé', + 'Asking rent (monthly)': 'Loyer demandé (mensuel)', + 'Total floor area (sqm)': 'Surface totale (m²)', + 'Number of bedrooms & living rooms': 'Nombre de chambres et séjours', 'Bedrooms': 'Chambres', 'Bathrooms': 'Salles de bain', - 'Construction year': 'Ann\u00E9e de construction', - 'Date of last transaction': 'Date de la derni\u00E8re transaction', + 'Construction year': 'Année de construction', + 'Date of last transaction': 'Date de la dernière transaction', 'Listing date': 'Date de mise en ligne', 'Former council house': 'Ancien logement social', - 'Current energy rating': 'Classement \u00E9nerg\u00E9tique actuel', - 'Potential energy rating': 'Classement \u00E9nerg\u00E9tique potentiel', - 'Interior height (m)': 'Hauteur int\u00E9rieure (m)', + 'Current energy rating': 'Classement énergétique actuel', + 'Potential energy rating': 'Classement énergétique potentiel', + 'Interior height (m)': 'Hauteur intérieure (m)', // ─ Feature names (Transport) ─ - 'Distance to nearest train or tube station (km)': 'Distance \u00E0 la gare ou station de m\u00E9tro la plus proche (km)', - 'Train or tube stations within 1km': 'Gares ou stations de m\u00E9tro \u00E0 moins d\u20191 km', + 'Distance to nearest train or tube station (km)': 'Distance à la gare ou station de métro la plus proche (km)', // ─ Feature names (Education) ─ - 'Good+ primary schools within 2km': '\u00C9coles primaires Bien+ dans un rayon de 2 km', - 'Good+ secondary schools within 2km': 'Coll\u00E8ges/lyc\u00E9es Bien+ dans un rayon de 2 km', - 'Good+ primary schools within 5km': '\u00C9coles primaires Bien+ dans un rayon de 5 km', - 'Good+ secondary schools within 5km': 'Coll\u00E8ges/lyc\u00E9es Bien+ dans un rayon de 5 km', - 'Education, Skills and Training Score': 'Score \u00E9ducation, comp\u00E9tences et formation', + 'Good+ primary schools within 2km': 'Écoles primaires Bien+ dans un rayon de 2 km', + 'Good+ secondary schools within 2km': 'Collèges/lycées Bien+ dans un rayon de 2 km', + 'Good+ primary schools within 5km': 'Écoles primaires Bien+ dans un rayon de 5 km', + 'Good+ secondary schools within 5km': 'Collèges/lycées Bien+ dans un rayon de 5 km', + 'Education, Skills and Training Score': 'Score éducation, compétences et formation', // ─ Feature names (Deprivation) ─ 'Income Score (rate)': 'Score de revenu (taux)', - 'Employment Score (rate)': 'Score d\u2019emploi (taux)', - 'Health Deprivation and Disability Score': 'Score de sant\u00E9 et handicap', + 'Employment Score (rate)': 'Score d’emploi (taux)', + 'Health Deprivation and Disability Score': 'Score de santé et handicap', 'Living Environment Score': 'Score du cadre de vie', - 'Indoors Sub-domain Score': 'Score du sous-domaine int\u00E9rieur', - 'Outdoors Sub-domain Score': 'Score du sous-domaine ext\u00E9rieur', + 'Indoors Sub-domain Score': 'Score du sous-domaine intérieur', + 'Outdoors Sub-domain Score': 'Score du sous-domaine extérieur', // ─ Feature names (Crime) ─ 'Serious crime per 1k residents (avg/yr)': 'Crimes graves pour 1k habitants (moy./an)', - 'Minor crime per 1k residents (avg/yr)': 'D\u00E9lits mineurs pour 1k habitants (moy./an)', + 'Minor crime per 1k residents (avg/yr)': 'Délits mineurs pour 1k habitants (moy./an)', 'Serious crime (avg/yr)': 'Crimes graves (moy./an)', - 'Minor crime (avg/yr)': 'D\u00E9lits mineurs (moy./an)', + 'Minor crime (avg/yr)': 'Délits mineurs (moy./an)', 'Violence and sexual offences (avg/yr)': 'Violences et infractions sexuelles (moy./an)', 'Burglary (avg/yr)': 'Cambriolages (moy./an)', 'Robbery (avg/yr)': 'Vols avec violence (moy./an)', - 'Vehicle crime (avg/yr)': 'Crimes li\u00E9s aux v\u00E9hicules (moy./an)', + 'Vehicle crime (avg/yr)': 'Crimes liés aux véhicules (moy./an)', 'Anti-social behaviour (avg/yr)': 'Comportements antisociaux (moy./an)', - 'Criminal damage and arson (avg/yr)': 'D\u00E9gradations et incendies criminels (moy./an)', + 'Criminal damage and arson (avg/yr)': 'Dégradations et incendies criminels (moy./an)', 'Other theft (avg/yr)': 'Autres vols (moy./an)', - 'Theft from the person (avg/yr)': 'Vols \u00E0 la personne (moy./an)', - 'Shoplifting (avg/yr)': 'Vols \u00E0 l\u2019\u00E9talage (moy./an)', - 'Bicycle theft (avg/yr)': 'Vols de v\u00E9los (moy./an)', - 'Drugs (avg/yr)': 'Infractions li\u00E9es aux stup\u00E9fiants (moy./an)', - 'Possession of weapons (avg/yr)': 'Possession d\u2019armes (moy./an)', - 'Public order (avg/yr)': 'Troubles \u00E0 l\u2019ordre public (moy./an)', + 'Theft from the person (avg/yr)': 'Vols à la personne (moy./an)', + 'Shoplifting (avg/yr)': 'Vols à l’étalage (moy./an)', + 'Bicycle theft (avg/yr)': 'Vols de vélos (moy./an)', + 'Drugs (avg/yr)': 'Infractions liées aux stupéfiants (moy./an)', + 'Possession of weapons (avg/yr)': 'Possession d’armes (moy./an)', + 'Public order (avg/yr)': 'Troubles à l’ordre public (moy./an)', 'Other crime (avg/yr)': 'Autres crimes (moy./an)', // ─ Feature names (Demographics) ─ - 'Median age': '\u00C2ge m\u00E9dian', + 'Median age': 'Âge médian', '% White': '% Blancs', '% South Asian': '% Sud-Asiatiques', '% Black': '% Noirs', '% East Asian': '% Est-Asiatiques', - '% Mixed': '% M\u00E9tis', + '% Mixed': '% Métis', '% Other': '% Autres', // ─ Feature names (Amenities) ─ 'Distance to nearest park (km)': 'Distance au parc le plus proche (km)', - 'Number of parks within 2km': 'Nombre de parcs \u00E0 moins de 2 km', - 'Number of restaurants within 2km': 'Nombre de restaurants \u00E0 moins de 2 km', - 'Number of grocery shops and supermarkets within 2km': 'Nombre d\u2019\u00E9piceries et supermarch\u00E9s \u00E0 moins de 2 km', + 'Number of parks within 2km': 'Nombre de parcs à moins de 2 km', + 'Number of restaurants within 2km': 'Nombre de restaurants à moins de 2 km', + 'Number of grocery shops and supermarkets within 2km': 'Nombre d’épiceries et supermarchés à moins de 2 km', 'Noise (dB)': 'Bruit (dB)', - 'Max available download speed (Mbps)': 'D\u00E9bit descendant max. disponible (Mbps)', + 'Max available download speed (Mbps)': 'Débit descendant max. disponible (Mbps)', // ─ Enum values ─ @@ -767,61 +776,61 @@ const fr: Translations = { 'For sale': 'En vente', 'For rent': 'En location', 'Detached': 'Individuelle', - 'Semi-Detached': 'Jumel\u00E9e', + 'Semi-Detached': 'Jumelée', 'Terraced': 'Mitoyenne', 'Flats/Maisonettes': 'Appartements/Duplex', 'Other': 'Autre', - 'Freehold': 'Pleine propri\u00E9t\u00E9', - 'Leasehold': 'Bail emphyt\u00E9otique', + 'Freehold': 'Pleine propriété', + 'Leasehold': 'Bail emphytéotique', 'Yes': 'Oui', 'No': 'Non', // ─ Stacked chart labels ─ 'Serious crime': 'Crimes graves', - 'Minor crime': 'D\u00E9lits mineurs', + 'Minor crime': 'Délits mineurs', 'Ethnic composition': 'Composition ethnique', // ─ POI group names ─ 'Public Transport': 'Transports en commun', 'Leisure': 'Loisirs', - 'Health': 'Sant\u00E9', - 'Emergency Services': 'Services d\u2019urgence', + 'Health': 'Santé', + 'Emergency Services': 'Services d’urgence', 'Groceries': 'Alimentation', - 'Local Businesses': 'Commerces de proximit\u00E9', + 'Local Businesses': 'Commerces de proximité', 'Culture': 'Culture', 'Services': 'Services', 'Shops': 'Boutiques', // ─ POI categories ─ - 'Airport': 'A\u00E9roport', + 'Airport': 'Aéroport', 'Ferry': 'Ferry', 'Rail station': 'Gare', - 'Bus stop': 'Arr\u00EAt de bus', - 'Bus station': 'Gare routi\u00E8re', + 'Bus stop': 'Arrêt de bus', + 'Bus station': 'Gare routière', 'Taxi rank': 'Station de taxi', - 'Metro or Tram stop': 'Station de m\u00E9tro ou tramway', - 'Caf\u00E9': 'Caf\u00E9', + 'Metro or Tram stop': 'Station de métro ou tramway', + 'Café': 'Café', 'Restaurant': 'Restaurant', 'Pub': 'Pub', 'Bar': 'Bar', 'Fast Food': 'Restauration rapide', - 'Nightclub': 'Bo\u00EEte de nuit', - 'Cinema': 'Cin\u00E9ma', - 'Theatre': 'Th\u00E9\u00E2tre', - 'Live Music & Events': 'Musique live et \u00E9v\u00E9nements', + 'Nightclub': 'Boîte de nuit', + 'Cinema': 'Cinéma', + 'Theatre': 'Théâtre', + 'Live Music & Events': 'Musique live et événements', 'Park': 'Parc', 'Playground': 'Aire de jeux', 'Sports Centre': 'Centre sportif', 'Entertainment': 'Divertissement', - 'Supermarket': 'Supermarch\u00E9', - 'Convenience Store': 'Sup\u00E9rette', + 'Supermarket': 'Supermarché', + 'Convenience Store': 'Supérette', 'Bakery': 'Boulangerie', 'Butcher & Fishmonger': 'Boucherie et poissonnerie', 'Greengrocer': 'Primeur', 'Off-Licence': 'Caviste', - 'Deli & Specialty': 'Traiteur et \u00E9picerie fine', - 'Fashion & Clothing': 'Mode et v\u00EAtements', - 'Electronics': '\u00C9lectronique', + 'Deli & Specialty': 'Traiteur et épicerie fine', + 'Fashion & Clothing': 'Mode et vêtements', + 'Electronics': 'Électronique', 'Charity Shop': 'Boutique caritative', 'DIY & Hardware': 'Bricolage et quincaillerie', 'Home & Garden': 'Maison et jardin', @@ -830,37 +839,37 @@ const fr: Translations = { 'Sports & Outdoor': 'Sports et plein air', 'Newsagent': 'Marchand de journaux', 'Department Store': 'Grand magasin', - 'Gift & Hobby': 'Cadeaux et loisirs cr\u00E9atifs', - 'Specialist Shop': 'Boutique sp\u00E9cialis\u00E9e', - 'Hairdresser & Beauty': 'Coiffure et beaut\u00E9', + 'Gift & Hobby': 'Cadeaux et loisirs créatifs', + 'Specialist Shop': 'Boutique spécialisée', + 'Hairdresser & Beauty': 'Coiffure et beauté', 'Gym & Fitness': 'Salle de sport', 'Dry Cleaner & Laundry': 'Pressing et laverie', 'Car Services': 'Services automobiles', 'Post Office': 'Bureau de poste', - 'Vet & Pet Care': 'V\u00E9t\u00E9rinaire et soins animaliers', + 'Vet & Pet Care': 'Vétérinaire et soins animaliers', 'Bank': 'Banque', 'Travel Agent': 'Agence de voyage', 'Police': 'Police', 'Fire Station': 'Caserne de pompiers', 'Ambulance Station': 'Centre ambulancier', - 'GP Surgery': 'Cabinet m\u00E9dical', + 'GP Surgery': 'Cabinet médical', 'Dentist': 'Dentiste', 'Pharmacy': 'Pharmacie', - 'Hospital & Clinic': 'H\u00F4pital et clinique', + 'Hospital & Clinic': 'Hôpital et clinique', 'Optician': 'Opticien', - 'Physiotherapy': 'Kin\u00E9sith\u00E9rapie', - 'Counselling & Therapy': 'Conseil et th\u00E9rapie', + 'Physiotherapy': 'Kinésithérapie', + 'Counselling & Therapy': 'Conseil et thérapie', 'Care Home': 'Maison de retraite', - 'Medical & Mobility': 'Mat\u00E9riel m\u00E9dical et mobilit\u00E9', - 'Museum': 'Mus\u00E9e', + 'Medical & Mobility': 'Matériel médical et mobilité', + 'Museum': 'Musée', 'Gallery': 'Galerie', - 'Library': 'Biblioth\u00E8que', + 'Library': 'Bibliothèque', 'Place of Worship': 'Lieu de culte', 'Arts Centre': 'Centre artistique', 'Zoo': 'Zoo', 'Tourist Attraction': 'Attraction touristique', - 'School': '\u00C9cole', - 'Hotel': 'H\u00F4tel', + 'School': 'École', + 'Hotel': 'Hôtel', 'Local Business': 'Commerce local', 'Offices': 'Bureaux', 'EV Charging': 'Borne de recharge', @@ -870,12 +879,12 @@ const fr: Translations = { // ─ Suffixes (used in formatters) ─ '/mo': '/mois', '/yr': '/an', - ' sqm': ' m\u00B2', + ' sqm': ' m²', ' km': ' km', ' m': ' m', ' dB': ' dB', ' years': ' ans', - ' rooms': ' pi\u00E8ces', + ' rooms': ' pièces', }, }; diff --git a/frontend/src/i18n/locales/hu.ts b/frontend/src/i18n/locales/hu.ts index a15847d..1c78e5d 100644 --- a/frontend/src/i18n/locales/hu.ts +++ b/frontend/src/i18n/locales/hu.ts @@ -3,32 +3,32 @@ import type { Translations } from './en'; const hu: Translations = { // ── Common ────────────────────────────────────────── common: { - save: 'Ment\u00E9s', - cancel: 'M\u00E9gse', - close: 'Bez\u00E1r\u00E1s', - delete: 'T\u00F6rl\u00E9s', - open: 'Megnyit\u00E1s', - share: 'Megoszt\u00E1s', - copy: 'M\u00E1sol\u00E1s', - copied: 'M\u00E1solva!', - copiedToClipboard: 'V\u00E1g\u00F3lapra m\u00E1solva', - loading: 'Bet\u00F6lt\u00E9s...', - loadMore: 'Tov\u00E1bbiak bet\u00F6lt\u00E9se', - remaining: 'm\u00E9g {{count}} h\u00E1tra', - search: 'Keres\u00E9s', + save: 'Mentés', + cancel: 'Mégse', + close: 'Bezárás', + delete: 'Törlés', + open: 'Megnyitás', + share: 'Megosztás', + copy: 'Másolás', + copied: 'Másolva!', + copiedToClipboard: 'Vágólapra másolva', + loading: 'Betöltés...', + loadMore: 'Továbbiak betöltése', + remaining: 'még {{count}} hátra', + search: 'Keresés', all: 'Mind', none: 'Egyik sem', - viewDataSource: 'Adatforr\u00E1s megtekint\u00E9se', - total: '\u00D6sszesen', + viewDataSource: 'Adatforrás megtekintése', + total: 'Összesen', min: 'perc', or: 'vagy', - area: 'Ter\u00FClet', + area: 'Terület', properties: 'Ingatlanok', - postcode: 'Ir\u00E1ny\u00EDt\u00F3sz\u00E1m', - noAreaSelected: 'Nincs kiv\u00E1lasztott ter\u00FClet', + postcode: 'Irányítószám', + noAreaSelected: 'Nincs kiválasztott terület', noAreaSelectedDesc: - 'Kattints b\u00E1rmelyik sz\u00EDnes ter\u00FCletre a t\u00E9rk\u00E9pen a b\u0171n\u00F6z\u00E9s, iskol\u00E1k, \u00E1rak \u00E9s egy\u00E9b adatok megtekint\u00E9s\u00E9hez', - clickForDetails: 'Kattints a r\u00E9szletekhez', + 'Kattints bármelyik színes területre a térképen a bűnözés, iskolák, árak és egyéb adatok megtekintéséhez', + clickForDetails: 'Kattints a részletekhez', property: 'ingatlan', propertiesPlural: 'ingatlan', }, @@ -36,576 +36,586 @@ const hu: Translations = { // ── Header / Nav ─────────────────────────────────── header: { appName: 'Perfect Postcode', - dashboard: 'T\u00E9rk\u00E9p', - learn: 'Tudnival\u00F3k', - pricing: '\u00C1rak', - inviteFriends: 'Bar\u00E1tok megh\u00EDv\u00E1sa', + dashboard: 'Térkép', + learn: 'Tudnivalók', + pricing: 'Árak', + inviteFriends: 'Barátok meghívása', saved: 'Mentett', - logIn: 'Bejelentkez\u00E9s', - createAccount: 'Regisztr\u00E1ci\u00F3', - sharing: 'Megoszt\u00E1s...', - exportLabel: 'Export\u00E1l\u00E1s', - exporting: 'Export\u00E1l\u00E1s...', - exportToExcel: 'Export\u00E1l\u00E1s Excelbe', - openMenu: 'Men\u00FC megnyit\u00E1sa', - closeMenu: 'Men\u00FC bez\u00E1r\u00E1sa', + logIn: 'Bejelentkezés', + createAccount: 'Regisztráció', + sharing: 'Megosztás...', + exportLabel: 'Exportálás', + exporting: 'Exportálás...', + exportToExcel: 'Exportálás Excelbe', + openMenu: 'Menü megnyitása', + closeMenu: 'Menü bezárása', }, // ── User Menu ────────────────────────────────────── userMenu: { - fullAccess: 'Teljes hozz\u00E1f\u00E9r\u00E9s', - demo: 'Dem\u00F3', - themeLight: 'T\u00E9ma: Vil\u00E1gos', - themeDark: 'T\u00E9ma: S\u00F6t\u00E9t', - account: 'Fi\u00F3k', - logOut: 'Kijelentkez\u00E9s', + fullAccess: 'Teljes hozzáférés', + demo: 'Demó', + themeLight: 'Téma: Világos', + themeDark: 'Téma: Sötét', + account: 'Fiók', + logOut: 'Kijelentkezés', }, // ── Mobile Menu ──────────────────────────────────── mobileMenu: { - menu: 'Men\u00FC', - home: 'F\u0151oldal', + menu: 'Menü', + home: 'Főoldal', }, // ── Auth Modal ───────────────────────────────────── auth: { - logIn: 'Bejelentkez\u00E9s', - createAccount: 'Regisztr\u00E1ci\u00F3', - resetPassword: 'Jelsz\u00F3 vissza\u00E1ll\u00EDt\u00E1sa', - valueProp: 'Mentsd el a keres\u00E9seidet, jel\u00F6ld meg az ingatlanokat, \u00E9s folytasd ott, ahol abbahagytad.', - continueWithGoogle: 'Folytat\u00E1s Google-lel', + logIn: 'Bejelentkezés', + createAccount: 'Regisztráció', + resetPassword: 'Jelszó visszaállítása', + valueProp: 'Mentsd el a kereséseidet, jelöld meg az ingatlanokat, és folytasd ott, ahol abbahagytad.', + continueWithGoogle: 'Folytatás Google-lel', email: 'E-mail', emailPlaceholder: 'te@pelda.hu', - password: 'Jelsz\u00F3', + password: 'Jelszó', passwordPlaceholderRegister: 'Minimum 8 karakter', passwordPlaceholderLogin: 'Jelszavad', forgotPassword: 'Elfelejtetted a jelszavad?', - resetSent: 'Ellen\u0151rizd az e-mailjeidet a vissza\u00E1ll\u00EDt\u00F3 linkhez.', - pleaseWait: 'K\u00E9rj\u00FCk, v\u00E1rj...', - sendResetLink: 'Vissza\u00E1ll\u00EDt\u00F3 link k\u00FCld\u00E9se', - backToLogin: 'Vissza a bejelentkez\u00E9shez', + resetSent: 'Ellenőrizd az e-mailjeidet a visszaállító linkhez.', + pleaseWait: 'Kérjük, várj...', + sendResetLink: 'Visszaállító link küldése', + backToLogin: 'Vissza a bejelentkezéshez', }, // ── Upgrade Modal ────────────────────────────────── upgrade: { - title: 'Fedezd fel eg\u00E9sz Angli\u00E1t', - description: 'Jelenleg a dem\u00F3 ter\u00FCletet felfedezed. Szerezz \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9st minden ir\u00E1ny\u00EDt\u00F3sz\u00E1mhoz, sz\u0171r\u0151h\u00F6z \u00E9s k\u00F6rny\u00E9khez. Egyetlen fizet\u00E9s, \u00F6r\u00F6kre.', + title: 'Fedezd fel egész Angliát', + description: 'Jelenleg a demó területet felfedezed. Szerezz élethosszig tartó hozzáférést minden irányítószámhoz, szűrőhöz és környékhez. Egyetlen fizetés, örökre.', free: 'Ingyenes', once: '/egyszeri', - freeForEarly: 'Ingyenes a korai felhaszn\u00E1l\u00F3knak. Nem sz\u00FCks\u00E9ges bankkartya.', - oneTimePayment: 'Egyszeri fizet\u00E9s. \u00C9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9s. 30 napos p\u00E9nzvisszat\u00E9r\u00EDt\u00E9si garancia.', - redirecting: '\u00C1tir\u00E1ny\u00EDt\u00E1s...', - claimFreeAccess: 'Ingyenes hozz\u00E1f\u00E9r\u00E9s ig\u00E9nyl\u00E9se', - upgradeFor: 'Friss\u00EDt\u00E9s {{price}} \u00E1ron', - registerAndUpgrade: 'Regisztr\u00E1ci\u00F3 \u00E9s friss\u00EDt\u00E9s', - alreadyHaveAccount: 'M\u00E1r van fi\u00F3kod? Jelentkezz be', - continueWithDemo: 'Folytat\u00E1s dem\u00F3val', - checkoutFailed: 'A fizet\u00E9s sikertelen', + freeForEarly: 'Ingyenes a korai felhasználóknak. Nem szükséges bankkartya.', + oneTimePayment: 'Egyszeri fizetés. Élethosszig tartó hozzáférés. 30 napos pénzvisszatérítési garancia.', + redirecting: 'Átirányítás...', + claimFreeAccess: 'Ingyenes hozzáférés igénylése', + upgradeFor: 'Frissítés {{price}} áron', + registerAndUpgrade: 'Regisztráció és frissítés', + alreadyHaveAccount: 'Már van fiókod? Jelentkezz be', + continueWithDemo: 'Folytatás demóval', + checkoutFailed: 'A fizetés sikertelen', }, // ── Save Search Modal ────────────────────────────── saveSearch: { - title: 'Keres\u00E9s ment\u00E9se', - saved: 'Keres\u00E9s elmentve', - savedSuccess: 'A keres\u00E9s sikeresen elmentve.', - viewSavedSearches: 'Mentett keres\u00E9sek megtekint\u00E9se', - name: 'N\u00E9v', - namePlaceholder: 'Keres\u00E9sem', - saving: 'Ment\u00E9s...', + title: 'Keresés mentése', + saved: 'Keresés elmentve', + savedSuccess: 'A keresés sikeresen elmentve.', + viewSavedSearches: 'Mentett keresések megtekintése', + name: 'Név', + namePlaceholder: 'Keresésem', + saving: 'Mentés...', }, // ── License Success ──────────────────────────────── licenseSuccess: { title: 'Benne vagy.', - subtitle: 'Az \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9sed most akt\u00EDv.', - description: 'Teljes hozz\u00E1f\u00E9r\u00E9s minden funkci\u00F3hoz, minden ir\u00E1ny\u00EDt\u00F3sz\u00E1mhoz, eg\u00E9sz Angli\u00E1ban.', - startExploring: 'Felfedez\u00E9s ind\u00EDt\u00E1sa', + subtitle: 'Az élethosszig tartó hozzáférésed most aktív.', + description: 'Teljes hozzáférés minden funkcióhoz, minden irányítószámhoz, egész Angliában.', + startExploring: 'Felfedezés indítása', }, // ── Filters ──────────────────────────────────────── filters: { - activeFilters: 'Akt\u00EDv sz\u0171r\u0151k', - addFilter: 'Sz\u0171r\u0151 hozz\u00E1ad\u00E1sa', - historical: 'T\u00F6rt\u00E9nelmi', - buy: 'V\u00E9tel', - rent: 'B\u00E9rl\u00E9s', - findingPerfectPostcode: 'A t\u00F6k\u00E9letes ir\u00E1ny\u00EDt\u00F3sz\u00E1m megtal\u00E1l\u00E1sa', - addFiltersHint: 'Adj hozz\u00E1 sz\u0171r\u0151ket a t\u00E9rk\u00E9p sz\u0171k\u00EDt\u00E9s\u00E9hez a felt\u00E9teleidnek megfelel\u0151en', - upgradePrompt: 'B\u0171n\u00F6z\u00E9s, iskol\u00E1k, zaj, sz\u00E9less\u00E1v \u00E9s 50+ tov\u00E1bbi sz\u0171r\u0151 eg\u00E9sz Angli\u00E1ban.', - oneTimeLifetime: 'Egyszeri fizet\u00E9s, \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9s.', - upgradeToFullMap: 'Friss\u00EDt\u00E9s a teljes t\u00E9rk\u00E9pre', - chooseFilters: 'V\u00E1laszd ki a sz\u00E1modra fontos sz\u0171r\u0151ket. A t\u00E9rk\u00E9p menet k\u00F6zben friss\u00FCl.', - searchFeatures: 'Jellemz\u0151k keres\u00E9se...', - noMatchingFeatures: 'Nincs tal\u00E1lat', - tryDifferentSearch: 'Pr\u00F3b\u00E1lj m\u00E1s keres\u0151kifejez\u00E9st', - allFeaturesActive: 'Minden jellemz\u0151 akt\u00EDv', - removeFilterHint: 'T\u00E1vol\u00EDts el egy sz\u0171r\u0151t az el\u00E9rhet\u0151 jellemz\u0151k megtekint\u00E9s\u00E9hez', - featureInfo: 'Jellemz\u0151 inform\u00E1ci\u00F3', - replayTutorial: 'Interakt\u00EDv bemutat\u00F3 \u00FAjraj\u00E1tsz\u00E1sa', + activeFilters: 'Aktív szűrők', + addFilter: 'Szűrő hozzáadása', + historical: 'Történelmi', + buy: 'Vétel', + rent: 'Bérlés', + findingPerfectPostcode: 'A tökéletes irányítószám megtalálása', + addFiltersHint: 'Adj hozzá szűrőket a térkép szűkítéséhez a feltételeidnek megfelelően', + upgradePrompt: 'Bűnözés, iskolák, zaj, szélessáv és 50+ további szűrő egész Angliában.', + oneTimeLifetime: 'Egyszeri fizetés, élethosszig tartó hozzáférés.', + upgradeToFullMap: 'Frissítés a teljes térképre', + chooseFilters: 'Válaszd ki a számodra fontos szűrőket. A térkép menet közben frissül.', + searchFeatures: 'Jellemzők keresése...', + noMatchingFeatures: 'Nincs találat', + tryDifferentSearch: 'Próbálj más keresőkifejezést', + allFeaturesActive: 'Minden jellemző aktív', + removeFilterHint: 'Távolíts el egy szűrőt az elérhető jellemzők megtekintéséhez', + featureInfo: 'Jellemző információ', + replayTutorial: 'Interaktív bemutató újrajátszása', + clearAll: 'Összes törlése', + clearAllTitle: 'Összes szűrő törlése?', + clearAllSavePrompt: 'Szeretnéd menteni a jelenlegi szűrőket a törlés előtt?', + saveAndClear: 'Mentés és törlés', + clearWithoutSaving: 'Törlés mentés nélkül', }, // ── Philosophy Popup ─────────────────────────────── philosophy: { - intro: 'Kezdd a felt\u00E9tlen\u00FCl sz\u00FCks\u00E9ges felt\u00E9telekkel, majd add hozz\u00E1 a k\u00EDv\u00E1nalmakat. A t\u00E9rk\u00E9p sz\u0171k\u00FCl, ahogy sz\u0171r\u0151ket adsz hozz\u00E1. A megmarad\u00F3 ter\u00FCletek a legjobb tal\u00E1latok.', - step1Title: 'K\u00F6lts\u00E9gvet\u00E9s \u00E9s alapok', - step1Desc: '(\u00E1rtartom\u00E1ny, alapter\u00FClet, ingatlant\u00EDpus)', - step2Title: 'Ingaz\u00E1s', - step2Desc: '(utaz\u00E1si id\u0151 a munkahelyre aut\u00F3val, ker\u00E9kp\u00E1rral vagy t\u00F6megk\u00F6zleked\u00E9ssel)', - step3Title: 'Biztons\u00E1g', - step3Desc: '(b\u0171n\u00F6z\u00E9si ar\u00E1nyok, zajszintek, talajstabilit\u00E1s)', - step4Title: 'Iskol\u00E1k', - step4Desc: '(k\u00F6zeli Ofsted \u00E1ltal J\u00F3 vagy Kiv\u00E1l\u00F3 min\u0151s\u00EDt\u00E9s\u0171 iskol\u00E1k)', - step5Title: '\u00C9letm\u00F3d', - step5Desc: '(\u00E9ttermek, parkok, sz\u00E9less\u00E1v\u00FA internet sebess\u00E9g)', + intro: 'Kezdd a feltétlenül szükséges feltételekkel, majd add hozzá a kívánalmakat. A térkép szűkül, ahogy szűrőket adsz hozzá. A megmaradó területek a legjobb találatok.', + step1Title: 'Költségvetés és alapok', + step1Desc: '(ártartomány, alapterület, ingatlantípus)', + step2Title: 'Ingazás', + step2Desc: '(utazási idő a munkahelyre autóval, kerékpárral vagy tömegközlekedéssel)', + step3Title: 'Biztonság', + step3Desc: '(bűnözési arányok, zajszintek, talajstabilitás)', + step4Title: 'Iskolák', + step4Desc: '(közeli Ofsted által Jó vagy Kiváló minősítésű iskolák)', + step5Title: 'Életmód', + step5Desc: '(éttermek, parkok, szélessávú internet sebesség)', step6Title: 'Energia', - step6Desc: '(EPC min\u0151s\u00EDt\u00E9sek, szigetel\u00E9s, f\u0171t\u00E9si k\u00F6lts\u00E9gek)', - tip: 'Tipp: ha semmi nem egyezik, engedj egy felt\u00E9telb\u0151l, \u00E9s n\u00E9zd meg, melyik kompromisszum nyitja meg a legt\u00F6bb lehet\u0151s\u00E9get.', + step6Desc: '(EPC minősítések, szigetelés, fűtési költségek)', + tip: 'Tipp: ha semmi nem egyezik, engedj egy feltételből, és nézd meg, melyik kompromisszum nyitja meg a legtöbb lehetőséget.', }, // ── Travel Time ──────────────────────────────────── travel: { - travelTime: 'Utaz\u00E1si id\u0151 ({{mode}})', - maxTime: 'Max. id\u0151', - selectDestination: '\u00DAtic\u00E9l kiv\u00E1laszt\u00E1sa...', + travelTime: 'Utazási idő ({{mode}})', + maxTime: 'Max. idő', + selectDestination: 'Úticél kiválasztása...', bestCase: 'Legjobb eset', - bestCaseTitle: 'Legjobb utaz\u00E1si id\u0151', - bestCaseDesc: 'A leggyorsabb re\u00E1lis utaz\u00E1si id\u0151t haszn\u00E1lja (ha j\u00F3l id\u0151z\u00EDted az indul\u00E1st \u00E9s j\u00F3 csatlakoz\u00E1sokat \u00E9rsz el). Az alap\u00E9rtelmezett a medi\u00E1nt haszn\u00E1lja, ami egy \u00E1tlagos utaz\u00E1st k\u00E9pvisel, f\u00FCggetlen\u00FCl az indul\u00E1s idej\u00E9t\u0151l.', - previewOnMap: 'El\u0151n\u00E9zet a t\u00E9rk\u00E9pen', - stopPreviewing: 'El\u0151n\u00E9zet le\u00E1ll\u00EDt\u00E1sa', - removeTravelTime: 'Utaz\u00E1si id\u0151 elt\u00E1vol\u00EDt\u00E1sa', - addTravelTime: '{{mode}} utaz\u00E1si id\u0151 hozz\u00E1ad\u00E1sa', - clearDestination: '\u00DAtic\u00E9l t\u00F6rl\u00E9se', - typeToFilter: 'G\u00E9pelj a sz\u0171r\u00E9shez...', - noDestinations: 'Nem tal\u00E1lhat\u00F3 \u00FAtic\u00E9l', - modeCar: 'Aut\u00F3', - modeBicycle: 'Ker\u00E9kp\u00E1r', + bestCaseTitle: 'Legjobb utazási idő', + bestCaseDesc: 'A leggyorsabb reális utazási időt használja (ha jól időzíted az indulást és jó csatlakozásokat érsz el). Az alapértelmezett a mediánt használja, ami egy átlagos utazást képvisel, függetlenül az indulás idejétől.', + previewOnMap: 'Előnézet a térképen', + stopPreviewing: 'Előnézet leállítása', + removeTravelTime: 'Utazási idő eltávolítása', + addTravelTime: '{{mode}} utazási idő hozzáadása', + clearDestination: 'Úticél törlése', + typeToFilter: 'Gépelj a szűréshez...', + noDestinations: 'Nem található úticél', + modeCar: 'Autó', + modeBicycle: 'Kerékpár', modeWalking: 'Gyalog', - modeTransit: 'T\u00F6megk\u00F6zleked\u00E9s', - modeCarDesc: 'Aut\u00F3s menetid\u0151 a leggyorsabb \u00FAton', - modeBicycleDesc: 'Ker\u00E9kp\u00E1ros menetid\u0151 ker\u00E9kp\u00E1rbar\u00E1t \u00FAtvonalakon', - modeWalkingDesc: 'Gyalogos menetid\u0151 s\u00E9t\u00E1l\u00F3utakon \u00E9s j\u00E1rd\u00E1kon', - modeTransitDesc: 'Utaz\u00E1si id\u0151 vonattal, metr\u00F3val \u00E9s busszal', + modeTransit: 'Tömegközlekedés', + modeCarDesc: 'Autós menetidő a leggyorsabb úton', + modeBicycleDesc: 'Kerékpáros menetidő kerékpárbarát útvonalakon', + modeWalkingDesc: 'Gyalogos menetidő sétálóutakon és járdákon', + modeTransitDesc: 'Utazási idő vonattal, metróval és busszal', }, // ── Travel Time Info Popup ───────────────────────── travelInfo: { - transitDesc: ' t\u00F6megk\u00F6zleked\u00E9ssel (busz, vonat, metr\u00F3). Az id\u0151ket egy \u00E1tlagos h\u00E9tk\u00F6znap d\u00E9lel\u0151tti id\u0151ablakra sz\u00E1m\u00EDtjuk.', - carDesc: ' aut\u00F3val, a t\u00EDpikus sebess\u00E9gek \u00E9s az \u00FAth\u00E1l\u00F3zat alapj\u00E1n.', - bicycleDesc: ' ker\u00E9kp\u00E1rral, ker\u00E9kp\u00E1rbar\u00E1t \u00FAtvonalakon.', - walkingDesc: ' gyalog, s\u00E9t\u00E1l\u00F3utakon \u00E9s j\u00E1rd\u00E1kon.', - mainDesc: 'Megmutatja, mennyi id\u0151be telik a kiv\u00E1lasztott \u00FAtic\u00E9l el\u00E9r\u00E9se az egyes ter\u00FCletekr\u0151l', - sliderHint: 'Haszn\u00E1ld a cs\u00FAszk\u00E1t a maxim\u00E1lis ingaz\u00E1si id\u0151 be\u00E1ll\u00EDt\u00E1s\u00E1hoz.', + transitDesc: ' tömegközlekedéssel (busz, vonat, metró). Az időket egy átlagos hétköznap délelőtti időablakra számítjuk.', + carDesc: ' autóval, a típikus sebességek és az úthálózat alapján.', + bicycleDesc: ' kerékpárral, kerékpárbarát útvonalakon.', + walkingDesc: ' gyalog, sétálóutakon és járdákon.', + mainDesc: 'Megmutatja, mennyi időbe telik a kiválasztott úticél elérése az egyes területekről', + sliderHint: 'Használd a csúszkát a maximális ingazási idő beállításához.', }, // ── AI Filter ────────────────────────────────────── aiFilter: { - describeIdealArea: '\u00CDrd le az ide\u00E1lis ter\u00FCleted mesters\u00E9ges intelligenci\u00E1val', - aiSearch: 'AI keres\u00E9s', - describeHint: '\u00CDrd le, mit keresel', - placeholder: 'pl. csendes ter\u00FClet, \u00A3400e alatt, j\u00F3 iskol\u00E1k k\u00F6zel\u00E9ben...', - example1: 'Biztons\u00E1gos ter\u00FClet j\u00F3 iskol\u00E1k k\u00F6zel\u00E9ben', - example2: '30 perces ingaz\u00E1s Kings Cross-hoz, \u00A3500e alatt', - example3: 'Csendes falu, 3 h\u00E1l\u00F3, gyors internet', - analysing: 'Lek\u00E9rdez\u00E9s elemz\u00E9se...', - searchingDestinations: '\u00DAtic\u00E9lok keres\u00E9se...', - generatingFilters: 'Sz\u0171r\u0151k l\u00E9trehoz\u00E1sa...', - refiningResults: 'Eredm\u00E9nyek finomhangol\u00E1sa...', - weeklyLimitReached: 'El\u00E9rted a heti AI haszn\u00E1lati limitet. Automatikusan vissza\u00E1ll j\u00F6v\u0151 h\u00E9ten.', + describeIdealArea: 'Írd le az ideális területed mesterséges intelligenciával', + aiSearch: 'AI keresés', + describeHint: 'Írd le, mit keresel', + placeholder: 'pl. csendes terület, £400e alatt, jó iskolák közelében...', + example1: 'Biztonságos terület jó iskolák közelében', + example2: '30 perces ingazás Kings Cross-hoz, £500e alatt', + example3: 'Csendes falu, 3 háló, gyors internet', + analysing: 'Lekérdezés elemzése...', + searchingDestinations: 'Úticélok keresése...', + generatingFilters: 'Szűrők létrehozása...', + refiningResults: 'Eredmények finomhangolása...', + weeklyLimitReached: 'Elérted a heti AI használati limitet. Automatikusan visszaáll jövő héten.', }, // ── Map Legend ───────────────────────────────────── mapLegend: { - clearColourView: 'Sz\u00EDnez\u00E9s t\u00F6rl\u00E9se', + clearColourView: 'Színezés törlése', + historicalMatches: 'Korábbi ingatlan találatok', + propertiesForSale: 'Eladó ingatlanok', + propertiesForRent: 'Kiadó ingatlanok', + numberOfProperties: 'Ingatlanok száma', + previewing: '\u201c{{name}}\u201d előnézete', }, // ── Properties Pane ──────────────────────────────── propertyCard: { - unknownAddress: 'Ismeretlen c\u00EDm', - unsaveProperty: 'Ingatlan ment\u00E9s\u00E9nek visszavon\u00E1sa', - saveProperty: 'Ingatlan ment\u00E9se', - lastSold: 'Utols\u00F3 elad\u00E1s: \u00A3{{price}}', - estValue: 'Becs\u00FClt \u00E9rt\u00E9k:', - type: 'T\u00EDpus:', - builtForm: '\u00C9p\u00FCletforma:', + unknownAddress: 'Ismeretlen cím', + unsaveProperty: 'Ingatlan mentésének visszavonása', + saveProperty: 'Ingatlan mentése', + lastSold: 'Utolsó eladás: £{{price}}', + estValue: 'Becsült érték:', + type: 'Típus:', + builtForm: 'Épületforma:', tenure: 'Tulajdonforma:', - floorArea: 'Alapter\u00FClet:', - bedrooms: 'H\u00E1l\u00F3szob\u00E1k:', - bathrooms: 'F\u00FCrd\u0151szob\u00E1k:', - rooms: 'Szob\u00E1k:', - built: '\u00C9p\u00EDtve:', - epcRating: 'EPC min\u0151s\u00EDt\u00E9s:', - epcPotential: 'EPC potenci\u00E1l:', + floorArea: 'Alapterület:', + bedrooms: 'Hálószobák:', + bathrooms: 'Fürdőszobák:', + rooms: 'Szobák:', + built: 'Építve:', + epcRating: 'EPC minősítés:', + epcPotential: 'EPC potenciál:', listed: 'Hirdetve:', - keyFeatures: 'F\u0151bb jellemz\u0151k', - renovations: 'Fel\u00FAj\u00EDt\u00E1sok', - viewExternalListing: 'K\u00FCls\u0151 hirdet\u00E9s megtekint\u00E9se', - perMonth: '/h\u00F3', - perSqm: '/m\u00B2', - searchPlaceholder: 'Keres\u00E9s c\u00EDm vagy ir\u00E1ny\u00EDt\u00F3sz\u00E1m alapj\u00E1n...', + keyFeatures: 'Főbb jellemzők', + renovations: 'Felújítások', + viewExternalListing: 'Külső hirdetés megtekintése', + perMonth: '/hó', + perSqm: '/m²', + searchPlaceholder: 'Keresés cím vagy irányítószám alapján...', propertyData: 'Ingatlanadatok', - propertyDataDesc: 'Az \u00E1rak a HM Land Registry-b\u0151l sz\u00E1rmaznak (a vev\u0151k \u00E1ltal t\u00E9nylegesen fizetett \u00F6sszeg). Az alapter\u00FClet, energetikai min\u0151s\u00EDt\u00E9sek, \u00E9p\u00EDt\u00E9si \u00E9v \u00E9s tulajdonforma a hivatalos EPC felm\u00E9r\u00E9sekb\u0151l sz\u00E1rmaznak. Mindk\u00E9t forr\u00E1s c\u00EDm alapj\u00E1n van \u00F6sszep\u00E1ros\u00EDtva az egyes ir\u00E1ny\u00EDt\u00F3sz\u00E1mokon bel\u00FCl.', + propertyDataDesc: 'Az árak a HM Land Registry-ből származnak (a vevők által ténylegesen fizetett összeg). Az alapterület, energetikai minősítések, építési év és tulajdonforma a hivatalos EPC felmérésekből származnak. Mindkét forrás cím alapján van összepárosítva az egyes irányítószámokon belül.', }, // ── Area Pane ────────────────────────────────────── areaPane: { - areaStatistics: 'Ter\u00FCleti statisztik\u00E1k', - statsFor: 'Statisztik\u00E1k a(z) {{type}} \u00F6sszes ingatlan\u00E1r\u00F3l', - matchingFilters: ' az \u00F6sszes akt\u00EDv sz\u0171r\u0151nek megfelel\u0151en', - viewProperties: '{{count}} ingatlan megtekint\u00E9se', - priceHistory: '\u00C1rt\u00F6rt\u00E9net', - journeysFrom: 'Utaz\u00E1sok innen: {{label}}', + areaStatistics: 'Területi statisztikák', + statsFor: 'Statisztikák a(z) {{type}} összes ingatlanáról', + matchingFilters: ' az összes aktív szűrőnek megfelelően', + viewProperties: '{{count}} ingatlan megtekintése', + priceHistory: 'Ártörténet', + journeysFrom: 'Utazások innen: {{label}}', to: 'Ide: {{destination}}', - noJourneyData: 'Nincs el\u00E9rhet\u0151 utaz\u00E1si adat', - viewOnGoogleMaps: 'Megtekint\u00E9s a Google Maps-en', + noJourneyData: 'Nincs elérhető utazási adat', + viewOnGoogleMaps: 'Megtekintés a Google Maps-en', walk: 'Gyalog', - cycle: 'Ker\u00E9kp\u00E1r', + cycle: 'Kerékpár', }, // ── Histogram Legend ─────────────────────────────── histogramLegend: { - tealBars: 'Z\u00F6ldeskk\u00E9k oszlopok', - tealBarsDesc: 'a kiv\u00E1lasztott ter\u00FClet eloszl\u00E1s\u00E1t mutatj\u00E1k', - greyBars: 'Sz\u00FCrke oszlopok', - greyBarsDesc: 'az \u00F6sszes ter\u00FClet \u00E1ltal\u00E1nos eloszl\u00E1s\u00E1t mutatj\u00E1k', + tealBars: 'Zöldeskkék oszlopok', + tealBarsDesc: 'a kiválasztott terület eloszlását mutatják', + greyBars: 'Szürke oszlopok', + greyBarsDesc: 'az összes terület általános eloszlását mutatják', dashedLine: 'Szaggatott vonal', - dashedLineDesc: 'az orsz\u00E1gos \u00E1tlagot jel\u00F6li', + dashedLineDesc: 'az országos átlagot jelöli', }, // ── Street View ──────────────────────────────────── streetView: { - title: 'Utcak\u00E9p', + title: 'Utcakép', }, // ── POI Pane ─────────────────────────────────────── poiPane: { pois: 'POI-k', - pointsOfInterest: '\u00C9rdekes pontok', - poiDescription: 'Forr\u00E1s: OpenStreetMap. Tartalmazza a t\u00F6megk\u00F6zleked\u00E9si meg\u00E1ll\u00F3kat, \u00FCzleteket, \u00E9ttermeket, eg\u00E9szs\u00E9g\u00FCgyi int\u00E9zm\u00E9nyeket, szabadid\u0151s l\u00E9tes\u00EDtm\u00E9nyeket \u00E9s m\u00E9g sok m\u00E1st. Rendszeresen friss\u00EDtve, teljes kateg\u00F3rialefedetts\u00E9ggel.', - searchCategories: 'Kateg\u00F3ri\u00E1k keres\u00E9se...', - dataSourceInfo: 'Adatforr\u00E1s inform\u00E1ci\u00F3', + pointsOfInterest: 'Érdekes pontok', + poiDescription: 'Forrás: OpenStreetMap. Tartalmazza a tömegközlekedési megállókat, üzleteket, éttermeket, egészségügyi intézményeket, szabadidős létesítményeket és még sok mást. Rendszeresen frissítve, teljes kategórialefedettséggel.', + searchCategories: 'Kategóriák keresése...', + dataSourceInfo: 'Adatforrás információ', }, // ── External Search Links ────────────────────────── externalSearch: { - searchOn: 'Keres\u00E9s {{radius}} sug\u00E1rban ezen:', - outcodeNotRecognised: 'Nem felismert k\u00F6rzeti k\u00F3d', + searchOn: 'Keresés {{radius}} sugárban ezen:', + outcodeNotRecognised: 'Nem felismert körzeti kód', }, // ── Location Search ──────────────────────────────── locationSearch: { - placeholder: 'Helyek vagy ir\u00E1ny\u00EDt\u00F3sz\u00E1mok keres\u00E9se...', - postcodeNotFound: 'Ir\u00E1ny\u00EDt\u00F3sz\u00E1m nem tal\u00E1lhat\u00F3', - lookupFailed: 'A keres\u00E9s sikertelen', - searchLabel: 'Helyek vagy ir\u00E1ny\u00EDt\u00F3sz\u00E1mok keres\u00E9se', - locateMe: 'Ugr\u00E1s a tart\u00F3zkod\u00E1si helyemre', - geolocationUnsupported: 'A b\u00F6ng\u00E9sz\u0151d nem t\u00E1mogatja a helymeghat\u00E1roz\u00E1st', - geolocationFailed: 'Nem siker\u00FClt meghat\u00E1rozni a tart\u00F3zkod\u00E1si helyed', + placeholder: 'Helyek vagy irányítószámok keresése...', + postcodeNotFound: 'Irányítószám nem található', + lookupFailed: 'A keresés sikertelen', + searchLabel: 'Helyek vagy irányítószámok keresése', + locateMe: 'Ugrás a tartózkodási helyemre', + geolocationUnsupported: 'A böngésződ nem támogatja a helymeghatározást', + geolocationFailed: 'Nem sikerült meghatározni a tartózkodási helyed', }, // ── Mobile Drawer ────────────────────────────────── mobileDrawer: { - closeDrawer: 'Fi\u00F3k bez\u00E1r\u00E1sa', + closeDrawer: 'Fiók bezárása', }, // ── Home Page ────────────────────────────────────── home: { - heroTitle1: 'Maxim\u00E1lis', - heroTitle2: '\u00C9rt\u00E9k', - heroTitle3: 'Minim\u00E1lis kompromisszum.', - heroSubtitle: 'Ingatlant keresel? Legyen a legnagyobb befektet\u00E9sed a legokosabb d\u00F6nt\u00E9sed.', - heroDescription: 'Annyi lehet\u0151s\u00E9g \u2013 a megfelel\u0151 kiv\u00E1laszt\u00E1sa neh\u00E9z lehet. Interakt\u00EDv t\u00E9rk\u00E9p\u00FCnk egyszer\u0171v\u00E9 teszi: v\u00E1laszd ki a felt\u00E9teleidet, \u00E9s azonnal l\u00E1sd a megfelel\u0151 ter\u00FCleteket.', - exploreTheMap: 'T\u00E9rk\u00E9p felfedez\u00E9se', - seeTheDifference: 'N\u00E9zd meg a k\u00FCl\u00F6nbs\u00E9get', + heroTitle1: 'Maximális', + heroTitle2: 'Érték', + heroTitle3: 'Minimális kompromisszum.', + heroSubtitle: 'Ingatlant keresel? Legyen a legnagyobb befektetésed a legokosabb döntésed.', + heroDescription: 'Annyi lehetőség – a megfelelő kiválasztása nehéz lehet. Interaktív térképünk egyszerűvé teszi: válaszd ki a feltételeidet, és azonnal lásd a megfelelő területeket.', + exploreTheMap: 'Térkép felfedezése', + seeTheDifference: 'Nézd meg a különbséget', statProperties: 'ingatlan', - statFilters: 'sz\u0171r\u0151', + statFilters: 'szűrő', statEvery: 'Minden', - statPostcodeInEngland: 'ir\u00E1ny\u00EDt\u00F3sz\u00E1m Angli\u00E1ban', - ourPhilosophy: 'Filoz\u00F3fi\u00E1nk', - philosophyP1: 'A Rightmove-on el\u0151sz\u00F6r ter\u00FCletet v\u00E1lasztasz, \u00E9s rem\u00E9led, hogy j\u00F3. V\u00E9g\u00FCl b\u0171n\u00F6z\u00E9si statisztik\u00E1kat, iskolai jelent\u00E9seket \u00E9s sz\u00E9less\u00E1v-ellen\u0151rz\u0151ket b\u00F6ng\u00E9szel tucat f\u00FCleken, egyszerre egy ir\u00E1ny\u00EDt\u00F3sz\u00E1mmal.', - philosophyP2: 'Mi megford\u00EDtjuk. Mondd el, mire van sz\u00FCks\u00E9ged (k\u00F6lts\u00E9gvet\u00E9s, ingaz\u00E1s, iskol\u00E1k, biztons\u00E1g), \u00E9s megmutatjuk Anglia \u00F6sszes megfelel\u0151 ter\u00FClet\u00E9t. Nincs tal\u00E1lgat\u00E1s. Nincs felesleges megtekint\u00E9s.', - howToUseIt: 'Hogyan haszn\u00E1ld', - howStep1Title: '\u00C1ll\u00EDtsd be a felt\u00E9teleidet', - howStep1Desc: 'K\u00F6lts\u00E9gvet\u00E9s, ingaz\u00E1s, iskol\u00E1k \u2014 a t\u00E9rk\u00E9p csak a megfelel\u0151ket mutatja.', - howStep2Title: 'Fedezz fel ter\u00FCleteket \u00E9s rejtett kincseket', - howStep2Desc: 'Nagy\u00EDts r\u00E1, m\u00E9lyedj el a r\u00E9szletekben \u00E9s a pluszokban.', - howStep3Title: 'Vizsg\u00E1ld meg az ir\u00E1ny\u00EDt\u00F3sz\u00E1mokat', - howStep3Desc: 'N\u00E9zd meg az egyes ingatlanokat, elad\u00E1si \u00E1rakat, alapter\u00FCletet, \u00E9s hasonl\u00EDtsd \u00F6ssze.', - howStep4Title: 'V\u00E1lassz magabiztosan', - howStep4Desc: 'A list\u00E1don minden ter\u00FClet megfelel a val\u00F3s felt\u00E9teleidnek \u2014 nem csak annak, amit azon a h\u00E9ten hirdettek.', - othersVs: 'M\u00E1sok vs.', - listingPortals: 'Hirdet\u00E9si port\u00E1lok', - checkMyPostcode: '\u201CIr\u00E1ny\u00EDt\u00F3sz\u00E1m ellen\u0151rz\u00E9se\u201D', - areaGuides: 'Ter\u00FCleti \u00FAtmutat\u00F3k', - compSearchWithout: 'Keres\u00E9s ter\u00FClet el\u0151zetes kiv\u00E1laszt\u00E1sa n\u00E9lk\u00FCl', - compSearchWithoutSub: '(ig\u00E9nyekb\u0151l indulj, nem helysz\u00EDnb\u0151l)', - compAreaData: 'Ter\u00FCleti adatok', - compAreaDataSub: '(b\u0171n\u00F6z\u00E9s, iskol\u00E1k, zaj, sz\u00E9less\u00E1v)', + statPostcodeInEngland: 'irányítószám Angliában', + ourPhilosophy: 'Filozófiánk', + philosophyP1: 'A Rightmove-on először területet választasz, és reméled, hogy jó. Végül bűnözési statisztikákat, iskolai jelentéseket és szélessáv-ellenőrzőket böngészel tucat füleken, egyszerre egy irányítószámmal.', + philosophyP2: 'Mi megfordítjuk. Mondd el, mire van szükséged (költségvetés, ingazás, iskolák, biztonság), és megmutatjuk Anglia összes megfelelő területét. Nincs találgatás. Nincs felesleges megtekintés.', + howToUseIt: 'Hogyan használd', + howStep1Title: 'Állítsd be a feltételeidet', + howStep1Desc: 'Költségvetés, ingazás, iskolák — a térkép csak a megfelelőket mutatja.', + howStep2Title: 'Fedezz fel területeket és rejtett kincseket', + howStep2Desc: 'Nagyíts rá, mélyedj el a részletekben és a pluszokban.', + howStep3Title: 'Vizsgáld meg az irányítószámokat', + howStep3Desc: 'Nézd meg az egyes ingatlanokat, eladási árakat, alapterületet, és hasonlítsd össze.', + howStep4Title: 'Válassz magabiztosan', + howStep4Desc: 'A listádon minden terület megfelel a valós feltételeidnek — nem csak annak, amit azon a héten hirdettek.', + othersVs: 'Mások vs.', + listingPortals: 'Hirdetési portálok', + checkMyPostcode: '“Irányítószám ellenőrzése”', + areaGuides: 'Területi útmutatók', + compSearchWithout: 'Keresés terület előzetes kiválasztása nélkül', + compSearchWithoutSub: '(igényekből indulj, nem helyszínből)', + compAreaData: 'Területi adatok', + compAreaDataSub: '(bűnözés, iskolák, zaj, szélessáv)', compPropertyData: 'Ingatlanspecifikus adatok', - compPropertyDataSub: '(\u00E1r, EPC, alapter\u00FClet)', - compFilters: '56 kombin\u00E1lhat\u00F3 sz\u0171r\u0151 egy helyen', - compFiltersSub: '(minden inform\u00E1ci\u00F3, egy interakt\u00EDv t\u00E9rk\u00E9p)', - ctaTitle: 'Legyen a legnagyobb befektet\u00E9sed a legokosabb\u00A0d\u00F6nt\u00E9sed.', - ctaDescription: 'Ez megfelel\u0151 eszk\u00F6z\u00F6ket \u00E9rdemel, ne b\u00EDzd a szerencs\u00E9re.', + compPropertyDataSub: '(ár, EPC, alapterület)', + compFilters: '56 kombinálható szűrő egy helyen', + compFiltersSub: '(minden információ, egy interaktív térkép)', + ctaTitle: 'Legyen a legnagyobb befektetésed a legokosabb döntésed.', + ctaDescription: 'Ez megfelelő eszközöket érdemel, ne bízd a szerencsére.', }, // ── Pricing Page ─────────────────────────────────── pricingPage: { - title: 'Korai hozz\u00E1f\u00E9r\u00E9s \u00E1rak', - subtitle: 'Fizess egyszer, haszn\u00E1ld \u00F6r\u00F6kre. Min\u00E9l kor\u00E1bban csatlakozol, ann\u00E1l kevesebbet fizetsz.', - costContext: 'Egy lak\u00E1sv\u00E1s\u00E1rl\u00E1s \u00A310 000+ illet\u00E9kbe, \u00A31 500 \u00FCgyv\u00E9di d\u00EDjba, \u00A3500 szak\u00E9rt\u0151i vizsg\u00E1latba ker\u00FCl. Ha rossz ter\u00FCletet v\u00E1lasztasz, r\u00E1ragadsz egy hossz\u00FA ingaz\u00E1sra, rossz iskol\u00E1kra, vagy egy \u00FAtra, amelyr\u0151l nem tudt\u00E1l.', - lessThanSurvey: 'Kevesebbe ker\u00FCl, mint egy \u00E9p\u00FCletszak\u00E9rt\u0151i vizsg\u00E1lat. Sokkal hasznosabb.', + title: 'Korai hozzáférés árak', + subtitle: 'Fizess egyszer, használd örökre. Minél korábban csatlakozol, annál kevesebbet fizetsz.', + costContext: 'Egy lakásvásárlás £10 000+ illetékbe, £1 500 ügyvédi díjba, £500 szakértői vizsgálatba kerül. Ha rossz területet választasz, ráragadsz egy hosszú ingazásra, rossz iskolákra, vagy egy útra, amelyről nem tudtál.', + lessThanSurvey: 'Kevesebbe kerül, mint egy épületszakértői vizsgálat. Sokkal hasznosabb.', currentTier: 'Jelenlegi szint', - firstNUsers: 'Els\u0151 {{count}} felhaszn\u00E1l\u00F3', - everyoneAfter: 'Mindenki m\u00E1s ut\u00E1na', - nextNUsers: 'K\u00F6vetkez\u0151 {{count}} felhaszn\u00E1l\u00F3', - lifetime: '/\u00E9lethosszig', + firstNUsers: 'Első {{count}} felhasználó', + everyoneAfter: 'Mindenki más utána', + nextNUsers: 'Következő {{count}} felhasználó', + lifetime: '/élethosszig', spotsRemaining: '{{count}} hely maradt', spotsRemainingPlural: '{{count}} hely maradt', filled: 'Betelt', - openDashboard: 'T\u00E9rk\u00E9p megnyit\u00E1sa', - getStarted: 'Kezdj\u00FCk el', - getStartedPrice: 'Kezdj\u00FCk el \u2013 {{price}}', - noCreditCard: 'Nem sz\u00FCks\u00E9ges bankkartya', - moneyBackGuarantee: '30 napos p\u00E9nzvisszat\u00E9r\u00EDt\u00E9si garancia', + openDashboard: 'Térkép megnyitása', + getStarted: 'Kezdjük el', + getStartedPrice: 'Kezdjük el – {{price}}', + noCreditCard: 'Nem szükséges bankkartya', + moneyBackGuarantee: '30 napos pénzvisszatérítési garancia', soldOut: 'Elfogyott', - upcoming: 'K\u00F6vetkez\u0151', - failedToLoad: 'Nem siker\u00FClt bet\u00F6lteni az \u00E1rakat. K\u00E9rj\u00FCk, pr\u00F3b\u00E1ld \u00FAjra k\u00E9s\u0151bb.', - feat1: '56 adatr\u00E9teg eg\u00E9sz Angli\u00E1ban', - feat2: 'Minden ir\u00E1ny\u00EDt\u00F3sz\u00E1m pontozva \u00E9s sz\u0171rhet\u0151', - feat3: 'Korl\u00E1tlan t\u00E9rk\u00E9pfelfedez\u00E9s \u00E9s export\u00E1l\u00E1s', - feat4: 'T\u00F6bb \u00E9vtizednyi t\u00F6rt\u00E9nelmi \u00E1radat', - feat5: 'B\u0171n\u00F6z\u00E9s, iskol\u00E1k, k\u00F6zleked\u00E9s, sz\u00E9less\u00E1v \u00E9s m\u00E9g sok m\u00E1s', - feat6: 'Minden j\u00F6v\u0151beli adatfriss\u00EDt\u00E9s benne van', + upcoming: 'Következő', + failedToLoad: 'Nem sikerült betölteni az árakat. Kérjük, próbáld újra később.', + feat1: '56 adatréteg egész Angliában', + feat2: 'Minden irányítószám pontozva és szűrhető', + feat3: 'Korlátlan térképfelfedezés és exportálás', + feat4: 'Több évtizednyi történelmi áradat', + feat5: 'Bűnözés, iskolák, közlekedés, szélessáv és még sok más', + feat6: 'Minden jövőbeli adatfrissítés benne van', }, // ── Learn Page ───────────────────────────────────── learnPage: { faq: 'GYIK', - dataSources: 'Adatforr\u00E1sok', - support: 'T\u00E1mogat\u00E1s', - dataSourcesIntro: 'Ez az alkalmaz\u00E1s {{count}} nyilv\u00E1nos adatk\u00E9szletet kombin\u00E1l, amelyek ingatllan\u00E1rakat, energetikai teljes\u00EDtm\u00E9nyt, k\u00F6zleked\u00E9st, demogr\u00E1fi\u00E1t, b\u0171n\u00F6z\u00E9st, k\u00F6rnyezetet \u00E9s m\u00E9g sok m\u00E1st fednek le.', - faqIntro: 'Ak\u00E1r v\u00E1s\u00E1rolsz, ak\u00E1r b\u00E9relsz, ak\u00E1r csak felfedezed, \u00EDgy seg\u00EDt a Perfect Postcode megtal\u00E1lni a megfelel\u0151 ter\u00FCletet.', - supportIntro: 'K\u00E9rd\u00E9sed van? N\u00E9zd meg a GYIK-et, vagy \u00EDrj nek\u00FCnk k\u00F6zvetlen\u00FCl.', - source: 'Forr\u00E1s:', - optOut: 'Nyilv\u00E1nos k\u00F6zz\u00E9t\u00E9tel visszautas\u00EDt\u00E1sa', - attribution: 'Forr\u00E1smegnevez\u00E9s', - attrLandRegistry: 'HM Land Registry adatokat tartalmaz \u00A9 Crown copyright and database right 2025.', - attrOgl: 'K\u00F6zszektorbeli inform\u00E1ci\u00F3t tartalmaz a k\u00F6vetkez\u0151 licenc alatt:', + dataSources: 'Adatforrások', + support: 'Támogatás', + dataSourcesIntro: 'Ez az alkalmazás {{count}} nyilvános adatkészletet kombinál, amelyek ingatllanárakat, energetikai teljesítményt, közlekedést, demográfiát, bűnözést, környezetet és még sok mást fednek le.', + faqIntro: 'Akár vásárolsz, akár bérelsz, akár csak felfedezed, így segít a Perfect Postcode megtalálni a megfelelő területet.', + supportIntro: 'Kérdésed van? Nézd meg a GYIK-et, vagy írj nekünk közvetlenül.', + source: 'Forrás:', + optOut: 'Nyilvános közzététel visszautasítása', + attribution: 'Forrásmegnevezés', + attrLandRegistry: 'HM Land Registry adatokat tartalmaz © Crown copyright and database right 2025.', + attrOgl: 'Közszektorbeli információt tartalmaz a következő licenc alatt:', attrOglLink: 'Open Government Licence v3.0', - attrOs: 'OS adatokat tartalmaz \u00A9 Crown copyright and database rights 2025.', - attrTfl: 'A TfL Open Data \u00E1ltal t\u00E1mogatva.', + attrOs: 'OS adatokat tartalmaz © Crown copyright and database rights 2025.', + attrTfl: 'A TfL Open Data által támogatva.', attrOsm: 'Adatokat tartalmaz innen:', - attrOsmContrib: '\u00A9 OpenStreetMap contributors', - attrOsmLicense: 'el\u00E9rhet\u0151 a k\u00F6vetkez\u0151 alatt:', + attrOsmContrib: '© OpenStreetMap contributors', + attrOsmLicense: 'elérhető a következő alatt:', attrOsmLicenseLink: 'Open Data Commons Open Database License (ODbL)', // Data source names & descriptions - dsPricePaidName: '\u00C1rfizetett adatok', + dsPricePaidName: 'Árfizetett adatok', dsPricePaidOrigin: 'HM Land Registry', - dsPricePaidUse: 'Teljes t\u00F6rt\u00E9nelmi ingatlanalad\u00E1si \u00E1rak Angli\u00E1ban.', - dsEpcName: 'Energetikai tan\u00FAs\u00EDtv\u00E1nyok (EPC)', + dsPricePaidUse: 'Teljes történelmi ingatlanaladási árak Angliában.', + dsEpcName: 'Energetikai tanúsítványok (EPC)', dsEpcOrigin: 'Ministry of Housing, Communities & Local Government', - dsEpcUse: 'Lak\u00F3ingatlan energetikai tan\u00FAs\u00EDtv\u00E1nyok, amelyek tartalmazzp\u00E1k az alapter\u00FCletet, szobasz\u00E1mot, \u00E9p\u00EDt\u00E9si \u00E9vet, energetikai min\u0151s\u00EDt\u00E9seket, ingatlant\u00EDpust \u00E9s \u00E9p\u00FCletform\u00E1t. Az \u00C1rfizetett nyilv\u00E1ntart\u00E1sokkal c\u00EDm alapj\u00E1n p\u00E1ros\u00EDtva az egyes ir\u00E1ny\u00EDt\u00F3sz\u00E1mokon bel\u00FCl. Az ingatlantulajdonosok visszautas\u00EDthatj\u00E1k a nyilv\u00E1nos k\u00F6zz\u00E9t\u00E9telt.', - dsNsplName: 'Nemzeti Statisztikai Ir\u00E1ny\u00EDt\u00F3sz\u00E1m Keres\u0151 (NSPL)', + dsEpcUse: 'Lakóingatlan energetikai tanúsítványok, amelyek tartalmazzpák az alapterületet, szobaszámot, építési évet, energetikai minősítéseket, ingatlantípust és épületformát. Az Árfizetett nyilvántartásokkal cím alapján párosítva az egyes irányítószámokon belül. Az ingatlantulajdonosok visszautasíthatják a nyilvános közzétételt.', + dsNsplName: 'Nemzeti Statisztikai Irányítószám Kereső (NSPL)', dsNsplOrigin: 'ONS / ArcGIS', - dsNsplUse: 'Ir\u00E1ny\u00EDt\u00F3sz\u00E1mokat koordin\u00E1t\u00E1khoz \u00E9s statisztikai ter\u00FCletk\u00F3dokhoz rendeli, amelyekkel az \u00F6sszes ter\u00FCleti szint\u0171 adatk\u00E9szletet az egyes ingatlanokhoz kapcsoljuk.', - dsIodName: 'Angol Depriv\u00E1ci\u00F3s Mutat\u00F3k 2025', + dsNsplUse: 'Irányítószámokat koordinátákhoz és statisztikai területkódokhoz rendeli, amelyekkel az összes területi szintű adatkészletet az egyes ingatlanokhoz kapcsoljuk.', + dsIodName: 'Angol Deprivációs Mutatók 2025', dsIodOrigin: 'Ministry of Housing, Communities & Local Government', - dsIodUse: 'Relat\u00EDv depriv\u00E1ci\u00F3s pontok j\u00F6vedelem, foglalkoztatotts\u00E1g, oktat\u00E1s, eg\u00E9szs\u00E9g, b\u0171n\u00F6z\u00E9s \u00E9s lak\u00F3k\u00F6rnyezet ter\u00FClet\u00E9n Anglia minden szomsz\u00E9ds\u00E1g\u00E1ra.', - dsEthnicityName: 'N\u00E9pess\u00E9g etnikai megoszl\u00E1s szerint (2021-es n\u00E9psz\u00E1ml\u00E1l\u00E1s)', + dsIodUse: 'Relatív deprivációs pontok jövedelem, foglalkoztatottság, oktatás, egészség, bűnözés és lakókörnyezet területén Anglia minden szomszédságára.', + dsEthnicityName: 'Népesség etnikai megoszlás szerint (2021-es népszámlálás)', dsEthnicityOrigin: 'ONS', - dsEthnicityUse: 'N\u00E9pess\u00E9g sz\u00E1zal\u00E9kos megoszl\u00E1sa etnikai csoportonk\u00E9nt (d\u00E9l-\u00E1zsiai, kelet-\u00E1zsiai, fekete, vegyes, feh\u00E9r, egy\u00E9b) helyi \u00F6nkorm\u00E1nyzatonk\u00E9nt.', - dsCrimeName: 'Utcaszint\u0171 b\u0171n\u00F6z\u00E9si adatok', + dsEthnicityUse: 'Népesség százalékos megoszlása etnikai csoportonként (dél-ázsiai, kelet-ázsiai, fekete, vegyes, fehér, egyéb) helyi önkormányzatonként.', + dsCrimeName: 'Utcaszintű bűnözési adatok', dsCrimeOrigin: 'data.police.uk', - dsCrimeUse: 'Utcaszint\u0171 b\u0171n\u00F6z\u00E9si adatok 2023-t\u00F3l 2025-ig, \u00E9ves \u00E1tlagokba \u00F6sszeg\u00E9zve LSOA-nk\u00E9nt \u00E9s b\u0171ncselekm\u00E9nyt\u00EDpusonk\u00E9nt (er\u0151szak, bet\u00F6r\u00E9s, k\u00F6z\u00E9rdek\u0171 rends\u00E9rts\u00E9g, k\u00E1b\u00EDt\u00F3szer, j\u00E1rm\u0171b\u0171n\u00F6z\u00E9s stb.).', + dsCrimeUse: 'Utcaszintű bűnözési adatok 2023-tól 2025-ig, éves átlagokba összegézve LSOA-nként és bűncselekménytípusonként (erőszak, betörés, közérdekű rendsértség, kábítószer, járműbűnözés stb.).', dsOsmName: 'OpenStreetMap POI-k', dsOsmOrigin: 'OpenStreetMap contributors / Geofabrik', - dsOsmUse: '\u00C9rdekes pontok, bele\u00E9rtve \u00FCzleteket, \u00E9ttermeket, eg\u00E9szs\u00E9g\u00FCgyet, szabadid\u0151t, turizmust \u00E9s m\u00E9g sok m\u00E1st Nagy-Britanni\u00E1ban.', + dsOsmUse: 'Érdekes pontok, beleértve üzleteket, éttermeket, egészségügyet, szabadidőt, turizmust és még sok mást Nagy-Britanniában.', dsGreenspaceName: 'OS Open Greenspace', dsGreenspaceOrigin: 'Ordnance Survey', - dsGreenspaceUse: 'Hivatalos z\u00F6ldter\u00FCleti hat\u00E1rok Nagy-Britanni\u00E1ban, bele\u00E9rtve a k\u00F6zparkokat, kerteket, sportter\u00FCleteket \u00E9s j\u00E1tsz\u00F3tereket. A poligon k\u00F6z\u00E9ppontjait haszn\u00E1ljuk a park k\u00F6zels\u00E9gi sz\u00E1ml\u00E1l\u00E1shoz \u00E9s a legk\u00F6zelebbi park t\u00E1vols\u00E1g\u00E1nak sz\u00E1m\u00EDt\u00E1s\u00E1hoz.', - dsNaptanName: 'NaPTAN (T\u00F6megk\u00F6zleked\u00E9si meg\u00E1ll\u00F3k)', + dsGreenspaceUse: 'Hivatalos zöldterületi határok Nagy-Britanniában, beleértve a közparkokat, kerteket, sportterületeket és játszótereket. A poligon középpontjait használjuk a park közelségi számláláshoz és a legközelebbi park távolságának számításához.', + dsNaptanName: 'NaPTAN (Tömegközlekedési megállók)', dsNaptanOrigin: 'Department for Transport', - dsNaptanUse: '\u00C1llom\u00E1s- \u00E9s meg\u00E1ll\u00F3helyek vas\u00FAt, busz, metr\u00F3/villamos, komp \u00E9s rep\u00FCl\u0151t\u00E9r sz\u00E1m\u00E1ra Angli\u00E1ban.', - dsNoiseName: 'Defra zajt\u00E9rk\u00E9pez\u00E9s', + dsNaptanUse: 'Állomás- és megállóhelyek vasút, busz, metró/villamos, komp és repülőtér számára Angliában.', + dsNoiseName: 'Defra zajtérképezés', dsNoiseOrigin: 'Defra / Environment Agency', - dsNoiseUse: 'K\u00F6z\u00FAti zajszintek (24 \u00F3r\u00E1s s\u00FAlyozott \u00E1tlag) a 2022-es strat\u00E9giai zajt\u00E9rk\u00E9pez\u00E9sb\u0151l, nagy felbont\u00E1sban modellezve \u00E9s minden ir\u00E1ny\u00EDt\u00F3sz\u00E1mn\u00E1l mintav\u00E9telezve.', - dsOfstedName: 'Ofsted iskolai vizsg\u00E1latok', + dsNoiseUse: 'Közúti zajszintek (24 órás súlyozott átlag) a 2022-es stratégiai zajtérképezésből, nagy felbontásban modellezve és minden irányítószámnál mintavételezve.', + dsOfstedName: 'Ofsted iskolai vizsgálatok', dsOfstedOrigin: 'Ofsted', - dsOfstedUse: 'Legfrissebb vizsg\u00E1lati eredm\u00E9nyek az \u00E1llami fenntart\u00E1s\u00FA iskol\u00E1kr\u00F3l (2025 \u00E1prilis\u00E1ig). Ir\u00E1ny\u00EDt\u00F3sz\u00E1monk\u00E9nt \u00E1tlagolva a helyi iskolai min\u0151s\u00E9g pontoz\u00E1s\u00E1hoz (1=Kiv\u00E1l\u00F3-t\u00F3l 4=El\u00E9gtelenig).', - dsBroadbandName: 'Ofcom sz\u00E9less\u00E1v\u00FA teljes\u00EDtm\u00E9ny', + dsOfstedUse: 'Legfrissebb vizsgálati eredmények az állami fenntartású iskolákról (2025 áprilisáig). Irányítószámonként átlagolva a helyi iskolai minőség pontozásához (1=Kiváló-tól 4=Elégtelenig).', + dsBroadbandName: 'Ofcom szélessávú teljesítmény', dsBroadbandOrigin: 'Ofcom', - dsBroadbandUse: 'Vezet\u00E9kes sz\u00E9less\u00E1v\u00FA lefedetts\u00E9g \u00E9s maxim\u00E1lis let\u00F6lt\u00E9si sebess\u00E9gek ter\u00FCltenk\u00E9nt az Ofcom Connected Nations 2025 jelent\u00E9sb\u0151l.', - dsCouncilTaxName: 'Helyi ad\u00F3szintek 2025-26', + dsBroadbandUse: 'Vezetékes szélessávú lefedettség és maximális letöltési sebességek terültenként az Ofcom Connected Nations 2025 jelentésből.', + dsCouncilTaxName: 'Helyi adószintek 2025-26', dsCouncilTaxOrigin: 'Ministry of Housing, Communities & Local Government', - dsCouncilTaxUse: '\u00C9ves helyi ad\u00F3 d\u00EDjszab\u00E1sok A-H s\u00E1vokra Anglia mind a 296 sz\u00E1ml\u00E1z\u00F3 hat\u00F3s\u00E1g\u00E1n\u00E1l, k\u00E9t feln\u0151tt \u00E1ltal lakott ingatlanra. Az ingatlanokhoz a helyi \u00F6nkorm\u00E1nyzati ker\u00FCleti k\u00F3don kereszt\u00FCl csatolva az NSPL ir\u00E1ny\u00EDt\u00F3sz\u00E1m keres\u0151b\u0151l.', - dsRentalName: 'Mag\u00E1nb\u00E9rleti piaci statisztik\u00E1k', + dsCouncilTaxUse: 'Éves helyi adó díjszabások A-H sávokra Anglia mind a 296 számlázó hatóságánál, két felnőtt által lakott ingatlanra. Az ingatlanokhoz a helyi önkormányzati kerületi kódon keresztül csatolva az NSPL irányítószám keresőből.', + dsRentalName: 'Magánbérleti piaci statisztikák', dsRentalOrigin: 'ONS / Valuation Office Agency', - dsRentalUse: 'Medi\u00E1n havi mag\u00E1nb\u00E9rleti d\u00EDjak helyi \u00F6nkorm\u00E1nyzatonk\u00E9nt \u00E9s h\u00E1l\u00F3szoba-kateg\u00F3ri\u00E1nk\u00E9nt (2022. okt. \u2013 2023. szept.). Az ingatlanokhoz a helyi \u00F6nkorm\u00E1nyzati ker\u00FCleti k\u00F3don \u00E9s becs\u00FClt h\u00E1l\u00F3szobasz\u00E1mon kereszt\u00FCl csatolva.', + dsRentalUse: 'Medián havi magánbérleti díjak helyi önkormányzatonként és hálószoba-kategóriánként (2022. okt. – 2023. szept.). Az ingatlanokhoz a helyi önkormányzati kerületi kódon és becsült hálószobaszámon keresztül csatolva.', // FAQ section titles - faqFindingTitle: 'Ter\u00FCleted megtal\u00E1l\u00E1sa', - faqCommuteTitle: 'Ingaz\u00E1s \u00E9s utaz\u00E1s', - faqBudgetTitle: 'K\u00F6lts\u00E9gvet\u00E9s \u00E9s \u00E9rt\u00E9k', - faqSafetyTitle: 'Biztons\u00E1g \u00E9s szomsz\u00E9ds\u00E1g', - faqFamiliesTitle: 'Csal\u00E1dok \u00E9s iskol\u00E1k', - faqEnvironmentTitle: 'K\u00F6rnyezet \u00E9s \u00E9letmin\u0151s\u00E9g', - faqWhyTitle: 'Mi\u00E9rt a Perfect Postcode', - faqPricingTitle: '\u00C1rak \u00E9s hozz\u00E1f\u00E9r\u00E9s', - faqTipsTitle: 'Tippek \u00E9s tr\u00FCkk\u00F6k', + faqFindingTitle: 'Területed megtalálása', + faqCommuteTitle: 'Ingazás és utazás', + faqBudgetTitle: 'Költségvetés és érték', + faqSafetyTitle: 'Biztonság és szomszédság', + faqFamiliesTitle: 'Családok és iskolák', + faqEnvironmentTitle: 'Környezet és életminőség', + faqWhyTitle: 'Miért a Perfect Postcode', + faqPricingTitle: 'Árak és hozzáférés', + faqTipsTitle: 'Tippek és trükkök', // FAQ items — Finding Your Area - faqFinding1Q: 'Fogalmam sincs, hol keressek. Seg\u00EDt ebben?', - faqFinding1A: 'Pont erre val\u00F3. \u00C1ll\u00EDtsd be a sz\u0171r\u0151ket (k\u00F6lts\u00E9gvet\u00E9s, ingaz\u00E1si id\u0151, alacsony b\u0171n\u00F6z\u00E9s, j\u00F3 iskol\u00E1k), \u00E9s a t\u00E9rk\u00E9p kivilg\u00EDtja minden ter\u00FCletet, ami megfelel. Nem kell t\u00F6bb\u00E9 \u00E9jf\u00E9lkor guglizni, hogy \u201Chol a legjobb lakni Manchester k\u00F6zel\u00E9ben\u201D.', - faqFinding2Q: 'Olyan helyre k\u00F6lt\u00F6z\u00F6m, ahol m\u00E9g soha nem voltam. Hogyan kezdjem?', - faqFinding2A: '\u00C1ll\u00EDtsd be a sz\u0171r\u0151ket arra, ami fontos, \u00E9s a t\u00E9rk\u00E9p azonnal kiemeli a megfelel\u0151 ter\u00FCleteket. Az \u201Cegyetlen utc\u00E1t sem ismerek\u201D-b\u0151l percek alatt r\u00F6vid list\u00E1hoz jutsz.', - faqFinding3Q: 'Hogyan tal\u00E1lom meg azokat a ter\u00FCleteket, amelyek minden felt\u00E9telemnek megfelelnek?', - faqFinding3A: 'Kombin\u00E1lj t\u00F6bb sz\u0171r\u0151t (b\u0171n\u00F6z\u00E9s \u00E1tlag alatt, j\u00F3 iskol\u00E1k, ingaz\u00E1s 40 perc alatt), majd sz\u00EDnezd a t\u00E9rk\u00E9pet \u00E1r szerint a legjobb \u00E9rt\u00E9k\u0171 ter\u00FCletek megtal\u00E1l\u00E1s\u00E1hoz. A t\u00E9rk\u00E9p \u00E9l\u0151ben friss\u00FCl, ahogy a cs\u00FAsz\u00E1kat h\u00FAzod.', + faqFinding1Q: 'Fogalmam sincs, hol keressek. Segít ebben?', + faqFinding1A: 'Pont erre való. Állítsd be a szűrőket (költségvetés, ingazási idő, alacsony bűnözés, jó iskolák), és a térkép kivilgítja minden területet, ami megfelel. Nem kell többé éjfélkor guglizni, hogy “hol a legjobb lakni Manchester közelében”.', + faqFinding2Q: 'Olyan helyre költözöm, ahol még soha nem voltam. Hogyan kezdjem?', + faqFinding2A: 'Állítsd be a szűrőket arra, ami fontos, és a térkép azonnal kiemeli a megfelelő területeket. Az “egyetlen utcát sem ismerek”-ből percek alatt rövid listához jutsz.', + faqFinding3Q: 'Hogyan találom meg azokat a területeket, amelyek minden feltételemnek megfelelnek?', + faqFinding3A: 'Kombinálj több szűrőt (bűnözés átlag alatt, jó iskolák, ingazás 40 perc alatt), majd színezd a térképet ár szerint a legjobb értékű területek megtalálásához. A térkép élőben frissül, ahogy a csúszákat húzod.', // FAQ items — Commute and Travel - faqCommute1Q: 'L\u00E1thatom, mennyi lenne az ingaz\u00E1som k\u00FCl\u00F6nb\u00F6z\u0151 ter\u00FCletekr\u0151l?', - faqCommute1A: '\u00C1ll\u00EDtsd be a munkahelyed \u00FAtic\u00E9lk\u00E9nt, \u00E9s minden ir\u00E1ny\u00EDt\u00F3sz\u00E1mot kisz\u00EDnez\u00FCnk utaz\u00E1si id\u0151 szerint, legyen az aut\u00F3, ker\u00E9kp\u00E1r vagy t\u00F6megk\u00F6zleked\u00E9s. Sz\u0171rj a maxim\u00E1lis ingaz\u00E1si id\u0151re, \u00E9s a t\u00F6bbi elt\u0171nik.', - faqCommute2Q: 'Mi\u00E9rt jobb ez, mint a Google Maps?', - faqCommute2A: 'A Google Maps egyszerre egy utaz\u00E1st mutat. Mi Anglia \u00F6sszes ir\u00E1ny\u00EDt\u00F3sz\u00E1m\u00E1t kisz\u00EDnezz\u00FCk ingaz\u00E1si id\u0151 szerint egyszerre, \u00EDgy sz\u00E1zn\u00E1l t\u00F6bb ter\u00FCletet hasonl\u00EDthatsz \u00F6ssze egyetlen pillant\u00E1ssal, ahelyett hogy egyenk\u00E9nt keres-g\u00E9tn\u00E9d \u0151ket.', + faqCommute1Q: 'Láthatom, mennyi lenne az ingazásom különböző területekről?', + faqCommute1A: 'Állítsd be a munkahelyed úticélként, és minden irányítószámot kiszínezünk utazási idő szerint, legyen az autó, kerékpár vagy tömegközlekedés. Szűrj a maximális ingazási időre, és a többi eltűnik.', + faqCommute2Q: 'Miért jobb ez, mint a Google Maps?', + faqCommute2A: 'A Google Maps egyszerre egy utazást mutat. Mi Anglia összes irányítószámát kiszínezzük ingazási idő szerint egyszerre, így száznál több területet hasonlíthatsz össze egyetlen pillantással, ahelyett hogy egyenként keres-gétnéd őket.', // FAQ items — Budget and Value - faqBudget1Q: 'Hogyan tal\u00E1lom meg, hol kapom a legt\u00F6bb helyet a p\u00E9nzemem\u00E9rt?', - faqBudget1A: 'Sz\u0171rj n\u00E9gyzetm\u00E9ter\u00E1r szerint, \u00E9s azonnal l\u00E1tod, mely ir\u00E1ny\u00EDt\u00F3sz\u00E1mok adj\u00E1k a legt\u00F6bb helyet fontonk\u00E9nt. P\u00E1ros\u00EDtsd az energetikai min\u0151s\u00EDt\u00E9s sz\u0171r\u0151vel, hogy elker\u00FCld a magas f\u0171t\u00E9si k\u00F6lts\u00E9g\u0171 ingatlanokat.', - faqBudget2Q: 'Hogyan bizonyosodjak meg, hogy egy olcs\u00F3 ter\u00FClet nem ok n\u00E9lk\u00FCl olcs\u00F3?', - faqBudget2A: 'R\u00E9tegezd r\u00E1 a depriv\u00E1ci\u00F3s pontokat, b\u0171n\u00F6z\u00E9si statisztik\u00E1kat, iskolai min\u0151s\u00EDt\u00E9seket \u00E9s sz\u00E9less\u00E1v-sebess\u00E9geket az \u00E1r mell\u00E9. Ha egy ir\u00E1ny\u00EDt\u00F3sz\u00E1m megfizethet\u0151 \u00E9s minden fontos szempont szerint j\u00F3l teljes\u00EDt, val\u00F3di \u00E9rt\u00E9ket tal\u00E1lt\u00E1l, nem csak alacsony \u00E1rat \u00E9szrev\u00E9tlen kompromisszumokkal.', + faqBudget1Q: 'Hogyan találom meg, hol kapom a legtöbb helyet a pénzememért?', + faqBudget1A: 'Szűrj négyzetméterár szerint, és azonnal látod, mely irányítószámok adják a legtöbb helyet fontonként. Párosítsd az energetikai minősítés szűrővel, hogy elkerüld a magas fűtési költségű ingatlanokat.', + faqBudget2Q: 'Hogyan bizonyosodjak meg, hogy egy olcsó terület nem ok nélkül olcsó?', + faqBudget2A: 'Rétegezd rá a deprivációs pontokat, bűnözési statisztikákat, iskolai minősítéseket és szélessáv-sebességeket az ár mellé. Ha egy irányítószám megfizethető és minden fontos szempont szerint jól teljesít, valódi értéket találtál, nem csak alacsony árat észrevétlen kompromisszumokkal.', // FAQ items — Safety and Neighbourhood - faqSafety1Q: 'Hogyan ellen\u0151rizhetem, biztons\u00E1gos-e egy ter\u00FClet, miel\u0151tt odak\u00F6lt\u00F6z\u00F6m?', - faqSafety1A: 'Val\u00F3s rend\u0151rs\u00E9gi b\u0171n\u00F6z\u00E9si adatokat vetit\u00FCnk Anglia minden szomsz\u00E9ds\u00E1g\u00E1ra, t\u00EDpusonk\u00E9nt lebontva. Sz\u0171rj er\u0151szakos b\u0171ncselekm\u00E9nyre, bet\u00F6r\u00E9sre vagy k\u00F6z\u00E9rdek\u0171 rends\u00E9rts\u00E9gre, \u00E9s azonnal l\u00E1sd, mely ir\u00E1ny\u00EDt\u00F3sz\u00E1mok a legbiztosabbak.', - faqSafety2Q: 'Folyamatosan tal\u00E1lok rem\u00E9knek t\u0171n\u0151 lak\u00E1sokat online, de a k\u00F6rnyezet rossz.', - faqSafety2A: 'Pont ez\u00E9rt k\u00E9sz\u00FClt ez. R\u00E9tegezd a b\u0171n\u00F6z\u00E9si ar\u00E1nyokat, zajszinteket, depriv\u00E1ci\u00F3s pontokat, k\u00F6zeli kocsmkat \u00E9s parkokat, valamint a sz\u00E9less\u00E1v-sebess\u00E9geket egyetlen t\u00E9rk\u00E9pre, \u00EDgy tudhatod, milyen val\u00F3j\u00E1ban egy szomsz\u00E9ds\u00E1g, miel\u0151tt megtekint\u00E9st foglalsz.', + faqSafety1Q: 'Hogyan ellenőrizhetem, biztonságos-e egy terület, mielőtt odaköltözöm?', + faqSafety1A: 'Valós rendőrségi bűnözési adatokat vetitünk Anglia minden szomszédságára, típusonként lebontva. Szűrj erőszakos bűncselekményre, betörésre vagy közérdekű rendsértségre, és azonnal lásd, mely irányítószámok a legbiztosabbak.', + faqSafety2Q: 'Folyamatosan találok reméknek tűnő lakásokat online, de a környezet rossz.', + faqSafety2A: 'Pont ezért készült ez. Rétegezd a bűnözési arányokat, zajszinteket, deprivációs pontokat, közeli kocsmkat és parkokat, valamint a szélessáv-sebességeket egyetlen térképre, így tudhatod, milyen valójában egy szomszédság, mielőtt megtekintést foglalsz.', // FAQ items — Families and Schools - faqFamilies1Q: 'Tal\u00E1lhatok ter\u00FCleteket j\u00F3 iskol\u00E1kkal \u00C9S alacsony b\u0171n\u00F6z\u00E9ssel egyetlen keres\u00E9ssel?', - faqFamilies1A: 'Igen. Kombin\u00E1ld az Ofsted min\u0151s\u00EDt\u00E9sek, b\u0171n\u00F6z\u00E9si ar\u00E1nyok, parkok \u00E9s b\u00E1rmi m\u00E1s, a csal\u00E1dod sz\u00E1m\u00E1ra fontos szempont sz\u0171r\u0151it, \u00E9s a t\u00E9rk\u00E9p csak a minden felt\u00E9telnek megfelel\u0151 ter\u00FCleteket emeli ki. Nem kell t\u00F6bb\u00E9 \u00F6t k\u00FCl\u00F6nb\u00F6z\u0151 weboldalt \u00F6sszevetni.', - faqFamilies2Q: 'Hogyan tudhatom meg, van-e park \u00E9s j\u00E1tsz\u00F3t\u00E9r a k\u00F6zelben?', - faqFamilies2A: 'Kapcsold be a parkok \u00E9s z\u00F6ldter\u00FCletek POI r\u00E9teget, hogy k\u00F6zvetlen\u00FCl a t\u00E9rk\u00E9pen l\u00E1sd \u0151ket. Sz\u0171rhetsz aszerint is, h\u00E1ny van s\u00E9tat\u00E1vols\u00E1gon bel\u00FCl az egyes ir\u00E1ny\u00EDt\u00F3sz\u00E1mokt\u00F3l.', + faqFamilies1Q: 'Találhatok területeket jó iskolákkal ÉS alacsony bűnözéssel egyetlen kereséssel?', + faqFamilies1A: 'Igen. Kombináld az Ofsted minősítések, bűnözési arányok, parkok és bármi más, a családod számára fontos szempont szűrőit, és a térkép csak a minden feltételnek megfelelő területeket emeli ki. Nem kell többé öt különböző weboldalt összevetni.', + faqFamilies2Q: 'Hogyan tudhatom meg, van-e park és játszótér a közelben?', + faqFamilies2A: 'Kapcsold be a parkok és zöldterületek POI réteget, hogy közvetlenül a térképen lásd őket. Szűrhetsz aszerint is, hány van sétatávolságon belül az egyes irányítószámoktól.', // FAQ items — Environment and Quality of Life - faqEnv1Q: 'Tal\u00E1lhatok energiahat\u00E9kony otthonokat, amelyek nincsenek zajos \u00FAton?', - faqEnv1A: 'Sz\u0171rj EPC min\u0151s\u00EDt\u00E9s szerint (A-C), majd r\u00E9tegezd r\u00E1 a k\u00F6z\u00FAti zajadatokat, hogy kisz\u0171rd a k\u00FCsz\u00F6b\u00E9rt\u00E9ked feletti ter\u00FCleteket. Sz\u00EDnezd b\u00E1rmelyik jellemz\u0151 szerint, hogy egy pillant\u00E1ssal \u00E9szrevedd a csendes, hat\u00E9kony utc\u00E1kat.', - faqEnv2Q: 'Mutatja az \u00E1rv\u00EDz- vagy s\u00FCllyedeskock\u00E1zatot?', - faqEnv2A: 'Tartalmazunk talajstabilit\u00E1si adatokat, \u00EDgy ellen\u0151rizheted a s\u00FCllyeed\u00E9st, agyagtalan zsugorod\u00E1s-duzzad\u00E1st \u00E9s egy\u00E9b geol\u00F3giai vesz\u00E9lyeket, miel\u0151tt elk\u00F6telezn\u00E9d magad egy ingatlan mellett. Sz\u0171rd ki a kock\u00E1zatos ter\u00FCleteket kor\u00E1n.', - faqEnv3Q: 'Tal\u00E1lhatok ter\u00FCleteket gyors internettel, amelyek t\u00E9nyleg csendesek?', - faqEnv3A: 'R\u00E9tegezd a sz\u00E9less\u00E1v-sebess\u00E9g sz\u0171r\u0151t a k\u00F6z\u00FAti zajadatokkal, hogy megtal\u00E1ld a kit\u0171n\u0151 kapcsolattal \u00E9s alacsony forgalmi zajjal rendelkez\u0151 utc\u00E1kat. Sz\u00EDnezd b\u00E1rmelyik m\u00E9r\u0151sz\u00E1m szerint a ter\u00FCletek \u00F6sszehasonl\u00EDt\u00E1s\u00E1hoz.', + faqEnv1Q: 'Találhatok energiahatékony otthonokat, amelyek nincsenek zajos úton?', + faqEnv1A: 'Szűrj EPC minősítés szerint (A-C), majd rétegezd rá a közúti zajadatokat, hogy kiszűrd a küszöbértéked feletti területeket. Színezd bármelyik jellemző szerint, hogy egy pillantással észrevedd a csendes, hatékony utcákat.', + faqEnv2Q: 'Mutatja az árvíz- vagy süllyedeskockázatot?', + faqEnv2A: 'Tartalmazunk talajstabilitási adatokat, így ellenőrizheted a süllyeedést, agyagtalan zsugorodás-duzzadást és egyéb geológiai veszélyeket, mielőtt elköteleznéd magad egy ingatlan mellett. Szűrd ki a kockázatos területeket korán.', + faqEnv3Q: 'Találhatok területeket gyors internettel, amelyek tényleg csendesek?', + faqEnv3A: 'Rétegezd a szélessáv-sebesség szűrőt a közúti zajadatokkal, hogy megtaláld a kitűnő kapcsolattal és alacsony forgalmi zajjal rendelkező utcákat. Színezd bármelyik mérőszám szerint a területek összehasonlításához.', // FAQ items — Why Perfect Postcode - faqWhy1Q: 'M\u00E1r haszn\u00E1lom a Rightmove-ot. Mit ad ez hozz\u00E1?', - faqWhy1A: 'A Rightmove h\u00E1zakat mutat. Mi ter\u00FCleteket. B\u0171n\u00F6z\u00E9si ar\u00E1nyok, iskolai min\u0151s\u00EDt\u00E9sek, sz\u00E9less\u00E1v-sebess\u00E9gek, zajszintek, depriv\u00E1ci\u00F3s pontok \u00E9s m\u00E9g sok m\u00E1s, minden sz\u0171rhet\u0151 egyetlen t\u00E9rk\u00E9pen. M\u00E9g azel\u0151tt meg\u00EDt\u00E9lheted a szomsz\u00E9ds\u00E1got, hogy akad hirdet\u00E9sekre n\u00E9zn\u00E9l.', - faqWhy2Q: 'Nem tudom mindezt ingyen is ut\u00E1nan\u00E9zni?', - faqWhy2A: '\u00D6sszevethatn\u00E9d a rend\u0151rs\u00E9gi adatokat, Ofsted jelent\u00E9seket, EPC nyilv\u00E1ntart\u00E1st, Land Registry adatokat \u00E9s ONS statisztik\u00E1kat egyenk\u00E9nt, ir\u00E1ny\u00EDt\u00F3sz\u00E1monk\u00E9nt. Vagy mindezt sz\u0171rhet\u0151en \u00E9s sz\u00EDnk\u00F3doltan egyetlen t\u00E9rk\u00E9pen, m\u00E1sodpercek alatt.', - faqWhy3Q: 'Honnan sz\u00E1rmaznak az adatok?', - faqWhy3A: 'Minden adatk\u00E9szlet hivatalos brit korm\u00E1nyzati forr\u00E1sokb\u00F3l sz\u00E1rmazik: Land Registry, EPC nyilv\u00E1ntart\u00E1s, ONS, Ofsted, Ofcom, data.police.uk \u00E9s Defra. Nem scrapel\u00FCnk ingatlanirrod\u00E1kat \u00E9s nem tal\u00E1lunk ki semmit. B\u00E1rmely rekordot ellen\u0151rizheted az eredeti forr\u00E1sban.', + faqWhy1Q: 'Már használom a Rightmove-ot. Mit ad ez hozzá?', + faqWhy1A: 'A Rightmove házakat mutat. Mi területeket. Bűnözési arányok, iskolai minősítések, szélessáv-sebességek, zajszintek, deprivációs pontok és még sok más, minden szűrhető egyetlen térképen. Még azelőtt megítélheted a szomszédságot, hogy akad hirdetésekre néznél.', + faqWhy2Q: 'Nem tudom mindezt ingyen is utánanézni?', + faqWhy2A: 'Összevethatnéd a rendőrségi adatokat, Ofsted jelentéseket, EPC nyilvántartást, Land Registry adatokat és ONS statisztikákat egyenként, irányítószámonként. Vagy mindezt szűrhetően és színkódoltan egyetlen térképen, másodpercek alatt.', + faqWhy3Q: 'Honnan származnak az adatok?', + faqWhy3A: 'Minden adatkészlet hivatalos brit kormányzati forrásokból származik: Land Registry, EPC nyilvántartás, ONS, Ofsted, Ofcom, data.police.uk és Defra. Nem scrapelünk ingatlanirrodákat és nem találunk ki semmit. Bármely rekordot ellenőrizheted az eredeti forrásban.', // FAQ items — Pricing and Access - faqPricing1Q: 'T\u00E9nyleg meg\u00E9ri fizetni egy ingatlan-keres\u0151 eszk\u00F6z\u00E9rt?', - faqPricing1A: 'Egy lak\u00E1sv\u00E1s\u00E1rl\u00E1s val\u00F3sz\u00EDn\u0171leg a legnagyobb v\u00E1s\u00E1rl\u00E1sod lesz. Egyetlen figyelmeztet\u0151 jel felismer\u00E9se (zajos \u00FAt, gyenge internet, n\u00F6vekv\u0151 b\u0171n\u00F6z\u00E9s) elk\u00F6telez\u0151d\u00E9s el\u0151tt \u00E9vekn\u0171i megb\u00E1n\u00E1st takar\u00EDthat meg. Ez kevesebbe ker\u00FCl, mint egy tank benzin.', - faqPricing2Q: 'Ez el\u0151fizet\u00E9s?', - faqPricing2A: 'Nem. Egyszeri fizet\u00E9s, \u00F6r\u00F6kre a tied. Haszn\u00E1ld intenz\u00EDven a keres\u00E9s sor\u00E1n, gyere vissza b\u00E1rmikor, ha k\u00EDv\u00E1ncsi vagy egy \u00FAj ter\u00FCletre, \u00E9s m\u00E9g mindig ott van, ha \u00FAjra k\u00F6lt\u00F6z\u00F6l.', - faqPricing3Q: 'Mit \u00E9rhetek el az ingyenes szinten?', - faqPricing3A: 'Az ingyenes felhaszn\u00E1l\u00F3k a dem\u00F3 ter\u00FCleten (Bels\u0151-London, megk\u00F6zel\u00EDt\u0151leg az 1-2. z\u00F3na) fedezhetik fel az \u00F6sszes funkci\u00F3t. Anglia t\u00F6bbi r\u00E9sz\u00E9nek adataihoz \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9s sz\u00FCks\u00E9ges.', - faqPricing4Q: 'K\u00E9rhetek visszat\u00E9r\u00EDt\u00E9st?', - faqPricing4A: 'Term\u00E9szetesen. 30 napos p\u00E9nzvisszat\u00E9r\u00EDt\u00E9si garanci\u00E1t k\u00EDn\u00E1lunk. Ha nem vagy el\u00E9gedett, \u00EDrj a support@perfect-postcode.co.uk c\u00EDmre 30 napon bel\u00FCl a teljes visszat\u00E9r\u00EDt\u00E9s\u00E9rt.', + faqPricing1Q: 'Tényleg megéri fizetni egy ingatlan-kereső eszközért?', + faqPricing1A: 'Egy lakásvásárlás valószínűleg a legnagyobb vásárlásod lesz. Egyetlen figyelmeztető jel felismerése (zajos út, gyenge internet, növekvő bűnözés) elköteleződés előtt éveknűi megbánást takaríthat meg. Ez kevesebbe kerül, mint egy tank benzin.', + faqPricing2Q: 'Ez előfizetés?', + faqPricing2A: 'Nem. Egyszeri fizetés, örökre a tied. Használd intenzíven a keresés során, gyere vissza bármikor, ha kíváncsi vagy egy új területre, és még mindig ott van, ha újra költözöl.', + faqPricing3Q: 'Mit érhetek el az ingyenes szinten?', + faqPricing3A: 'Az ingyenes felhasználók a demó területen (Belső-London, megközelítőleg az 1-2. zóna) fedezhetik fel az összes funkciót. Anglia többi részének adataihoz élethosszig tartó hozzáférés szükséges.', + faqPricing4Q: 'Kérhetek visszatérítést?', + faqPricing4A: 'Természetesen. 30 napos pénzvisszatérítési garanciát kínálunk. Ha nem vagy elégedett, írj a support@perfect-postcode.co.uk címre 30 napon belül a teljes visszatérítésért.', // FAQ items — Tips and Tricks - faqTips1Q: 'Hogyan haszn\u00E1ljam az AI sz\u0171r\u0151t a sz\u0171r\u0151k egyenk\u00E9nti hozz\u00E1ad\u00E1sa helyett?', - faqTips1A: '\u00CDrd le egyszer\u0171 angolul, mit szeretn\u00E9l, p\u00E9ld\u00E1ul \u201Ccsendes ter\u00FClet j\u00F3 iskol\u00E1k k\u00F6zel\u00E9ben, gyors internettel, \u00A3400e alatt\u201D, \u00E9s az \u00F6sszes megfelel\u0151 sz\u0171r\u0151t egyszerre be\u00E1ll\u00EDtja. Ut\u00E1na b\u00E1rmelyiket k\u00E9zzel finomhangolhatod.', - faqTips2Q: 'Elmenthetem a keres\u00E9st, \u00E9s k\u00E9s\u0151bb visszat\u00E9rhetek hozz\u00E1?', - faqTips2A: 'Nyomd meg a ment\u00E9s gombot, \u00E9s mindent r\u00F6gz\u00EDt\u00FCnk: sz\u0171r\u0151id, a nagy\u00EDt\u00E1si szint, \u00E9s melyik adatr\u00E9teg szerint sz\u00EDnezel. Folytasd pontosan ott, ahol abbahagytad, vagy oszd meg a linket a p\u00E1roddal.', - faqTips3Q: 'Export\u00E1lhatom az adatokat, amiket l\u00E1tok?', - faqTips3A: 'Az export\u00E1l\u00E1s gombbal let\u00F6ltheted a jelenlegi sz\u0171r\u0151knek megfelel\u0151 ingatlanokat t\u00E1bl\u00E1zatk\u00E9nt. Az export figyelembe veszi az \u00F6sszes akt\u00EDv sz\u0171r\u0151t, \u00EDgy pontosan azokat az adatokat kapod, amiket szeretn\u00E9l.', + faqTips1Q: 'Hogyan használjam az AI szűrőt a szűrők egyenkénti hozzáadása helyett?', + faqTips1A: 'Írd le egyszerű angolul, mit szeretnél, például “csendes terület jó iskolák közelében, gyors internettel, £400e alatt”, és az összes megfelelő szűrőt egyszerre beállítja. Utána bármelyiket kézzel finomhangolhatod.', + faqTips2Q: 'Elmenthetem a keresést, és később visszatérhetek hozzá?', + faqTips2A: 'Nyomd meg a mentés gombot, és mindent rögzítünk: szűrőid, a nagyítási szint, és melyik adatréteg szerint színezel. Folytasd pontosan ott, ahol abbahagytad, vagy oszd meg a linket a pároddal.', + faqTips3Q: 'Exportálhatom az adatokat, amiket látok?', + faqTips3A: 'Az exportálás gombbal letöltheted a jelenlegi szűrőknek megfelelő ingatlanokat táblázatként. Az export figyelembe veszi az összes aktív szűrőt, így pontosan azokat az adatokat kapod, amiket szeretnél.', }, // ── Account Page ─────────────────────────────────── accountPage: { emailLabel: 'E-mail', - subscriptionLabel: 'El\u0151fizet\u00E9s', - upgrade: 'Friss\u00EDt\u00E9s', - redirecting: '\u00C1tir\u00E1ny\u00EDt\u00E1s\u2026', - receiveNewsletter: 'H\u00EDrlev\u00E9l fogad\u00E1sa', - needHelp: 'Seg\u00EDts\u00E9gre van sz\u00FCks\u00E9ged? \u00CDrj nek\u00FCnk:', - responseTime: '\u00C1ltal\u00E1ban 24 \u00F3r\u00E1n bel\u00FCl v\u00E1laszolunk.', + subscriptionLabel: 'Előfizetés', + upgrade: 'Frissítés', + redirecting: 'Átirányítás…', + receiveNewsletter: 'Hírlevél fogadása', + needHelp: 'Segítségre van szükséged? Írj nekünk:', + responseTime: 'Általában 24 órán belül válaszolunk.', }, // ── Saved Page ───────────────────────────────────── savedPage: { - searches: 'Keres\u00E9sek', - noSavedSearches: 'M\u00E9g nincsenek mentett keres\u00E9sek', - noSavedSearchesDesc: 'Mentsd el a sz\u0171r\u0151ket \u00E9s a t\u00E9rk\u00E9pn\u00E9zetet, hogy pontosan ott folytasd, ahol abbahagytad.', - noSavedProperties: 'M\u00E9g nincsenek mentett ingatlanok', - noSavedPropertiesDesc: 'Jel\u00F6ld meg az ingatlanokat felfedez\u00E9s k\u00F6zben, \u00E9s \u00E9p\u00EDtsd a r\u00F6vid list\u00E1dat elveszt\u00E9s n\u00E9lk\u00FCl.', - openPostcode: 'Ir\u00E1ny\u00EDt\u00F3sz\u00E1m megnyit\u00E1sa', - viewListing: 'Hirdet\u00E9s megtekint\u00E9se', - clickToRename: 'Kattints az \u00E1tnevez\u00E9shez', - notesPlaceholder: '\u00CDrd le a gondolataidat...', - deleteSearch: 'Keres\u00E9s t\u00F6rl\u00E9se', - deleteSearchConfirm: 'Biztosan t\u00F6r\u00F6lni szeretn\u00E9d ezt a mentett keres\u00E9st? Ez nem vonhat\u00F3 vissza.', - deleteProperty: 'Ingatlan t\u00F6rl\u00E9se', - deletePropertyConfirm: 'Biztosan t\u00F6r\u00F6lni szeretn\u00E9d ezt a mentett ingatlant? Ez nem vonhat\u00F3 vissza.', - bed: 'h\u00E1l\u00F3', + searches: 'Keresések', + noSavedSearches: 'Még nincsenek mentett keresések', + noSavedSearchesDesc: 'Mentsd el a szűrőket és a térképnézetet, hogy pontosan ott folytasd, ahol abbahagytad.', + noSavedProperties: 'Még nincsenek mentett ingatlanok', + noSavedPropertiesDesc: 'Jelöld meg az ingatlanokat felfedezés közben, és építsd a rövid listádat elvesztés nélkül.', + openPostcode: 'Irányítószám megnyitása', + viewListing: 'Hirdetés megtekintése', + clickToRename: 'Kattints az átnevezéshez', + notesPlaceholder: 'Írd le a gondolataidat...', + deleteSearch: 'Keresés törlése', + deleteSearchConfirm: 'Biztosan törölni szeretnéd ezt a mentett keresést? Ez nem vonható vissza.', + deleteProperty: 'Ingatlan törlése', + deletePropertyConfirm: 'Biztosan törölni szeretnéd ezt a mentett ingatlant? Ez nem vonható vissza.', + bed: 'háló', epc: 'EPC', }, // ── Invites Page ─────────────────────────────────── invitesPage: { - inviteLinksLicensed: 'A megh\u00EDv\u00F3 linkek a licencelt felhaszn\u00E1l\u00F3k sz\u00E1m\u00E1ra \u00E9rhet\u0151k el.', - inviteAdminLabel: 'Bar\u00E1tok megh\u00EDv\u00E1sa (100% kedvezm\u00E9ny)', - inviteReferralLabel: 'Bar\u00E1tok megh\u00EDv\u00E1sa (30% kedvezm\u00E9ny)', - generateFreeInvite: 'Ingyenes megh\u00EDv\u00F3 link l\u00E9trehoz\u00E1sa', - generateReferralLink: 'Aj\u00E1nl\u00F3 link l\u00E9trehoz\u00E1sa', - copyInviteLink: 'Megh\u00EDv\u00F3 link m\u00E1sol\u00E1sa', - adminInvitesTitle: 'Adminisztr\u00E1tori megh\u00EDv\u00F3k (100% kedvezm\u00E9ny)', - referralInvitesTitle: 'Aj\u00E1nl\u00F3 megh\u00EDv\u00F3k (30% kedvezm\u00E9ny)', - yourInviteLinks: 'Megh\u00EDv\u00F3 linkjeid', - noInvitesYet: 'M\u00E9g nincsenek l\u00E9trehozott megh\u00EDv\u00F3k', + inviteLinksLicensed: 'A meghívó linkek a licencelt felhasználók számára érhetők el.', + inviteAdminLabel: 'Barátok meghívása (100% kedvezmény)', + inviteReferralLabel: 'Barátok meghívása (30% kedvezmény)', + generateFreeInvite: 'Ingyenes meghívó link létrehozása', + generateReferralLink: 'Ajánló link létrehozása', + copyInviteLink: 'Meghívó link másolása', + adminInvitesTitle: 'Adminisztrátori meghívók (100% kedvezmény)', + referralInvitesTitle: 'Ajánló meghívók (30% kedvezmény)', + yourInviteLinks: 'Meghívó linkjeid', + noInvitesYet: 'Még nincsenek létrehozott meghívók', link: 'Link', - status: '\u00C1llapot', - created: 'L\u00E9trehozva', - redeemed: 'Bev\u00E1ltva', - pending: 'F\u00FCgg\u0151ben', + status: 'Állapot', + created: 'Létrehozva', + redeemed: 'Beváltva', + pending: 'Függőben', }, // ── Invite Page ──────────────────────────────────── invitePage: { - youreInvited: 'Megh\u00EDv\u00E1st kapt\u00E1l!', - specialOffer: 'K\u00FCl\u00F6nleges aj\u00E1nlat!', - invitedByFree: '{{name}} megh\u00EDvott, hogy ingyenes \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9st kapj.', - invitedByDiscount: '{{name}} megoszt veled egy 30%-os kedvezm\u00E9nyt az \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9sre.', - genericFreeInvite: 'Megh\u00EDv\u00E1st kapt\u00E1l ingyenes \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9sre.', - genericDiscount: 'Egy bar\u00E1t megoszt veled egy 30%-os kedvezm\u00E9nyt az \u00E9lethosszig tart\u00F3 hozz\u00E1f\u00E9r\u00E9sre.', - exploreEvery: 'Fedezd fel Anglia minden szomsz\u00E9ds\u00E1g\u00E1t', - propertyInfo: 'Ingatlan\u00E1rak, energetikai min\u0151s\u00EDt\u00E9sek, b\u0171n\u00F6z\u00E9si adatok, iskolai min\u0151s\u00EDt\u00E9sek \u00E9s m\u00E9g sok m\u00E1s', - invalidInvite: '\u00C9rv\u00E9nytelen megh\u00EDv\u00F3', - inviteAlreadyUsed: 'A megh\u00EDv\u00F3 m\u00E1r felhaszn\u00E1lva', - inviteAlreadyUsedDesc: 'Ez a megh\u00EDv\u00F3 link m\u00E1r be lett v\u00E1ltva.', - invalidInviteLink: '\u00C9rv\u00E9nytelen megh\u00EDv\u00F3 link', - invalidInviteLinkDesc: 'Ez a megh\u00EDv\u00F3 link \u00E9rv\u00E9nytelen vagy lej\u00E1rt.', - licenseActivated: 'Licenc aktiv\u00E1lva!', - fullAccessGranted: 'Most m\u00E1r teljes hozz\u00E1f\u00E9r\u00E9sed van a Perfect Postcode-hoz.', - activating: 'Aktiv\u00E1l\u00E1s...', - activateLicense: 'Licenc aktiv\u00E1l\u00E1sa', - claimDiscount: 'Kedvezm\u00E9ny ig\u00E9nyl\u00E9se', - registerToClaim: 'Regisztr\u00E1lj az ig\u00E9nyl\u00E9shez', - youAlreadyHaveLicense: 'M\u00E1r van licenced', - accountHasFullAccess: 'A fi\u00F3kod m\u00E1r teljes hozz\u00E1f\u00E9r\u00E9ssel rendelkezik.', - failedToValidate: 'Nem siker\u00FClt a megh\u00EDv\u00F3 link \u00E9rv\u00E9nyes\u00EDt\u00E9se', + youreInvited: 'Meghívást kaptál!', + specialOffer: 'Különleges ajánlat!', + invitedByFree: '{{name}} meghívott, hogy ingyenes élethosszig tartó hozzáférést kapj.', + invitedByDiscount: '{{name}} megoszt veled egy 30%-os kedvezményt az élethosszig tartó hozzáférésre.', + genericFreeInvite: 'Meghívást kaptál ingyenes élethosszig tartó hozzáférésre.', + genericDiscount: 'Egy barát megoszt veled egy 30%-os kedvezményt az élethosszig tartó hozzáférésre.', + exploreEvery: 'Fedezd fel Anglia minden szomszédságát', + propertyInfo: 'Ingatlanárak, energetikai minősítések, bűnözési adatok, iskolai minősítések és még sok más', + invalidInvite: 'Érvénytelen meghívó', + inviteAlreadyUsed: 'A meghívó már felhasználva', + inviteAlreadyUsedDesc: 'Ez a meghívó link már be lett váltva.', + invalidInviteLink: 'Érvénytelen meghívó link', + invalidInviteLinkDesc: 'Ez a meghívó link érvénytelen vagy lejárt.', + licenseActivated: 'Licenc aktiválva!', + fullAccessGranted: 'Most már teljes hozzáférésed van a Perfect Postcode-hoz.', + activating: 'Aktiválás...', + activateLicense: 'Licenc aktiválása', + claimDiscount: 'Kedvezmény igénylése', + registerToClaim: 'Regisztrálj az igényléshez', + youAlreadyHaveLicense: 'Már van licenced', + accountHasFullAccess: 'A fiókod már teljes hozzáféréssel rendelkezik.', + failedToValidate: 'Nem sikerült a meghívó link érvényesítése', }, // ── Map Page ─────────────────────────────────────── mapPage: { - unsavedProperty: 'Elt\u00E1vol\u00EDt\u00E1s', + unsavedProperty: 'Eltávolítás', savedProperty: 'Mentve', }, // ── Format / Time ────────────────────────────────── format: { - justNow: 'az im\u00E9nt', + justNow: 'az imént', minutesAgo: '{{count}} perce', - hoursAgo: '{{count}} \u00F3r\u00E1ja', + hoursAgo: '{{count}} órája', daysAgo: '{{count}} napja', - nFilters: '{{count}} sz\u0171r\u0151', - noFilters: 'Nincs sz\u0171r\u0151', - poiCategory: '{{count}} POI-kateg\u00F3ria', - poiCategories: '{{count}} POI-kateg\u00F3ria', - travelDestination: '{{count}} utaz\u00E1si c\u00E9l', - travelDestinations: '{{count}} utaz\u00E1si c\u00E9l', + nFilters: '{{count}} szűrő', + noFilters: 'Nincs szűrő', + poiCategory: '{{count}} POI-kategória', + poiCategories: '{{count}} POI-kategória', + travelDestination: '{{count}} utazási cél', + travelDestinations: '{{count}} utazási cél', propertiesMatch: '{{count}} ingatlan megfelel', - setFilters: '{{count}} sz\u0171r\u0151 be\u00E1ll\u00EDt\u00E1sa: {{list}}', - noFiltersSet: 'Nincs sz\u0171r\u0151 be\u00E1ll\u00EDtva', + setFilters: '{{count}} szűrő beállítása: {{list}}', + noFiltersSet: 'Nincs szűrő beállítva', toDestination: '{{mode}} ide: {{label}} {{bounds}}', lessThanMin: '< {{max}} perc', moreThanMin: '> {{min}} perc', @@ -613,18 +623,18 @@ const hu: Translations = { // ── Tutorial ────────────────────────────────────── tutorial: { - step1Title: 'Mondja el a t\u00E9rk\u00E9pnek, mi fontos', - step1Content: '\u00C1ll\u00EDtsa be a k\u00F6lts\u00E9gvet\u00E9st, maximalis ingaz\u00E1si id\u0151t, iskola min\u0151s\u00E9get \u00E9s b\u0171n\u00F6z\u00E9si k\u00FAsz\u00F6b\u00F6t. Ami \u00D6nnek fontos. Csak a megfelel\u0151 ter\u00FCletek maradnak kiemelve. Haszn\u00E1lja a szem ikont b\u00E1rmely jellemz\u0151 szerinti sz\u00EDnez\u00E9shez.', - step2Title: 'Vagy egyszer\u0171en \u00EDrja le', - step2Content: '\u00CDrja le magyarul, mit keres, p\u00E9ld\u00E1ul \u201Ecsendes ter\u00FClet j\u00F3 iskol\u00E1k k\u00F6zel\u00E9ben \u00A3400k alatt\u201D, \u00E9s be\u00E1ll\u00EDtjuk a sz\u0171r\u0151ket \u00D6nnek.', + step1Title: 'Mondja el a térképnek, mi fontos', + step1Content: 'Állítsa be a költségvetést, maximalis ingazási időt, iskola minőséget és bűnözési kúszöböt. Ami Önnek fontos. Csak a megfelelő területek maradnak kiemelve. Használja a szem ikont bármely jellemző szerinti színezéshez.', + step2Title: 'Vagy egyszerűen írja le', + step2Content: 'Írja le magyarul, mit keres, például „csendes terület jó iskolák közelében £400k alatt”, és beállítjuk a szűrőket Önnek.', step3Title: 'Fedezze fel, mi van odakint', - step3Content: 'G\u00F6rgessen \u00E9s nagy\u00EDtson Anglia-szerte. Kattintson b\u00E1rmely sz\u00EDnes ter\u00FCletre a b\u0171n\u00F6z\u00E9s, iskol\u00E1k, \u00E1rak, sz\u00E9less\u00E1v, zaj \u00E9s egy\u00E9b adatok megtekint\u00E9s\u00E9hez.', - step4Title: 'Ugr\u00E1s egy helyre', - step4Content: 'Keressen r\u00E1 b\u00E1rmely helyre vagy ir\u00E1ny\u00EDt\u00F3sz\u00E1mra, hogy azonnal odajusson.', - step5Title: 'Mer\u00FClj\u00F6n el a r\u00E9szletekben', - step5Content: 'Tekintse meg a ter\u00FCleti statisztik\u00E1kat, hisztogramokat \u00E9s az egyes ingatlanadatokat: \u00E1rak, alapter\u00FClet, energetikai besorol\u00E1s \u00E9s t\u00F6bb.', - step6Title: 'Mi van a k\u00F6zelben?', - step6Content: 'Kapcsolja be az iskol\u00E1kat, \u00FCzleteket, \u00E1llom\u00E1sokat, parkokat \u00E9s \u00E9ttermeket a t\u00E9rk\u00E9pen, hogy l\u00E1ssa, mi \u00E9rhet\u0151 el.', + step3Content: 'Görgessen és nagyítson Anglia-szerte. Kattintson bármely színes területre a bűnözés, iskolák, árak, szélessáv, zaj és egyéb adatok megtekintéséhez.', + step4Title: 'Ugrás egy helyre', + step4Content: 'Keressen rá bármely helyre vagy irányítószámra, hogy azonnal odajusson.', + step5Title: 'Merüljön el a részletekben', + step5Content: 'Tekintse meg a területi statisztikákat, hisztogramokat és az egyes ingatlanadatokat: árak, alapterület, energetikai besorolás és több.', + step6Title: 'Mi van a közelben?', + step6Content: 'Kapcsolja be az iskolákat, üzleteket, állomásokat, parkokat és éttermeket a térképen, hogy lássa, mi érhető el.', }, // ── Server-derived values ────────────────────────── @@ -633,206 +643,205 @@ const hu: Translations = { server: { // ─ Feature group names ─ 'Properties': 'Ingatlanok', - 'Transport': 'K\u00F6zleked\u00E9s', - 'Education': 'Oktat\u00E1s', - 'Deprivation': 'Depriv\u00E1ci\u00F3', - 'Crime': 'B\u0171n\u00F6z\u00E9s', - 'Demographics': 'Demogr\u00E1fia', - 'Amenities': 'Szolg\u00E1ltat\u00E1sok', + 'Transport': 'Közlekedés', + 'Education': 'Oktatás', + 'Deprivation': 'Depriváció', + 'Crime': 'Bűnözés', + 'Demographics': 'Demográfia', + 'Amenities': 'Szolgáltatások', // ─ Feature names (Properties) ─ - 'Listing status': 'Hirdet\u00E9s \u00E1llapota', - 'Property type': 'Ingatlant\u00EDpus', - 'Leasehold/Freehold': 'B\u00E9rleti/Tulajdonjog', - 'Last known price': 'Utols\u00F3 ismert \u00E1r', - 'Estimated current price': 'Becs\u00FClt jelenlegi \u00E1r', - 'Asking price': 'Hirdet\u00E9si \u00E1r', - 'Price per sqm': '\u00C1r per nm', - 'Est. price per sqm': 'Becs\u00FClt \u00E1r per nm', - 'Asking price per sqm': 'Hirdet\u00E9si \u00E1r per nm', - 'Estimated monthly rent': 'Becs\u00FClt havi b\u00E9rleti d\u00EDj', - 'Asking rent (monthly)': 'K\u00E9rt b\u00E9rleti d\u00EDj (havi)', - 'Total floor area (sqm)': 'Teljes alapter\u00FClet (nm)', - 'Number of bedrooms & living rooms': 'H\u00E1l\u00F3- \u00E9s nappalik sz\u00E1ma', - 'Bedrooms': 'H\u00E1l\u00F3szob\u00E1k', - 'Bathrooms': 'F\u00FCrd\u0151szob\u00E1k', - 'Construction year': '\u00C9p\u00EDt\u00E9si \u00E9v', - 'Date of last transaction': 'Utols\u00F3 tranzakci\u00F3 d\u00E1tuma', - 'Listing date': 'Hirdet\u00E9s d\u00E1tuma', - 'Former council house': 'Volt \u00F6nkorm\u00E1nyzati lak\u00E1s', - 'Current energy rating': 'Jelenlegi energetikai min\u0151s\u00EDt\u00E9s', - 'Potential energy rating': 'Potenci\u00E1lis energetikai min\u0151s\u00EDt\u00E9s', - 'Interior height (m)': 'Belmagass\u00E1g (m)', + 'Listing status': 'Hirdetés állapota', + 'Property type': 'Ingatlantípus', + 'Leasehold/Freehold': 'Bérleti/Tulajdonjog', + 'Last known price': 'Utolsó ismert ár', + 'Estimated current price': 'Becsült jelenlegi ár', + 'Asking price': 'Hirdetési ár', + 'Price per sqm': 'Ár per nm', + 'Est. price per sqm': 'Becsült ár per nm', + 'Asking price per sqm': 'Hirdetési ár per nm', + 'Estimated monthly rent': 'Becsült havi bérleti díj', + 'Asking rent (monthly)': 'Kért bérleti díj (havi)', + 'Total floor area (sqm)': 'Teljes alapterület (nm)', + 'Number of bedrooms & living rooms': 'Háló- és nappalik száma', + 'Bedrooms': 'Hálószobák', + 'Bathrooms': 'Fürdőszobák', + 'Construction year': 'Építési év', + 'Date of last transaction': 'Utolsó tranzakció dátuma', + 'Listing date': 'Hirdetés dátuma', + 'Former council house': 'Volt önkormányzati lakás', + 'Current energy rating': 'Jelenlegi energetikai minősítés', + 'Potential energy rating': 'Potenciális energetikai minősítés', + 'Interior height (m)': 'Belmagasság (m)', // ─ Feature names (Transport) ─ - 'Distance to nearest train or tube station (km)': 'T\u00E1vols\u00E1g a legk\u00F6zelebbi vonat- vagy metr\u00F3\u00E1llom\u00E1st\u00F3l (km)', - 'Train or tube stations within 1km': 'Vonat- vagy metr\u00F3\u00E1llom\u00E1sok 1 km-en bel\u00FCl', + 'Distance to nearest train or tube station (km)': 'Távolság a legközelebbi vonat- vagy metróállomástól (km)', // ─ Feature names (Education) ─ - 'Good+ primary schools within 2km': 'J\u00F3+ \u00E1ltal\u00E1nos iskol\u00E1k 2 km-en bel\u00FCl', - 'Good+ secondary schools within 2km': 'J\u00F3+ k\u00F6z\u00E9piskol\u00E1k 2 km-en bel\u00FCl', - 'Good+ primary schools within 5km': 'J\u00F3+ \u00E1ltal\u00E1nos iskol\u00E1k 5 km-en bel\u00FCl', - 'Good+ secondary schools within 5km': 'J\u00F3+ k\u00F6z\u00E9piskol\u00E1k 5 km-en bel\u00FCl', - 'Education, Skills and Training Score': 'Oktat\u00E1s, k\u00E9szs\u00E9gek \u00E9s k\u00E9pz\u00E9s pontsz\u00E1m', + 'Good+ primary schools within 2km': 'Jó+ általános iskolák 2 km-en belül', + 'Good+ secondary schools within 2km': 'Jó+ középiskolák 2 km-en belül', + 'Good+ primary schools within 5km': 'Jó+ általános iskolák 5 km-en belül', + 'Good+ secondary schools within 5km': 'Jó+ középiskolák 5 km-en belül', + 'Education, Skills and Training Score': 'Oktatás, készségek és képzés pontszám', // ─ Feature names (Deprivation) ─ - 'Income Score (rate)': 'J\u00F6vedelmi pontsz\u00E1m (ar\u00E1ny)', - 'Employment Score (rate)': 'Foglalkoztatotts\u00E1gi pontsz\u00E1m (ar\u00E1ny)', - 'Health Deprivation and Disability Score': 'Eg\u00E9szs\u00E9g\u00FCgyi depriv\u00E1ci\u00F3 \u00E9s fogyat\u00E9koss\u00E1g pontsz\u00E1m', - 'Living Environment Score': 'Lak\u00F3k\u00F6rnyezet pontsz\u00E1m', - 'Indoors Sub-domain Score': 'Belt\u00E9ri alterulet pontsz\u00E1m', - 'Outdoors Sub-domain Score': 'K\u00FClt\u00E9ri alterulet pontsz\u00E1m', + 'Income Score (rate)': 'Jövedelmi pontszám (arány)', + 'Employment Score (rate)': 'Foglalkoztatottsági pontszám (arány)', + 'Health Deprivation and Disability Score': 'Egészségügyi depriváció és fogyatékosság pontszám', + 'Living Environment Score': 'Lakókörnyezet pontszám', + 'Indoors Sub-domain Score': 'Beltéri alterulet pontszám', + 'Outdoors Sub-domain Score': 'Kültéri alterulet pontszám', // ─ Feature names (Crime) ─ - 'Serious crime per 1k residents (avg/yr)': 'S\u00FAlyos b\u0171ncselekm\u00E9ny 1000 lakosra (\u00E9ves \u00E1tlag)', - 'Minor crime per 1k residents (avg/yr)': 'Kisebb b\u0171ncselekm\u00E9ny 1000 lakosra (\u00E9ves \u00E1tlag)', - 'Serious crime (avg/yr)': 'S\u00FAlyos b\u0171ncselekm\u00E9ny (\u00E9ves \u00E1tlag)', - 'Minor crime (avg/yr)': 'Kisebb b\u0171ncselekm\u00E9ny (\u00E9ves \u00E1tlag)', - 'Violence and sexual offences (avg/yr)': 'Er\u0151szak \u00E9s szexu\u00E1lis b\u0171ncselekm\u00E9nyek (\u00E9ves \u00E1tlag)', - 'Burglary (avg/yr)': 'Bet\u00F6r\u00E9s (\u00E9ves \u00E1tlag)', - 'Robbery (avg/yr)': 'Rabl\u00E1s (\u00E9ves \u00E1tlag)', - 'Vehicle crime (avg/yr)': 'J\u00E1rm\u0171b\u0171n\u00F6z\u00E9s (\u00E9ves \u00E1tlag)', - 'Anti-social behaviour (avg/yr)': 'K\u00F6z\u00E9rdek\u0171 rends\u00E9rts\u00E9g (\u00E9ves \u00E1tlag)', - 'Criminal damage and arson (avg/yr)': 'Rong\u00E1l\u00E1s \u00E9s gy\u00FAjtogat\u00E1s (\u00E9ves \u00E1tlag)', - 'Other theft (avg/yr)': 'Egy\u00E9b lop\u00E1s (\u00E9ves \u00E1tlag)', - 'Theft from the person (avg/yr)': 'Szem\u00E9ly elleni lop\u00E1s (\u00E9ves \u00E1tlag)', - 'Shoplifting (avg/yr)': 'Bolt\u00ED lop\u00E1s (\u00E9ves \u00E1tlag)', - 'Bicycle theft (avg/yr)': 'Ker\u00E9kp\u00E1rlop\u00E1s (\u00E9ves \u00E1tlag)', - 'Drugs (avg/yr)': 'K\u00E1b\u00EDt\u00F3szer (\u00E9ves \u00E1tlag)', - 'Possession of weapons (avg/yr)': 'Fegyvertart\u00E1s (\u00E9ves \u00E1tlag)', - 'Public order (avg/yr)': 'K\u00F6zrend (\u00E9ves \u00E1tlag)', - 'Other crime (avg/yr)': 'Egy\u00E9b b\u0171ncselekm\u00E9ny (\u00E9ves \u00E1tlag)', + 'Serious crime per 1k residents (avg/yr)': 'Súlyos bűncselekmény 1000 lakosra (éves átlag)', + 'Minor crime per 1k residents (avg/yr)': 'Kisebb bűncselekmény 1000 lakosra (éves átlag)', + 'Serious crime (avg/yr)': 'Súlyos bűncselekmény (éves átlag)', + 'Minor crime (avg/yr)': 'Kisebb bűncselekmény (éves átlag)', + 'Violence and sexual offences (avg/yr)': 'Erőszak és szexuális bűncselekmények (éves átlag)', + 'Burglary (avg/yr)': 'Betörés (éves átlag)', + 'Robbery (avg/yr)': 'Rablás (éves átlag)', + 'Vehicle crime (avg/yr)': 'Járműbűnözés (éves átlag)', + 'Anti-social behaviour (avg/yr)': 'Közérdekű rendsértség (éves átlag)', + 'Criminal damage and arson (avg/yr)': 'Rongálás és gyújtogatás (éves átlag)', + 'Other theft (avg/yr)': 'Egyéb lopás (éves átlag)', + 'Theft from the person (avg/yr)': 'Személy elleni lopás (éves átlag)', + 'Shoplifting (avg/yr)': 'Boltí lopás (éves átlag)', + 'Bicycle theft (avg/yr)': 'Kerékpárlopás (éves átlag)', + 'Drugs (avg/yr)': 'Kábítószer (éves átlag)', + 'Possession of weapons (avg/yr)': 'Fegyvertartás (éves átlag)', + 'Public order (avg/yr)': 'Közrend (éves átlag)', + 'Other crime (avg/yr)': 'Egyéb bűncselekmény (éves átlag)', // ─ Feature names (Demographics) ─ - 'Median age': 'Medi\u00E1n \u00E9letkor', - '% White': '% feh\u00E9r', - '% South Asian': '% d\u00E9l-\u00E1zsiai', + 'Median age': 'Medián életkor', + '% White': '% fehér', + '% South Asian': '% dél-ázsiai', '% Black': '% fekete', - '% East Asian': '% kelet-\u00E1zsiai', + '% East Asian': '% kelet-ázsiai', '% Mixed': '% vegyes', - '% Other': '% egy\u00E9b', + '% Other': '% egyéb', // ─ Feature names (Amenities) ─ - 'Distance to nearest park (km)': 'T\u00E1vols\u00E1g a legk\u00F6zelebbi parkt\u00F3l (km)', - 'Number of parks within 2km': 'Parkok sz\u00E1ma 2 km-en bel\u00FCl', - 'Number of restaurants within 2km': '\u00C9ttermek sz\u00E1ma 2 km-en bel\u00FCl', - 'Number of grocery shops and supermarkets within 2km': '\u00C9lelmiszerboltok \u00E9s szupermarketek sz\u00E1ma 2 km-en bel\u00FCl', + 'Distance to nearest park (km)': 'Távolság a legközelebbi parktól (km)', + 'Number of parks within 2km': 'Parkok száma 2 km-en belül', + 'Number of restaurants within 2km': 'Éttermek száma 2 km-en belül', + 'Number of grocery shops and supermarkets within 2km': 'Élelmiszerboltok és szupermarketek száma 2 km-en belül', 'Noise (dB)': 'Zaj (dB)', - 'Max available download speed (Mbps)': 'Max el\u00E9rhet\u0151 let\u00F6lt\u00E9si sebess\u00E9g (Mbps)', + 'Max available download speed (Mbps)': 'Max elérhető letöltési sebesség (Mbps)', // ─ Enum values ─ - 'Historical sale': 'T\u00F6rt\u00E9nelmi elad\u00E1s', - 'For sale': 'Elad\u00F3', - 'For rent': 'Kiad\u00F3', - 'Detached': 'K\u00FCl\u00F6n\u00E1ll\u00F3', - 'Semi-Detached': 'Ikerh\u00E1z', - 'Terraced': 'Sorh\u00E1z', - 'Flats/Maisonettes': 'Lak\u00E1sok/Maisonette-ek', - 'Other': 'Egy\u00E9b', + 'Historical sale': 'Történelmi eladás', + 'For sale': 'Eladó', + 'For rent': 'Kiadó', + 'Detached': 'Különálló', + 'Semi-Detached': 'Ikerház', + 'Terraced': 'Sorház', + 'Flats/Maisonettes': 'Lakások/Maisonette-ek', + 'Other': 'Egyéb', 'Freehold': 'Tulajdonjog', - 'Leasehold': 'B\u00E9rleti jog', + 'Leasehold': 'Bérleti jog', 'Yes': 'Igen', 'No': 'Nem', // ─ Stacked chart labels ─ - 'Serious crime': 'S\u00FAlyos b\u0171ncselekm\u00E9ny', - 'Minor crime': 'Kisebb b\u0171ncselekm\u00E9ny', - 'Ethnic composition': 'Etnikai \u00F6sszet\u00E9tel', + 'Serious crime': 'Súlyos bűncselekmény', + 'Minor crime': 'Kisebb bűncselekmény', + 'Ethnic composition': 'Etnikai összetétel', // ─ POI group names ─ - 'Public Transport': 'T\u00F6megk\u00F6zleked\u00E9s', - 'Leisure': 'Szabadid\u0151', - 'Health': 'Eg\u00E9szs\u00E9g\u00FCgy', - 'Emergency Services': 'S\u00FCrg\u0151ss\u00E9gi szolg\u00E1latok', - 'Groceries': '\u00C9lelmiszer', - 'Local Businesses': 'Helyi v\u00E1llalkoz\u00E1sok', - 'Culture': 'Kult\u00FAra', - 'Services': 'Szolg\u00E1ltat\u00E1sok', - 'Shops': '\u00DCzletek', + 'Public Transport': 'Tömegközlekedés', + 'Leisure': 'Szabadidő', + 'Health': 'Egészségügy', + 'Emergency Services': 'Sürgősségi szolgálatok', + 'Groceries': 'Élelmiszer', + 'Local Businesses': 'Helyi vállalkozások', + 'Culture': 'Kultúra', + 'Services': 'Szolgáltatások', + 'Shops': 'Üzletek', // ─ POI categories ─ - 'Airport': 'Rep\u00FCl\u0151t\u00E9r', + 'Airport': 'Repülőtér', 'Ferry': 'Komp', - 'Rail station': 'Vas\u00FAt\u00E1llom\u00E1s', - 'Bus stop': 'Buszmeg\u00E1ll\u00F3', - 'Bus station': 'Buszp\u00E1lyaudvar', - 'Taxi rank': 'Taxi\u00E1llom\u00E1s', - 'Metro or Tram stop': 'Metr\u00F3- vagy villamosmeg\u00E1ll\u00F3', - 'Caf\u00E9': 'K\u00E1v\u00E9z\u00F3', - 'Restaurant': '\u00C9tterem', + 'Rail station': 'Vasútállomás', + 'Bus stop': 'Buszmegálló', + 'Bus station': 'Buszpályaudvar', + 'Taxi rank': 'Taxiállomás', + 'Metro or Tram stop': 'Metró- vagy villamosmegálló', + 'Café': 'Kávézó', + 'Restaurant': 'Étterem', 'Pub': 'Kocsma', - 'Bar': 'B\u00E1r', - 'Fast Food': 'Gyors\u00E9tterem', - 'Nightclub': '\u00C9jszakai klub', + 'Bar': 'Bár', + 'Fast Food': 'Gyorsétterem', + 'Nightclub': 'Éjszakai klub', 'Cinema': 'Mozi', - 'Theatre': 'Sz\u00EDnh\u00E1z', - 'Live Music & Events': '\u00C9l\u0151zene \u00E9s rendezv\u00E9nyek', + 'Theatre': 'Színház', + 'Live Music & Events': 'Élőzene és rendezvények', 'Park': 'Park', - 'Playground': 'J\u00E1tsz\u00F3t\u00E9r', - 'Sports Centre': 'Sportk\u00F6zpont', - 'Entertainment': 'Sz\u00F3rakoztat\u00E1s', + 'Playground': 'Játszótér', + 'Sports Centre': 'Sportközpont', + 'Entertainment': 'Szórakoztatás', 'Supermarket': 'Szupermarket', - 'Convenience Store': 'K\u00EDsbolt', - 'Bakery': 'P\u00E9ks\u00E9g', - 'Butcher & Fishmonger': 'Hentes \u00E9s halas', - 'Greengrocer': 'Z\u00F6lds\u00E9ges', - 'Off-Licence': 'Italoz\u00F3', - 'Deli & Specialty': 'Csemege \u00E9s k\u00FCl\u00F6nleges', - 'Fashion & Clothing': 'Divat \u00E9s ruh\u00E1zat', + 'Convenience Store': 'Kísbolt', + 'Bakery': 'Pékség', + 'Butcher & Fishmonger': 'Hentes és halas', + 'Greengrocer': 'Zöldséges', + 'Off-Licence': 'Italozó', + 'Deli & Specialty': 'Csemege és különleges', + 'Fashion & Clothing': 'Divat és ruházat', 'Electronics': 'Elektronika', - 'Charity Shop': 'J\u00F3t\u00E9konys\u00E1gi bolt', - 'DIY & Hardware': 'Barkacs \u00E9s vas', - 'Home & Garden': 'Otthon \u00E9s kert', - 'Bookshop': 'K\u00F6nyvesbolt', - 'Pet Shop': '\u00C1llatkeresked\u00E9s', - 'Sports & Outdoor': 'Sport \u00E9s szabadt\u00E9r', - 'Newsagent': '\u00DAjs\u00E1g\u00E1rus', - 'Department Store': '\u00C1ruh\u00E1z', - 'Gift & Hobby': 'Aj\u00E1nd\u00E9k \u00E9s hobbi', - 'Specialist Shop': 'Szak\u00FCzlet', - 'Hairdresser & Beauty': 'Fodr\u00E1sz \u00E9s sz\u00E9ps\u00E9g', - 'Gym & Fitness': 'Edzterem \u00E9s fitnesz', - 'Dry Cleaner & Laundry': 'Vegytiszt\u00EDt\u00F3 \u00E9s mosoda', - 'Car Services': 'Aut\u00F3s szolg\u00E1ltat\u00E1sok', + 'Charity Shop': 'Jótékonysági bolt', + 'DIY & Hardware': 'Barkacs és vas', + 'Home & Garden': 'Otthon és kert', + 'Bookshop': 'Könyvesbolt', + 'Pet Shop': 'Állatkereskedés', + 'Sports & Outdoor': 'Sport és szabadtér', + 'Newsagent': 'Újságárus', + 'Department Store': 'Áruház', + 'Gift & Hobby': 'Ajándék és hobbi', + 'Specialist Shop': 'Szaküzlet', + 'Hairdresser & Beauty': 'Fodrász és szépség', + 'Gym & Fitness': 'Edzterem és fitnesz', + 'Dry Cleaner & Laundry': 'Vegytisztító és mosoda', + 'Car Services': 'Autós szolgáltatások', 'Post Office': 'Posta', - 'Vet & Pet Care': '\u00C1llatorvos \u00E9s \u00E1llatgondoz\u00E1s', + 'Vet & Pet Care': 'Állatorvos és állatgondozás', 'Bank': 'Bank', - 'Travel Agent': 'Utaz\u00E1si iroda', - 'Police': 'Rend\u0151rs\u00E9g', - 'Fire Station': 'T\u0171zolt\u00F3s\u00E1g', - 'Ambulance Station': 'Ment\u0151\u00E1llom\u00E1s', - 'GP Surgery': 'H\u00E1ziorvosi rendel\u0151', + 'Travel Agent': 'Utazási iroda', + 'Police': 'Rendőrség', + 'Fire Station': 'Tűzoltóság', + 'Ambulance Station': 'Mentőállomás', + 'GP Surgery': 'Háziorvosi rendelő', 'Dentist': 'Fogorvos', - 'Pharmacy': 'Gy\u00F3gyszert\u00E1r', - 'Hospital & Clinic': 'K\u00F3rh\u00E1z \u00E9s klinika', + 'Pharmacy': 'Gyógyszertár', + 'Hospital & Clinic': 'Kórház és klinika', 'Optician': 'Optikus', - 'Physiotherapy': 'Fizioter\u00E1pia', - 'Counselling & Therapy': 'Tan\u00E1csad\u00E1s \u00E9s ter\u00E1pia', - 'Care Home': 'Gondoz\u00F3h\u00E1z', - 'Medical & Mobility': 'Eg\u00E9szs\u00E9g\u00FCgyi \u00E9s mobilit\u00E1si eszk\u00F6z\u00F6k', - 'Museum': 'M\u00FAzeum', - 'Gallery': 'Gal\u00E9ria', - 'Library': 'K\u00F6nyvt\u00E1r', + 'Physiotherapy': 'Fizioterápia', + 'Counselling & Therapy': 'Tanácsadás és terápia', + 'Care Home': 'Gondozóház', + 'Medical & Mobility': 'Egészségügyi és mobilitási eszközök', + 'Museum': 'Múzeum', + 'Gallery': 'Galéria', + 'Library': 'Könyvtár', 'Place of Worship': 'Istentiszteleti hely', - 'Arts Centre': 'M\u0171v\u00E9szeti k\u00F6zpont', - 'Zoo': '\u00C1llatkert', - 'Tourist Attraction': 'Turisztikai l\u00E1tv\u00E1nyoss\u00E1g', + 'Arts Centre': 'Művészeti központ', + 'Zoo': 'Állatkert', + 'Tourist Attraction': 'Turisztikai látványosság', 'School': 'Iskola', - 'Hotel': 'Sz\u00E1lloda', - 'Local Business': 'Helyi v\u00E1llalkoz\u00E1s', - 'Offices': 'Irod\u00E1k', - 'EV Charging': 'Elektromos t\u00F6lt\u0151\u00E1llom\u00E1s', - 'Fuel Station': 'Benzink\u00FAt', - 'Community Centre': 'K\u00F6z\u00F6ss\u00E9gi k\u00F6zpont', + 'Hotel': 'Szálloda', + 'Local Business': 'Helyi vállalkozás', + 'Offices': 'Irodák', + 'EV Charging': 'Elektromos töltőállomás', + 'Fuel Station': 'Benzinkút', + 'Community Centre': 'Közösségi központ', // ─ Suffixes (used in formatters) ─ - '/mo': '/h\u00F3', - '/yr': '/\u00E9v', + '/mo': '/hó', + '/yr': '/év', ' sqm': ' nm', ' km': ' km', ' m': ' m', ' dB': ' dB', - ' years': ' \u00E9v', + ' years': ' év', ' rooms': ' szoba', }, }; diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index c2fdf8c..ce1eace 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -141,6 +141,11 @@ const zh: Translations = { removeFilterHint: '移除一个筛选条件以查看可用的数据指标', featureInfo: '数据指标信息', replayTutorial: '重新播放交互教程', + clearAll: '全部清除', + clearAllTitle: '清除所有筛选条件?', + clearAllSavePrompt: '是否要在清除前保存当前的筛选条件?', + saveAndClear: '保存并清除', + clearWithoutSaving: '不保存直接清除', }, // ── Philosophy Popup ─────────────────────────────── @@ -201,9 +206,9 @@ const zh: Translations = { describeIdealArea: '用 AI 描述您的理想区域', aiSearch: 'AI 搜索', describeHint: '描述您要找的区域', - placeholder: '例如:安静的区域,低于 \u00A340万,靠近好学校...', + placeholder: '例如:安静的区域,低于 £40万,靠近好学校...', example1: '安全的区域,靠近好学校', - example2: '到国王十字站30分钟通勤,低于 \u00A350万', + example2: '到国王十字站30分钟通勤,低于 £50万', example3: '安静的村庄,3间卧室,快速宽带', analysing: '正在分析您的需求...', searchingDestinations: '正在搜索目的地...', @@ -215,6 +220,11 @@ const zh: Translations = { // ── Map Legend ───────────────────────────────────── mapLegend: { clearColourView: '清除颜色视图', + historicalMatches: '历史房产匹配', + propertiesForSale: '待售房产', + propertiesForRent: '待租房产', + numberOfProperties: '房产数量', + previewing: '预览\u201c{{name}}\u201d', }, // ── Properties Pane ──────────────────────────────── @@ -222,7 +232,7 @@ const zh: Translations = { unknownAddress: '地址未知', unsaveProperty: '取消收藏', saveProperty: '收藏房产', - lastSold: '上次成交价:\u00A3{{price}}', + lastSold: '上次成交价:£{{price}}', estValue: '估计价值:', type: '类型:', builtForm: '建筑形式:', @@ -239,7 +249,7 @@ const zh: Translations = { renovations: '翻新记录', viewExternalListing: '查看外部房源', perMonth: '/月', - perSqm: '/m\u00B2', + perSqm: '/m²', searchPlaceholder: '按地址或邮编搜索...', propertyData: '房产数据', propertyDataDesc: '价格来自英国土地注册局(买家实际支付的金额)。建筑面积、能源评级、建造年份和产权来自官方能源性能证书调查。两个数据源通过每个邮编内的地址进行匹配。', @@ -343,7 +353,7 @@ const zh: Translations = { compPropertyDataSub: '(价格、能源性能证书、建筑面积)', compFilters: '56 项可组合筛选条件,尽在一处', compFiltersSub: '(所有信息,一张交互式地图)', - ctaTitle: '让您最大的投资成为最明智的\u00A0决定。', + ctaTitle: '让您最大的投资成为最明智的 决定。', ctaDescription: '这值得用专业的工具来做,别全靠运气。', }, @@ -351,7 +361,7 @@ const zh: Translations = { pricingPage: { title: '早期访问价格', subtitle: '一次付款,永久访问。越早加入,价格越优惠。', - costContext: '买房需要支付超过 \u00A310,000 的印花税、\u00A31,500 的律师费、\u00A3500 的房屋评估费。选错区域,您可能要忍受漫长的通勤、差劲的学校,或一条您事先不知道的嘈杂马路。', + costContext: '买房需要支付超过 £10,000 的印花税、£1,500 的律师费、£500 的房屋评估费。选错区域,您可能要忍受漫长的通勤、差劲的学校,或一条您事先不知道的嘈杂马路。', lessThanSurvey: '不到一次房屋评估的费用,却有用得多。', currentTier: '当前档位', firstNUsers: '前 {{count}} 名用户', @@ -514,7 +524,7 @@ const zh: Translations = { emailLabel: '邮箱', subscriptionLabel: '订阅', upgrade: '升级', - redirecting: '跳转中\u2026', + redirecting: '跳转中…', receiveNewsletter: '接收新闻邮件', needHelp: '需要帮助?请发邮件至', responseTime: '我们通常在 24 小时内回复。', @@ -592,38 +602,38 @@ const zh: Translations = { // ── Format / Time ────────────────────────────────── format: { - justNow: '\u521A\u521A', - minutesAgo: '{{count}}\u5206\u949F\u524D', - hoursAgo: '{{count}}\u5C0F\u65F6\u524D', - daysAgo: '{{count}}\u5929\u524D', - nFilters: '{{count}} \u4E2A\u7B5B\u9009', - noFilters: '\u65E0\u7B5B\u9009', - poiCategory: '{{count}} \u4E2A POI \u7C7B\u522B', - poiCategories: '{{count}} \u4E2A POI \u7C7B\u522B', - travelDestination: '{{count}} \u4E2A\u51FA\u884C\u76EE\u7684\u5730', - travelDestinations: '{{count}} \u4E2A\u51FA\u884C\u76EE\u7684\u5730', - propertiesMatch: '{{count}} \u5957\u623F\u4EA7\u7B26\u5408', - setFilters: '\u8BBE\u7F6E {{count}} \u4E2A\u7B5B\u9009\uFF1A{{list}}', - noFiltersSet: '\u672A\u8BBE\u7F6E\u7B5B\u9009', - toDestination: '{{mode}}\u5230 {{label}} {{bounds}}', - lessThanMin: '< {{max}} \u5206\u949F', - moreThanMin: '> {{min}} \u5206\u949F', + justNow: '刚刚', + minutesAgo: '{{count}}分钟前', + hoursAgo: '{{count}}小时前', + daysAgo: '{{count}}天前', + nFilters: '{{count}} 个筛选', + noFilters: '无筛选', + poiCategory: '{{count}} 个 POI 类别', + poiCategories: '{{count}} 个 POI 类别', + travelDestination: '{{count}} 个出行目的地', + travelDestinations: '{{count}} 个出行目的地', + propertiesMatch: '{{count}} 套房产符合', + setFilters: '设置 {{count}} 个筛选:{{list}}', + noFiltersSet: '未设置筛选', + toDestination: '{{mode}}到 {{label}} {{bounds}}', + lessThanMin: '< {{max}} 分钟', + moreThanMin: '> {{min}} 分钟', }, // ── Tutorial ────────────────────────────────────── tutorial: { - step1Title: '\u544A\u8BC9\u5730\u56FE\u4EC0\u4E48\u91CD\u8981', - step1Content: '\u8BBE\u7F6E\u9884\u7B97\u3001\u901A\u52E4\u4E0A\u9650\u3001\u5B66\u6821\u8D28\u91CF\u3001\u72AF\u7F6A\u95E8\u69DB\u3002\u60A8\u5173\u5FC3\u7684\u4E00\u5207\u3002\u53EA\u6709\u7B26\u5408\u6761\u4EF6\u7684\u533A\u57DF\u4F1A\u4FDD\u6301\u9AD8\u4EAE\u3002\u4F7F\u7528\u773C\u775B\u56FE\u6807\u6309\u4EFB\u610F\u7279\u5F81\u7740\u8272\u3002', - step2Title: '\u6216\u8005\u76F4\u63A5\u63CF\u8FF0', - step2Content: '\u7528\u4E2D\u6587\u8F93\u5165\u60A8\u7684\u9700\u6C42\uFF0C\u4F8B\u5982\u201C\u5B89\u9759\u7684\u5730\u533A\uFF0C\u9760\u8FD1\u597D\u5B66\u6821\uFF0C\u00A3400k \u4EE5\u4E0B\u201D\uFF0C\u6211\u4EEC\u4F1A\u4E3A\u60A8\u8BBE\u7F6E\u7B5B\u9009\u3002', - step3Title: '\u63A2\u7D22\u73B0\u6709\u4F4F\u5B85', - step3Content: '\u5728\u82F1\u683C\u5170\u5404\u5730\u5E73\u79FB\u548C\u7F29\u653E\u3002\u70B9\u51FB\u4EFB\u4F55\u5F69\u8272\u533A\u57DF\u67E5\u770B\u72AF\u7F6A\u3001\u5B66\u6821\u3001\u4EF7\u683C\u3001\u5BBD\u5E26\u3001\u566A\u97F3\u7B49\u4FE1\u606F\u3002', - step4Title: '\u8DF3\u8F6C\u5230\u67D0\u4E2A\u4F4D\u7F6E', - step4Content: '\u641C\u7D22\u4EFB\u4F55\u5730\u70B9\u6216\u90AE\u7F16\uFF0C\u5373\u53EF\u76F4\u63A5\u8DF3\u8F6C\u3002', - step5Title: '\u6DF1\u5165\u4E86\u89E3\u7EC6\u8282', - step5Content: '\u67E5\u770B\u533A\u57DF\u7EDF\u8BA1\u3001\u76F4\u65B9\u56FE\u548C\u5355\u4E2A\u623F\u4EA7\u8BB0\u5F55\uFF1A\u4EF7\u683C\u3001\u5EFA\u7B51\u9762\u79EF\u3001\u80FD\u6548\u8BC4\u7EA7\u7B49\u3002', - step6Title: '\u9644\u8FD1\u6709\u4EC0\u4E48\uFF1F', - step6Content: '\u5728\u5730\u56FE\u4E0A\u5F00\u542F\u5B66\u6821\u3001\u5546\u5E97\u3001\u8F66\u7AD9\u3001\u516C\u56ED\u548C\u9910\u5385\u56FE\u5C42\uFF0C\u67E5\u770B\u5468\u8FB9\u8BBE\u65BD\u3002', + step1Title: '告诉地图什么重要', + step1Content: '设置预算、通勤上限、学校质量、犯罪门槛。您关心的一切。只有符合条件的区域会保持高亮。使用眼睛图标按任意特征着色。', + step2Title: '或者直接描述', + step2Content: '用中文输入您的需求,例如“安静的地区,靠近好学校,£400k 以下”,我们会为您设置筛选。', + step3Title: '探索现有住宅', + step3Content: '在英格兰各地平移和缩放。点击任何彩色区域查看犯罪、学校、价格、宽带、噪音等信息。', + step4Title: '跳转到某个位置', + step4Content: '搜索任何地点或邮编,即可直接跳转。', + step5Title: '深入了解细节', + step5Content: '查看区域统计、直方图和单个房产记录:价格、建筑面积、能效评级等。', + step6Title: '附近有什么?', + step6Content: '在地图上开启学校、商店、车站、公园和餐厅图层,查看周边设施。', }, // ── Server-derived values ────────────────────────── @@ -631,209 +641,208 @@ const zh: Translations = { // The English keys MUST match exactly what the API returns. server: { // ─ Feature group names ─ - 'Properties': '\u623F\u4EA7', - 'Transport': '\u4EA4\u901A', - 'Education': '\u6559\u80B2', - 'Deprivation': '\u8D2B\u56F0\u6307\u6570', - 'Crime': '\u72AF\u7F6A', - 'Demographics': '\u4EBA\u53E3\u7EDF\u8BA1', - 'Amenities': '\u914D\u5957\u8BBE\u65BD', + 'Properties': '房产', + 'Transport': '交通', + 'Education': '教育', + 'Deprivation': '贫困指数', + 'Crime': '犯罪', + 'Demographics': '人口统计', + 'Amenities': '配套设施', // ─ Feature names (Properties) ─ - 'Listing status': '\u623F\u6E90\u72B6\u6001', - 'Property type': '\u623F\u4EA7\u7C7B\u578B', - 'Leasehold/Freehold': '\u79DF\u8D41\u4EA7\u6743/\u6C38\u4E45\u4EA7\u6743', - 'Last known price': '\u4E0A\u6B21\u6210\u4EA4\u4EF7', - 'Estimated current price': '\u4F30\u8BA1\u5F53\u524D\u4EF7\u683C', - 'Asking price': '\u6302\u724C\u4EF7', - 'Price per sqm': '\u6BCF\u5E73\u65B9\u7C73\u4EF7\u683C', - 'Est. price per sqm': '\u4F30\u8BA1\u6BCF\u5E73\u65B9\u7C73\u4EF7\u683C', - 'Asking price per sqm': '\u6302\u724C\u4EF7\u6BCF\u5E73\u65B9\u7C73', - 'Estimated monthly rent': '\u4F30\u8BA1\u6708\u79DF', - 'Asking rent (monthly)': '\u6708\u79DF', - 'Total floor area (sqm)': '\u603B\u5EFA\u7B51\u9762\u79EF\uFF08\u5E73\u65B9\u7C73\uFF09', - 'Number of bedrooms & living rooms': '\u5367\u5BA4\u548C\u5BA2\u5385\u6570\u91CF', - 'Bedrooms': '\u5367\u5BA4', - 'Bathrooms': '\u6D74\u5BA4', - 'Construction year': '\u5EFA\u9020\u5E74\u4EFD', - 'Date of last transaction': '\u4E0A\u6B21\u4EA4\u6613\u65E5\u671F', - 'Listing date': '\u4E0A\u5E02\u65E5\u671F', - 'Former council house': '\u539F\u516C\u5171\u4F4F\u623F', - 'Current energy rating': '\u5F53\u524D\u80FD\u6E90\u8BC4\u7EA7', - 'Potential energy rating': '\u6F5C\u5728\u80FD\u6E90\u8BC4\u7EA7', - 'Interior height (m)': '\u5BA4\u5185\u5C42\u9AD8\uFF08\u7C73\uFF09', + 'Listing status': '房源状态', + 'Property type': '房产类型', + 'Leasehold/Freehold': '租赁产权/永久产权', + 'Last known price': '上次成交价', + 'Estimated current price': '估计当前价格', + 'Asking price': '挂牌价', + 'Price per sqm': '每平方米价格', + 'Est. price per sqm': '估计每平方米价格', + 'Asking price per sqm': '挂牌价每平方米', + 'Estimated monthly rent': '估计月租', + 'Asking rent (monthly)': '月租', + 'Total floor area (sqm)': '总建筑面积(平方米)', + 'Number of bedrooms & living rooms': '卧室和客厅数量', + 'Bedrooms': '卧室', + 'Bathrooms': '浴室', + 'Construction year': '建造年份', + 'Date of last transaction': '上次交易日期', + 'Listing date': '上市日期', + 'Former council house': '原公共住房', + 'Current energy rating': '当前能源评级', + 'Potential energy rating': '潜在能源评级', + 'Interior height (m)': '室内层高(米)', // ─ Feature names (Transport) ─ - 'Distance to nearest train or tube station (km)': '\u5230\u6700\u8FD1\u706B\u8F66\u6216\u5730\u94C1\u7AD9\u7684\u8DDD\u79BB\uFF08\u516C\u91CC\uFF09', - 'Train or tube stations within 1km': '1\u516C\u91CC\u5185\u706B\u8F66\u6216\u5730\u94C1\u7AD9\u6570\u91CF', + 'Distance to nearest train or tube station (km)': '到最近火车或地铁站的距离(公里)', // ─ Feature names (Education) ─ - 'Good+ primary schools within 2km': '2\u516C\u91CC\u5185\u826F\u597D+\u5C0F\u5B66\u6570\u91CF', - 'Good+ secondary schools within 2km': '2\u516C\u91CC\u5185\u826F\u597D+\u4E2D\u5B66\u6570\u91CF', - 'Good+ primary schools within 5km': '5\u516C\u91CC\u5185\u826F\u597D+\u5C0F\u5B66\u6570\u91CF', - 'Good+ secondary schools within 5km': '5\u516C\u91CC\u5185\u826F\u597D+\u4E2D\u5B66\u6570\u91CF', - 'Education, Skills and Training Score': '\u6559\u80B2\u3001\u6280\u80FD\u548C\u57F9\u8BAD\u5F97\u5206', + 'Good+ primary schools within 2km': '2公里内良好+小学数量', + 'Good+ secondary schools within 2km': '2公里内良好+中学数量', + 'Good+ primary schools within 5km': '5公里内良好+小学数量', + 'Good+ secondary schools within 5km': '5公里内良好+中学数量', + 'Education, Skills and Training Score': '教育、技能和培训得分', // ─ Feature names (Deprivation) ─ - 'Income Score (rate)': '\u6536\u5165\u5F97\u5206\uFF08\u6BD4\u7387\uFF09', - 'Employment Score (rate)': '\u5C31\u4E1A\u5F97\u5206\uFF08\u6BD4\u7387\uFF09', - 'Health Deprivation and Disability Score': '\u5065\u5EB7\u4E0E\u6B8B\u969C\u5F97\u5206', - 'Living Environment Score': '\u5C45\u4F4F\u73AF\u5883\u5F97\u5206', - 'Indoors Sub-domain Score': '\u5BA4\u5185\u5B50\u9886\u57DF\u5F97\u5206', - 'Outdoors Sub-domain Score': '\u5BA4\u5916\u5B50\u9886\u57DF\u5F97\u5206', + 'Income Score (rate)': '收入得分(比率)', + 'Employment Score (rate)': '就业得分(比率)', + 'Health Deprivation and Disability Score': '健康与残障得分', + 'Living Environment Score': '居住环境得分', + 'Indoors Sub-domain Score': '室内子领域得分', + 'Outdoors Sub-domain Score': '室外子领域得分', // ─ Feature names (Crime) ─ - 'Serious crime per 1k residents (avg/yr)': '\u6BCF\u5343\u4EBA\u4E25\u91CD\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Minor crime per 1k residents (avg/yr)': '\u6BCF\u5343\u4EBA\u8F7B\u5FAE\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Serious crime (avg/yr)': '\u4E25\u91CD\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Minor crime (avg/yr)': '\u8F7B\u5FAE\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Violence and sexual offences (avg/yr)': '\u66B4\u529B\u548C\u6027\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Burglary (avg/yr)': '\u5165\u5BA4\u76D7\u7A83\uFF08\u5E74\u5747\uFF09', - 'Robbery (avg/yr)': '\u62A2\u52AB\uFF08\u5E74\u5747\uFF09', - 'Vehicle crime (avg/yr)': '\u8F66\u8F86\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Anti-social behaviour (avg/yr)': '\u53CD\u793E\u4F1A\u884C\u4E3A\uFF08\u5E74\u5747\uFF09', - 'Criminal damage and arson (avg/yr)': '\u5211\u4E8B\u6BC1\u574F\u548C\u7EB5\u706B\uFF08\u5E74\u5747\uFF09', - 'Other theft (avg/yr)': '\u5176\u4ED6\u76D7\u7A83\uFF08\u5E74\u5747\uFF09', - 'Theft from the person (avg/yr)': '\u4EBA\u8EAB\u76D7\u7A83\uFF08\u5E74\u5747\uFF09', - 'Shoplifting (avg/yr)': '\u5546\u5E97\u76D7\u7A83\uFF08\u5E74\u5747\uFF09', - 'Bicycle theft (avg/yr)': '\u81EA\u884C\u8F66\u76D7\u7A83\uFF08\u5E74\u5747\uFF09', - 'Drugs (avg/yr)': '\u6BD2\u54C1\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', - 'Possession of weapons (avg/yr)': '\u975E\u6CD5\u6301\u6709\u6B66\u5668\uFF08\u5E74\u5747\uFF09', - 'Public order (avg/yr)': '\u6270\u4E71\u516C\u5171\u79E9\u5E8F\uFF08\u5E74\u5747\uFF09', - 'Other crime (avg/yr)': '\u5176\u4ED6\u72AF\u7F6A\uFF08\u5E74\u5747\uFF09', + 'Serious crime per 1k residents (avg/yr)': '每千人严重犯罪(年均)', + 'Minor crime per 1k residents (avg/yr)': '每千人轻微犯罪(年均)', + 'Serious crime (avg/yr)': '严重犯罪(年均)', + 'Minor crime (avg/yr)': '轻微犯罪(年均)', + 'Violence and sexual offences (avg/yr)': '暴力和性犯罪(年均)', + 'Burglary (avg/yr)': '入室盗窃(年均)', + 'Robbery (avg/yr)': '抢劫(年均)', + 'Vehicle crime (avg/yr)': '车辆犯罪(年均)', + 'Anti-social behaviour (avg/yr)': '反社会行为(年均)', + 'Criminal damage and arson (avg/yr)': '刑事毁坏和纵火(年均)', + 'Other theft (avg/yr)': '其他盗窃(年均)', + 'Theft from the person (avg/yr)': '人身盗窃(年均)', + 'Shoplifting (avg/yr)': '商店盗窃(年均)', + 'Bicycle theft (avg/yr)': '自行车盗窃(年均)', + 'Drugs (avg/yr)': '毒品犯罪(年均)', + 'Possession of weapons (avg/yr)': '非法持有武器(年均)', + 'Public order (avg/yr)': '扰乱公共秩序(年均)', + 'Other crime (avg/yr)': '其他犯罪(年均)', // ─ Feature names (Demographics) ─ - 'Median age': '\u4E2D\u4F4D\u5E74\u9F84', - '% White': '% \u767D\u4EBA', - '% South Asian': '% \u5357\u4E9A\u88D4', - '% Black': '% \u9ED1\u4EBA', - '% East Asian': '% \u4E1C\u4E9A\u88D4', - '% Mixed': '% \u6DF7\u8840', - '% Other': '% \u5176\u4ED6', + 'Median age': '中位年龄', + '% White': '% 白人', + '% South Asian': '% 南亚裔', + '% Black': '% 黑人', + '% East Asian': '% 东亚裔', + '% Mixed': '% 混血', + '% Other': '% 其他', // ─ Feature names (Amenities) ─ - 'Distance to nearest park (km)': '\u5230\u6700\u8FD1\u516C\u56ED\u7684\u8DDD\u79BB\uFF08\u516C\u91CC\uFF09', - 'Number of parks within 2km': '2\u516C\u91CC\u5185\u516C\u56ED\u6570\u91CF', - 'Number of restaurants within 2km': '2\u516C\u91CC\u5185\u9910\u5385\u6570\u91CF', - 'Number of grocery shops and supermarkets within 2km': '2\u516C\u91CC\u5185\u98DF\u54C1\u5E97\u548C\u8D85\u5E02\u6570\u91CF', - 'Noise (dB)': '\u566A\u97F3\uFF08\u5206\u8D1D\uFF09', - 'Max available download speed (Mbps)': '\u6700\u5927\u53EF\u7528\u4E0B\u8F7D\u901F\u5EA6\uFF08Mbps\uFF09', + 'Distance to nearest park (km)': '到最近公园的距离(公里)', + 'Number of parks within 2km': '2公里内公园数量', + 'Number of restaurants within 2km': '2公里内餐厅数量', + 'Number of grocery shops and supermarkets within 2km': '2公里内食品店和超市数量', + 'Noise (dB)': '噪音(分贝)', + 'Max available download speed (Mbps)': '最大可用下载速度(Mbps)', // ─ Enum values ─ - 'Historical sale': '\u5386\u53F2\u4EA4\u6613', - 'For sale': '\u5728\u552E', - 'For rent': '\u51FA\u79DF', - 'Detached': '\u72EC\u7ACB\u5F0F\u4F4F\u5B85', - 'Semi-Detached': '\u534A\u72EC\u7ACB\u5F0F\u4F4F\u5B85', - 'Terraced': '\u8054\u6392\u4F4F\u5B85', - 'Flats/Maisonettes': '\u516C\u5BD3/\u590D\u5F0F\u516C\u5BD3', - 'Other': '\u5176\u4ED6', - 'Freehold': '\u6C38\u4E45\u4EA7\u6743', - 'Leasehold': '\u79DF\u8D41\u4EA7\u6743', - 'Yes': '\u662F', - 'No': '\u5426', + 'Historical sale': '历史交易', + 'For sale': '在售', + 'For rent': '出租', + 'Detached': '独立式住宅', + 'Semi-Detached': '半独立式住宅', + 'Terraced': '联排住宅', + 'Flats/Maisonettes': '公寓/复式公寓', + 'Other': '其他', + 'Freehold': '永久产权', + 'Leasehold': '租赁产权', + 'Yes': '是', + 'No': '否', // ─ Stacked chart labels ─ - 'Serious crime': '\u4E25\u91CD\u72AF\u7F6A', - 'Minor crime': '\u8F7B\u5FAE\u72AF\u7F6A', - 'Ethnic composition': '\u65CF\u88D4\u7EC4\u6210', + 'Serious crime': '严重犯罪', + 'Minor crime': '轻微犯罪', + 'Ethnic composition': '族裔组成', // ─ POI group names ─ - 'Public Transport': '\u516C\u5171\u4EA4\u901A', - 'Leisure': '\u4F11\u95F2', - 'Health': '\u5065\u5EB7', - 'Emergency Services': '\u7D27\u6025\u670D\u52A1', - 'Groceries': '\u98DF\u54C1\u6742\u8D27', - 'Local Businesses': '\u672C\u5730\u5546\u4E1A', - 'Culture': '\u6587\u5316', - 'Services': '\u670D\u52A1', - 'Shops': '\u5546\u5E97', + 'Public Transport': '公共交通', + 'Leisure': '休闲', + 'Health': '健康', + 'Emergency Services': '紧急服务', + 'Groceries': '食品杂货', + 'Local Businesses': '本地商业', + 'Culture': '文化', + 'Services': '服务', + 'Shops': '商店', // ─ POI categories ─ - 'Airport': '\u673A\u573A', - 'Ferry': '\u6E21\u8F6E', - 'Rail station': '\u706B\u8F66\u7AD9', - 'Bus stop': '\u516C\u4EA4\u7AD9', - 'Bus station': '\u516C\u4EA4\u67A2\u7EBD', - 'Taxi rank': '\u51FA\u79DF\u8F66\u7AD9', - 'Metro or Tram stop': '\u5730\u94C1\u6216\u6709\u8F68\u7535\u8F66\u7AD9', - 'Caf\u00E9': '\u5496\u5561\u9986', - 'Restaurant': '\u9910\u5385', - 'Pub': '\u9152\u5427', - 'Bar': '\u9152\u5427', - 'Fast Food': '\u5FEB\u9910', - 'Nightclub': '\u591C\u5E97', - 'Cinema': '\u7535\u5F71\u9662', - 'Theatre': '\u5267\u9662', - 'Live Music & Events': '\u73B0\u573A\u97F3\u4E50\u4E0E\u6D3B\u52A8', - 'Park': '\u516C\u56ED', - 'Playground': '\u6E38\u4E50\u573A', - 'Sports Centre': '\u4F53\u80B2\u4E2D\u5FC3', - 'Entertainment': '\u5A31\u4E50', - 'Supermarket': '\u8D85\u5E02', - 'Convenience Store': '\u4FBF\u5229\u5E97', - 'Bakery': '\u9762\u5305\u623A', - 'Butcher & Fishmonger': '\u8089\u94FA\u4E0E\u9C7C\u94FA', - 'Greengrocer': '\u679C\u852C\u5E97', - 'Off-Licence': '\u9152\u7C7B\u5546\u5E97', - 'Deli & Specialty': '\u719F\u98DF\u4E0E\u7279\u4EA7\u5E97', - 'Fashion & Clothing': '\u65F6\u88C5\u670D\u9970', - 'Electronics': '\u7535\u5B50\u4EA7\u54C1', - 'Charity Shop': '\u6148\u5584\u5546\u5E97', - 'DIY & Hardware': '\u5EFA\u6750\u4E94\u91D1', - 'Home & Garden': '\u5BB6\u5C45\u4E0E\u56ED\u827A', - 'Bookshop': '\u4E66\u5E97', - 'Pet Shop': '\u5BA0\u7269\u5E97', - 'Sports & Outdoor': '\u4F53\u80B2\u4E0E\u6237\u5916', - 'Newsagent': '\u62A5\u520A\u4EAD', - 'Department Store': '\u767E\u8D27\u5546\u5E97', - 'Gift & Hobby': '\u793C\u54C1\u4E0E\u7231\u597D', - 'Specialist Shop': '\u4E13\u4E1A\u5546\u5E97', - 'Hairdresser & Beauty': '\u7F8E\u53D1\u4E0E\u7F8E\u5BB9', - 'Gym & Fitness': '\u5065\u8EAB\u623F', - 'Dry Cleaner & Laundry': '\u5E72\u6D17\u4E0E\u6D17\u8863', - 'Car Services': '\u6C7D\u8F66\u670D\u52A1', - 'Post Office': '\u90AE\u5C40', - 'Vet & Pet Care': '\u5BA0\u7269\u533B\u9662\u4E0E\u62A4\u7406', - 'Bank': '\u94F6\u884C', - 'Travel Agent': '\u65C5\u884C\u793E', - 'Police': '\u8B66\u5BDF', - 'Fire Station': '\u6D88\u9632\u7AD9', - 'Ambulance Station': '\u6025\u6551\u7AD9', - 'GP Surgery': '\u5168\u79D1\u8BCA\u6240', - 'Dentist': '\u7259\u79D1', - 'Pharmacy': '\u836F\u623F', - 'Hospital & Clinic': '\u533B\u9662\u4E0E\u8BCA\u6240', - 'Optician': '\u773C\u955C\u5E97', - 'Physiotherapy': '\u7406\u7597', - 'Counselling & Therapy': '\u5FC3\u7406\u54A8\u8BE2\u4E0E\u6CBB\u7597', - 'Care Home': '\u517B\u8001\u9662', - 'Medical & Mobility': '\u533B\u7597\u5668\u68B0\u4E0E\u8F85\u52A9\u8BBE\u5907', - 'Museum': '\u535A\u7269\u9986', - 'Gallery': '\u7F8E\u672F\u9986', - 'Library': '\u56FE\u4E66\u9986', - 'Place of Worship': '\u5B97\u6559\u573A\u6240', - 'Arts Centre': '\u827A\u672F\u4E2D\u5FC3', - 'Zoo': '\u52A8\u7269\u56ED', - 'Tourist Attraction': '\u65C5\u6E38\u666F\u70B9', - 'School': '\u5B66\u6821', - 'Hotel': '\u9152\u5E97', - 'Local Business': '\u672C\u5730\u5546\u4E1A', - 'Offices': '\u5199\u5B57\u697C', - 'EV Charging': '\u7535\u52A8\u8F66\u5145\u7535\u7AD9', - 'Fuel Station': '\u52A0\u6CB9\u7AD9', - 'Community Centre': '\u793E\u533A\u4E2D\u5FC3', + 'Airport': '机场', + 'Ferry': '渡轮', + 'Rail station': '火车站', + 'Bus stop': '公交站', + 'Bus station': '公交枢纽', + 'Taxi rank': '出租车站', + 'Metro or Tram stop': '地铁或有轨电车站', + 'Café': '咖啡馆', + 'Restaurant': '餐厅', + 'Pub': '酒吧', + 'Bar': '酒吧', + 'Fast Food': '快餐', + 'Nightclub': '夜店', + 'Cinema': '电影院', + 'Theatre': '剧院', + 'Live Music & Events': '现场音乐与活动', + 'Park': '公园', + 'Playground': '游乐场', + 'Sports Centre': '体育中心', + 'Entertainment': '娱乐', + 'Supermarket': '超市', + 'Convenience Store': '便利店', + 'Bakery': '面包戺', + 'Butcher & Fishmonger': '肉铺与鱼铺', + 'Greengrocer': '果蔬店', + 'Off-Licence': '酒类商店', + 'Deli & Specialty': '熟食与特产店', + 'Fashion & Clothing': '时装服饰', + 'Electronics': '电子产品', + 'Charity Shop': '慈善商店', + 'DIY & Hardware': '建材五金', + 'Home & Garden': '家居与园艺', + 'Bookshop': '书店', + 'Pet Shop': '宠物店', + 'Sports & Outdoor': '体育与户外', + 'Newsagent': '报刊亭', + 'Department Store': '百货商店', + 'Gift & Hobby': '礼品与爱好', + 'Specialist Shop': '专业商店', + 'Hairdresser & Beauty': '美发与美容', + 'Gym & Fitness': '健身房', + 'Dry Cleaner & Laundry': '干洗与洗衣', + 'Car Services': '汽车服务', + 'Post Office': '邮局', + 'Vet & Pet Care': '宠物医院与护理', + 'Bank': '银行', + 'Travel Agent': '旅行社', + 'Police': '警察', + 'Fire Station': '消防站', + 'Ambulance Station': '急救站', + 'GP Surgery': '全科诊所', + 'Dentist': '牙科', + 'Pharmacy': '药房', + 'Hospital & Clinic': '医院与诊所', + 'Optician': '眼镜店', + 'Physiotherapy': '理疗', + 'Counselling & Therapy': '心理咨询与治疗', + 'Care Home': '养老院', + 'Medical & Mobility': '医疗器械与辅助设备', + 'Museum': '博物馆', + 'Gallery': '美术馆', + 'Library': '图书馆', + 'Place of Worship': '宗教场所', + 'Arts Centre': '艺术中心', + 'Zoo': '动物园', + 'Tourist Attraction': '旅游景点', + 'School': '学校', + 'Hotel': '酒店', + 'Local Business': '本地商业', + 'Offices': '写字楼', + 'EV Charging': '电动车充电站', + 'Fuel Station': '加油站', + 'Community Centre': '社区中心', // ─ Suffixes (used in formatters) ─ - '/mo': '/\u6708', - '/yr': '/\u5E74', - ' sqm': ' \u5E73\u65B9\u7C73', - ' km': ' \u516C\u91CC', - ' m': ' \u7C73', - ' dB': ' \u5206\u8D1D', - ' years': ' \u5E74', - ' rooms': ' \u95F4', + '/mo': '/月', + '/yr': '/年', + ' sqm': ' 平方米', + ' km': ' 公里', + ' m': ' 米', + ' dB': ' 分贝', + ' years': ' 年', + ' rooms': ' 间', }, }; diff --git a/frontend/src/lib/feature-icons.tsx b/frontend/src/lib/feature-icons.tsx index e8c104d..d6bcecd 100644 --- a/frontend/src/lib/feature-icons.tsx +++ b/frontend/src/lib/feature-icons.tsx @@ -141,15 +141,6 @@ const FEATURE_ICON_PATHS: Record = { ), // ── Transport ──────────────────────────────── - 'Train or tube stations within 1km': ( - <> - - - - - - - ), 'Distance to nearest train or tube station (km)': ( <> diff --git a/pipeline/transform/merge.py b/pipeline/transform/merge.py index cd0a5ac..e53862a 100644 --- a/pipeline/transform/merge.py +++ b/pipeline/transform/merge.py @@ -50,7 +50,6 @@ _AREA_COLUMNS = [ "Number of restaurants within 2km", "Number of grocery shops and supermarkets within 2km", "Number of parks within 2km", - "Train or tube stations within 1km", "Distance to nearest train or tube station (km)", "Distance to nearest park (km)", # Environment @@ -325,7 +324,6 @@ def _build( "restaurants_2km": "Number of restaurants within 2km", "groceries_2km": "Number of grocery shops and supermarkets within 2km", "parks_2km": "Number of parks within 2km", - "train_tube_1km": "Train or tube stations within 1km", "train_tube_nearest_km": "Distance to nearest train or tube station (km)", "parks_nearest_km": "Distance to nearest park (km)", "latest_price": "Last known price", diff --git a/pipeline/transform/poi_proximity.py b/pipeline/transform/poi_proximity.py index 3f14d64..7bf2766 100644 --- a/pipeline/transform/poi_proximity.py +++ b/pipeline/transform/poi_proximity.py @@ -15,11 +15,6 @@ POI_GROUPS_2KM = { "groceries": ["Greengrocer", "Supermarket", "Convenience Store"], } -# Train/tube stations counted at 1km radius -TRAIN_TUBE_GROUP = { - "train_tube": ["Metro or Tram stop", "Rail station"], -} - # Groups for which to compute distance to nearest POI (from filtered POIs) DISTANCE_GROUPS = { "train_tube": ["Metro or Tram stop", "Rail station"], @@ -67,11 +62,6 @@ def main(): postcodes, pois, groups=POI_GROUPS_2KM, radius_km=2 ) - # Count train/tube stations within 1km - counts_1km = count_pois_per_postcode( - postcodes, pois, groups=TRAIN_TUBE_GROUP, radius_km=1 - ) - # Distance to nearest train/tube station (from filtered POIs) distances = min_distance_per_postcode(postcodes, pois, groups=DISTANCE_GROUPS) @@ -86,8 +76,7 @@ def main(): # Join all results on postcode result = ( - counts_2km.join(counts_1km, on="postcode") - .join(distances, on="postcode") + counts_2km.join(distances, on="postcode") .join(park_counts_2km, on="postcode") .join(park_distances, on="postcode") ) diff --git a/server-rs/src/consts.rs b/server-rs/src/consts.rs index c2976be..10c8c10 100644 --- a/server-rs/src/consts.rs +++ b/server-rs/src/consts.rs @@ -25,6 +25,6 @@ pub const AI_FILTERS_WEEKLY_TOKEN_LIMIT: u64 = 10_000_000; /// Timeout for outbound HTTP service calls (seconds). pub const SERVICE_CALL_TIMEOUT: u64 = 120; -/// Inner London free zone bounds (south, west, north, east) — roughly zone 1. +/// Demo free zone bounds (south, west, north, east) — inner London, roughly zone 1. /// Users without a license can only query data within these bounds. pub const FREE_ZONE_BOUNDS: (f64, f64, f64, f64) = (51.44, -0.31, 51.59, 0.05); diff --git a/server-rs/src/features.rs b/server-rs/src/features.rs index 7fb4b0d..15b5fba 100644 --- a/server-rs/src/features.rs +++ b/server-rs/src/features.rs @@ -402,23 +402,6 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[ modes: &[], linked: "", }), - Feature::Numeric(FeatureConfig { - name: "Train or tube stations within 1km", - bounds: Bounds::Percentile { - low: 5.0, - high: 95.0, - }, - step: 1.0, - description: "Number of train or tube stations within 1km", - detail: "Rail stations and Tube/metro/tram stops within 1km of the postcode. Does not include bus stops.", - source: "naptan", - prefix: "", - suffix: "", - raw: false, - absolute: false, - modes: &[], - linked: "", - }), ], }, FeatureGroup { diff --git a/server-rs/src/licensing.rs b/server-rs/src/licensing.rs index 3301a69..1992df3 100644 --- a/server-rs/src/licensing.rs +++ b/server-rs/src/licensing.rs @@ -28,7 +28,7 @@ pub fn check_license_bounds( let body = json!({ "error": "license_required", - "message": "A license is required to view data outside inner London", + "message": "A license is required to view data outside the demo area", "free_zone": { "south": fz_south, "west": fz_west, diff --git a/server-rs/src/parsing.rs b/server-rs/src/parsing.rs index 4227915..36c8922 100644 --- a/server-rs/src/parsing.rs +++ b/server-rs/src/parsing.rs @@ -5,5 +5,7 @@ mod h3; pub use bounds::{bounds_intersect, h3_cell_bounds, parse_bounds, require_bounds}; pub use fields::{parse_field_indices, parse_field_set}; -pub use filters::{parse_filters, row_passes_filters, ParsedEnumFilter, ParsedFilter}; +pub use filters::{ + count_filter_impacts, parse_filters, row_passes_filters, ParsedEnumFilter, ParsedFilter, +}; pub use h3::{cell_for_row, cell_for_row_cached, needs_parent, validate_h3_resolution}; diff --git a/server-rs/src/parsing/filters.rs b/server-rs/src/parsing/filters.rs index 04fde0e..f87e9f9 100644 --- a/server-rs/src/parsing/filters.rs +++ b/server-rs/src/parsing/filters.rs @@ -121,6 +121,65 @@ pub fn row_passes_filters( }) } +/// Single-pass marginal impact counting. +/// +/// Returns `(total_passing, impacts)` where `impacts[i]` is how many MORE rows +/// would pass if the i-th filter (numeric first, then enum) were removed. +/// +/// For each row we record which filters reject it: +/// - 0 failures → passes (counted in `total_passing`) +/// - exactly 1 failure → that filter's marginal cost (counted in `impacts[i]`) +/// - 2+ failures → removing any single filter won't recover it (ignored) +pub fn count_filter_impacts( + filters: &[ParsedFilter], + enum_filters: &[ParsedEnumFilter], + feature_data: &[u16], + num_features: usize, + rows: impl Iterator, +) -> (u32, Vec) { + let n = filters.len() + enum_filters.len(); + let mut total_passing: u32 = 0; + let mut impacts = vec![0u32; n]; + + for row_idx in rows { + let base = row_idx as usize * num_features; + let mut fail_count: u32 = 0; + let mut fail_index: usize = 0; + + for (i, f) in filters.iter().enumerate() { + let raw = feature_data[base + f.feat_idx]; + if raw == NAN_U16 || raw < f.min_u16 || raw > f.max_u16 { + fail_count += 1; + fail_index = i; + if fail_count > 1 { + break; + } + } + } + + if fail_count <= 1 { + for (i, f) in enum_filters.iter().enumerate() { + let raw = feature_data[base + f.feat_idx]; + if raw == NAN_U16 || !f.allowed.contains(&raw) { + fail_count += 1; + fail_index = filters.len() + i; + if fail_count > 1 { + break; + } + } + } + } + + match fail_count { + 0 => total_passing += 1, + 1 => impacts[fail_index] += 1, + _ => {} + } + } + + (total_passing, impacts) +} + #[cfg(test)] mod tests { use super::*; @@ -536,4 +595,85 @@ mod tests { assert!(!row_passes_filters(0, &[], &enum_filters, &feature_data, 1)); } + + #[test] + fn filter_impacts_single_pass() { + // 2 numeric features, 4 rows: + // row 0: price=150, area=100 → passes both + // row 1: price=600, area=100 → fails price only + // row 2: price=150, area=300 → fails area only + // row 3: price=600, area=300 → fails both + let tq = test_quant(2, 2); + let feature_data = vec![ + tq.encode(0, 150.0), tq.encode(1, 100.0), // row 0 + tq.encode(0, 600.0), tq.encode(1, 100.0), // row 1 + tq.encode(0, 150.0), tq.encode(1, 300.0), // row 2 + tq.encode(0, 600.0), tq.encode(1, 300.0), // row 3 + ]; + let filters = vec![ + ParsedFilter { + feat_idx: 0, + min_u16: tq.as_ref().encode_min(0, 100.0), + max_u16: tq.as_ref().encode_max(0, 500.0), + }, + ParsedFilter { + feat_idx: 1, + min_u16: tq.as_ref().encode_min(1, 50.0), + max_u16: tq.as_ref().encode_max(1, 200.0), + }, + ]; + + let (total, impacts) = + count_filter_impacts(&filters, &[], &feature_data, 2, (0..4u32).into_iter()); + + assert_eq!(total, 1); // only row 0 passes + assert_eq!(impacts[0], 1); // row 1 fails price only + assert_eq!(impacts[1], 1); // row 2 fails area only + // row 3 fails both → not counted + } + + #[test] + fn filter_impacts_with_enum() { + // 1 numeric + 1 enum, 3 rows: + // row 0: price=150, type=0(A) → passes both + // row 1: price=150, type=2(C) → fails enum only + // row 2: price=600, type=0(A) → fails numeric only + let tq = test_quant(2, 1); + let feature_data = vec![ + tq.encode(0, 150.0), 0u16, // row 0 + tq.encode(0, 150.0), 2u16, // row 1 + tq.encode(0, 600.0), 0u16, // row 2 + ]; + let num_filters = vec![ParsedFilter { + feat_idx: 0, + min_u16: tq.as_ref().encode_min(0, 100.0), + max_u16: tq.as_ref().encode_max(0, 500.0), + }]; + let enum_filters = vec![ParsedEnumFilter { + feat_idx: 1, + allowed: [0u16, 1].into_iter().collect(), + }]; + + let (total, impacts) = count_filter_impacts( + &num_filters, + &enum_filters, + &feature_data, + 2, + (0..3u32).into_iter(), + ); + + assert_eq!(total, 1); // row 0 + assert_eq!(impacts[0], 1); // row 2 fails numeric only → impacts[0] + assert_eq!(impacts[1], 1); // row 1 fails enum only → impacts[1] + } + + #[test] + fn filter_impacts_no_filters() { + let tq = test_quant(1, 1); + let feature_data = vec![tq.encode(0, 100.0)]; + let (total, impacts) = + count_filter_impacts(&[], &[], &feature_data, 1, (0..1u32).into_iter()); + assert_eq!(total, 1); + assert!(impacts.is_empty()); + } } diff --git a/server-rs/src/routes.rs b/server-rs/src/routes.rs index f775fe4..c3b55a7 100644 --- a/server-rs/src/routes.rs +++ b/server-rs/src/routes.rs @@ -1,6 +1,7 @@ mod ai_filters; mod checkout; mod export; +mod filter_counts; mod features; mod hexagon_stats; pub(crate) mod hexagons; @@ -31,6 +32,7 @@ pub(crate) mod travel_time; pub use ai_filters::{build_system_prompt, post_ai_filters}; pub use checkout::post_checkout; pub use export::get_export; +pub use filter_counts::get_filter_counts; pub use features::{build_features_response, get_features, FeatureInfo, FeaturesResponse}; pub use hexagon_stats::get_hexagon_stats; pub use hexagons::get_hexagons; @@ -43,7 +45,7 @@ pub use places::get_places; pub use pois::{get_poi_categories, get_pois}; pub use postcode_properties::get_postcode_properties; pub use postcode_stats::get_postcode_stats; -pub use postcodes::{get_postcode_lookup, get_postcodes}; +pub use postcodes::{get_nearest_postcode, get_postcode_lookup, get_postcodes}; pub use pricing::get_pricing; pub use properties::get_hexagon_properties; pub use reload::post_reload; diff --git a/server-rs/src/routes/ai_filters.rs b/server-rs/src/routes/ai_filters.rs index cc5a0dc..fcf1463 100644 --- a/server-rs/src/routes/ai_filters.rs +++ b/server-rs/src/routes/ai_filters.rs @@ -39,8 +39,6 @@ pub struct AiFiltersRequest { query: String, /// Current filters for conversational refinement (e.g. "make it cheaper") context: Option, - /// Current listing mode (historical/buy/rent). Defaults to "historical". - listing_type: Option, } #[derive(Serialize)] @@ -62,8 +60,6 @@ pub struct AiFiltersResponse { /// What the LLM couldn't map to existing filters (empty if everything matched) #[serde(skip_serializing_if = "String::is_empty")] notes: String, - /// The listing mode used for this response (historical/buy/rent) - listing_type: String, /// Number of properties matching the proposed filters (excludes travel time) match_count: usize, } @@ -345,34 +341,19 @@ pub fn build_system_prompt( modes_list, )); - // Listing modes section + // Feature guidance — only historical features are available parts.push( - "\n--- LISTING MODES ---\n\ - There are three listing modes that control which property data is shown:\n\ - - \"historical\": Historical sales from Land Registry (default). Uses features like \ - \"Last known price\", \"Estimated current price\", \"Price per sqm\".\n\ - - \"buy\": Properties currently listed for sale. Uses features like \"Asking price\", \ - \"Asking price per sqm\".\n\ - - \"rent\": Properties currently listed for rent. Uses features like \ - \"Asking rent (monthly)\".\n\ + "\n--- DATA SOURCE ---\n\ + The data is historical property sales from the Land Registry.\n\ \n\ - When the user mentions buying, purchasing, for-sale properties, or asking prices, \ - set listing_type to \"buy\".\n\ - When the user mentions renting, letting, rental properties, or monthly rent, \ - set listing_type to \"rent\".\n\ - When the user doesn't specify or mentions historical prices/past sales, \ - omit listing_type to keep the current mode.\n\ + Use these features for price queries:\n\ + - For purchase price: use \"Estimated current price\" or \"Last known price\"\n\ + - For price per sqm: use \"Est. price per sqm\"\n\ + - For rent: use \"Estimated monthly rent\"\n\ \n\ - Features marked with [mode] below are only available in that mode. \ - Features without a mode annotation work in all modes. \ - ONLY use features valid for the chosen listing_type.\n\ - If the user mentions price and the mode is \"buy\", use \"Asking price\" (not \"Last known price\").\n\ - If the user mentions rent/price and the mode is \"rent\", use \"Asking rent (monthly)\".\n\ - \n\ - Feature equivalences across modes:\n\ - - \"Estimated current price\" (historical) ↔ \"Asking price\" (buy)\n\ - - \"Est. price per sqm\" (historical) ↔ \"Asking price per sqm\" (buy)\n\ - - \"Estimated monthly rent\" (historical) ↔ \"Asking rent (monthly)\" (rent)" + Features marked with [historical] below are available. \ + Features marked with [buy] or [rent] are NOT available — do not use them.\n\ + ONLY use features marked [historical] or unmarked." .to_string(), ); @@ -412,7 +393,7 @@ pub fn build_system_prompt( description, .. } => { - // Skip Listing status — handled via listing_type field + // Skip Listing status — auto-injected as "Historical sale" if name == "Listing status" { continue; } @@ -499,11 +480,11 @@ pub fn build_system_prompt( .to_string(), ); - // Examples showing listing mode switching + // Examples showing rent and price features parts.push( - "\nUser: \"2 bed flat to rent under £1500/month\"\n\ - Output: {\"listing_type\": \"rent\", \ - \"numeric_filters\": [{\"name\": \"Asking rent (monthly)\", \"bound\": \"max\", \"value\": 1500}], \ + "\nUser: \"2 bed flat with rent under £1500/month\"\n\ + Output: {\ + \"numeric_filters\": [{\"name\": \"Estimated monthly rent\", \"bound\": \"max\", \"value\": 1500}], \ \"enum_filters\": [{\"name\": \"Property type\", \"values\": [\"Flats/Maisonettes\"]}], \ \"travel_time_filters\": [], \ \"notes\": \"\"}" @@ -511,9 +492,9 @@ pub fn build_system_prompt( ); parts.push( - "\nUser: \"3 bed house to buy under 500k with good schools\"\n\ - Output: {\"listing_type\": \"buy\", \ - \"numeric_filters\": [{\"name\": \"Asking price\", \"bound\": \"max\", \"value\": 500000}, \ + "\nUser: \"3 bed house under 500k with good schools\"\n\ + Output: {\ + \"numeric_filters\": [{\"name\": \"Estimated current price\", \"bound\": \"max\", \"value\": 500000}, \ {\"name\": \"Good+ primary schools within 2km\", \"bound\": \"min\", \"value\": 2}], \ \"enum_filters\": [{\"name\": \"Property type\", \ \"values\": [\"Detached\", \"Semi-Detached\", \"Terraced\"]}], \ @@ -525,11 +506,9 @@ pub fn build_system_prompt( // Output format reminder parts.push( "\n--- OUTPUT FORMAT ---\n\ - {\"listing_type\": \"buy\"|\"rent\" (OPTIONAL — only when switching mode), \ - \"numeric_filters\": [...], \"enum_filters\": [...], \ + {\"numeric_filters\": [...], \"enum_filters\": [...], \ \"travel_time_filters\": [{\"mode\": \"...\", \"slug\": \"...\", \"label\": \"...\", \ \"bound\": \"min\"|\"max\", \"value\": N}, ...], \"notes\": \"...\"}\n\ - - listing_type: include only when the user explicitly wants to buy or rent. Omit to keep current mode.\n\ - travel_time_filters: use ONLY slugs returned by search_destinations. If a place isn't found, mention it in notes.\n\ Respond with ONLY the JSON object. No explanation." .to_string(), @@ -779,17 +758,9 @@ pub async fn post_ai_filters( let tools = build_tool_declarations(&state); - // Resolve current listing mode from request - let current_mode = req.listing_type.as_deref().unwrap_or("historical"); - let current_mode = match current_mode { - "historical" | "buy" | "rent" => current_mode, - _ => "historical", - }; - - // Build user message with listing mode and optional context for conversational refinement + // Build user message with optional context for conversational refinement let user_text = if let Some(ref ctx) = req.context { let mut msg = String::new(); - msg.push_str(&format!("Current listing mode: {}\n", current_mode)); msg.push_str("Currently active filters:\n"); msg.push_str(&serde_json::to_string(&ctx.filters).unwrap_or_default()); if !ctx.travel_time.is_empty() { @@ -807,10 +778,7 @@ pub async fn post_ai_filters( msg.push_str(&format!("\nUser request: {}", req.query)); msg } else { - format!( - "Current listing mode: {}\nUser request: {}", - current_mode, req.query - ) + req.query.clone() }; let mut contents = vec![json!({ @@ -967,17 +935,8 @@ pub async fn post_ai_filters( } }; - // Resolve listing_type: LLM output > request > "historical" - let listing_type = raw - .get("listing_type") - .and_then(|val| val.as_str()) - .unwrap_or(current_mode); - let listing_type = match listing_type { - "historical" | "buy" | "rent" => listing_type, - _ => current_mode, - }; - - let mut filters = validate_and_convert(&raw, &state.features_response, listing_type); + // Only historical mode is supported — validate features accordingly + let mut filters = validate_and_convert(&raw, &state.features_response, "historical"); let travel_time_filters = validate_travel_time_filters(&raw, &state); let notes = raw .get("notes") @@ -985,14 +944,12 @@ pub async fn post_ai_filters( .unwrap_or("") .to_string(); - // Auto-inject Listing status filter for the chosen mode - let listing_value = match listing_type { - "buy" => "For sale", - "rent" => "For rent", - _ => "Historical sale", - }; + // Auto-inject Listing status filter for historical mode if let Value::Object(ref mut map) = filters { - map.insert("Listing status".to_string(), json!([listing_value])); + map.insert( + "Listing status".to_string(), + json!(["Historical sale"]), + ); } // Count matching properties and refine if too restrictive @@ -1031,7 +988,6 @@ pub async fn post_ai_filters( filters, travel_time_filters, notes, - listing_type: listing_type.to_string(), match_count: 0, })); } @@ -1073,7 +1029,7 @@ pub async fn post_ai_filters( let log_state = state.clone(); let log_user_id = user.id.clone(); let log_query = req.query.clone(); - let log_listing_type = listing_type.to_string(); + let log_listing_type = "historical".to_string(); let log_notes = notes.clone(); let log_rounds = (round + 1) as u64; tokio::spawn(async move { @@ -1094,7 +1050,6 @@ pub async fn post_ai_filters( filters, travel_time_filters, notes, - listing_type: listing_type.to_string(), match_count, })); } diff --git a/server-rs/src/routes/filter_counts.rs b/server-rs/src/routes/filter_counts.rs new file mode 100644 index 0000000..23909d1 --- /dev/null +++ b/server-rs/src/routes/filter_counts.rs @@ -0,0 +1,203 @@ +use std::sync::Arc; + +use axum::extract::{Query, State}; +use axum::http::StatusCode; +use axum::response::{IntoResponse, Json}; +use rustc_hash::FxHashMap; +use serde::{Deserialize, Serialize}; +use tracing::info; + +use crate::consts::NAN_U16; +use crate::data::travel_time::TravelData; +use crate::parsing::{parse_filters, require_bounds}; +use crate::routes::travel_time::parse_optional_travel; +use crate::state::SharedState; + +#[derive(Deserialize)] +pub struct FilterCountsParams { + bounds: Option, + filters: Option, + travel: Option, +} + +#[derive(Serialize)] +pub struct FilterCountsResponse { + total: u32, + impacts: FxHashMap, +} + +pub async fn get_filter_counts( + State(shared): State>, + Query(params): Query, +) -> Result, axum::response::Response> { + let state = shared.load_state(); + + let (south, west, north, east) = + require_bounds(params.bounds).map_err(IntoResponse::into_response)?; + + let quant = state.data.quant_ref(); + let (parsed_filters, parsed_enum_filters) = parse_filters( + params.filters.as_deref(), + &state.feature_name_to_index, + &state.data.enum_values, + &quant, + ) + .map_err(|err| (StatusCode::BAD_REQUEST, err).into_response())?; + + let travel_entries = parse_optional_travel(params.travel.as_deref()) + .map_err(|err| (StatusCode::BAD_REQUEST, err).into_response())?; + + let num_regular = parsed_filters.len() + parsed_enum_filters.len(); + // Only travel entries with a filter range count as filters for impact tracking + let travel_filter_indices: Vec = travel_entries + .iter() + .enumerate() + .filter(|(_, e)| e.filter_min.is_some()) + .map(|(i, _)| i) + .collect(); + let num_total_filters = num_regular + travel_filter_indices.len(); + + if num_total_filters == 0 { + return Ok(Json(FilterCountsResponse { + total: 0, + impacts: FxHashMap::default(), + })); + } + + let filters_str = params.filters; + + let response = tokio::task::spawn_blocking(move || -> Result { + let t0 = std::time::Instant::now(); + let num_features = state.data.num_features; + let feature_data = &state.data.feature_data; + + // Load travel time data + let travel_data: Vec = travel_entries + .iter() + .map(|entry| { + state + .travel_time_store + .get(&entry.mode, &entry.slug) + .map_err(|err| format!("Failed to load travel data: {}", err)) + }) + .collect::, _>>()?; + + let has_travel = !travel_entries.is_empty(); + let (pc_interner, pc_keys) = state.data.postcode_parts(); + + let rows = state.grid.query(south, west, north, east); + let row_count = rows.len(); + + let mut total_passing: u32 = 0; + let mut impacts = vec![0u32; num_total_filters]; + + for row_idx in rows { + let row = row_idx as usize; + let base = row * num_features; + let mut fail_count: u32 = 0; + let mut fail_index: usize = 0; + + // Test numeric filters + for (i, f) in parsed_filters.iter().enumerate() { + let raw = feature_data[base + f.feat_idx]; + if raw == NAN_U16 || raw < f.min_u16 || raw > f.max_u16 { + fail_count += 1; + fail_index = i; + if fail_count > 1 { + break; + } + } + } + + // Test enum filters + if fail_count <= 1 { + for (i, f) in parsed_enum_filters.iter().enumerate() { + let raw = feature_data[base + f.feat_idx]; + if raw == NAN_U16 || !f.allowed.contains(&raw) { + fail_count += 1; + fail_index = parsed_filters.len() + i; + if fail_count > 1 { + break; + } + } + } + } + + // Test travel time filters + if fail_count <= 1 && has_travel { + let postcode = pc_interner.resolve(&pc_keys[row]); + for (slot, &ti) in travel_filter_indices.iter().enumerate() { + let entry = &travel_entries[ti]; + let minutes = travel_data[ti].get(postcode).map(|r| { + if entry.use_best { + r.best_minutes.unwrap_or(r.minutes) + } else { + r.minutes + } + }); + let passes = match (minutes, entry.filter_min, entry.filter_max) { + (Some(mins), Some(fmin), Some(fmax)) => { + (mins as f32) >= fmin && (mins as f32) <= fmax + } + (None, Some(_), Some(_)) => false, + _ => true, + }; + if !passes { + fail_count += 1; + fail_index = num_regular + slot; + if fail_count > 1 { + break; + } + } + } + } + + match fail_count { + 0 => total_passing += 1, + 1 => impacts[fail_index] += 1, + _ => {} + } + } + + // Map filter indices back to feature/travel names + let mut impact_map: FxHashMap = FxHashMap::default(); + for (i, &count) in impacts.iter().enumerate() { + if count == 0 { + continue; + } + let name = if i < parsed_filters.len() { + state.data.feature_names[parsed_filters[i].feat_idx].clone() + } else if i < num_regular { + let ei = i - parsed_filters.len(); + state.data.feature_names[parsed_enum_filters[ei].feat_idx].clone() + } else { + let slot = i - num_regular; + let ti = travel_filter_indices[slot]; + let e = &travel_entries[ti]; + format!("tt_{}_{}", e.mode, e.slug) + }; + impact_map.insert(name, count); + } + + let elapsed = t0.elapsed(); + info!( + rows = row_count, + filters = num_total_filters, + travel = travel_filter_indices.len(), + total = total_passing, + filters_raw = filters_str.as_deref().unwrap_or("-"), + ms = format_args!("{:.1}", elapsed.as_secs_f64() * 1000.0), + "GET /api/filter-counts" + ); + + Ok(FilterCountsResponse { + total: total_passing, + impacts: impact_map, + }) + }) + .await + .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response())? + .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err).into_response())?; + + Ok(Json(response)) +} diff --git a/server-rs/src/routes/reload.rs b/server-rs/src/routes/reload.rs index 0d12069..367d61a 100644 --- a/server-rs/src/routes/reload.rs +++ b/server-rs/src/routes/reload.rs @@ -144,6 +144,7 @@ fn rebuild_data(shared: &SharedState, start: Instant) -> anyhow::Result<(usize, poi_grid: Arc::clone(&old.poi_grid), place_data: Arc::clone(&old.place_data), postcode_data: Arc::clone(&old.postcode_data), + outcode_data: Arc::clone(&old.outcode_data), poi_category_groups: Arc::clone(&old.poi_category_groups), travel_time_store: Arc::clone(&old.travel_time_store), token_cache: Arc::clone(&old.token_cache),