.
This commit is contained in:
parent
a9bad79339
commit
701c17a703
14 changed files with 3243 additions and 2974 deletions
|
|
@ -189,8 +189,8 @@ $(GREENSPACE): $(PBF)
|
||||||
$(OS_GREENSPACE):
|
$(OS_GREENSPACE):
|
||||||
uv run python -m pipeline.download.os_greenspace --output $@
|
uv run python -m pipeline.download.os_greenspace --output $@
|
||||||
|
|
||||||
$(PLACES): $(PBF) $(ENGLAND_BOUNDARY)
|
$(PLACES): $(PBF) $(ENGLAND_BOUNDARY) $(NAPTAN)
|
||||||
uv run python -m pipeline.download.places --output $@ --pbf $(PBF) --boundary $(ENGLAND_BOUNDARY)
|
uv run python -m pipeline.download.places --output $@ --pbf $(PBF) --boundary $(ENGLAND_BOUNDARY) --naptan $(NAPTAN)
|
||||||
|
|
||||||
$(LSOA_POP):
|
$(LSOA_POP):
|
||||||
uv run python -m pipeline.download.lsoa_population --output $@
|
uv run python -m pipeline.download.lsoa_population --output $@
|
||||||
|
|
|
||||||
54
frontend/eslint.config.cjs
Normal file
54
frontend/eslint.config.cjs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
const js = require('@eslint/js');
|
||||||
|
const tsParser = require('@typescript-eslint/parser');
|
||||||
|
const tsPlugin = require('@typescript-eslint/eslint-plugin');
|
||||||
|
const globals = require('globals');
|
||||||
|
const reactPlugin = require('eslint-plugin-react');
|
||||||
|
const reactHooksPlugin = require('eslint-plugin-react-hooks');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
ignores: ['dist/**', 'node_modules/**'],
|
||||||
|
linterOptions: {
|
||||||
|
reportUnusedDisableDirectives: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['src/**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.es2021,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsPlugin,
|
||||||
|
react: reactPlugin,
|
||||||
|
'react-hooks': reactHooksPlugin,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactPlugin.configs.recommended.rules,
|
||||||
|
...tsPlugin.configs.recommended.rules,
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
'no-undef': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
5948
frontend/package-lock.json
generated
5948
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,62 +15,68 @@
|
||||||
"check:i18n": "node scripts/check-translations.mjs"
|
"check:i18n": "node scripts/check-translations.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@deck.gl/core": "^9.0.0",
|
"@deck.gl/core": "^9.3.2",
|
||||||
"@deck.gl/geo-layers": "^9.0.0",
|
"@deck.gl/extensions": "^9.3.2",
|
||||||
"@deck.gl/layers": "^9.0.0",
|
"@deck.gl/geo-layers": "^9.3.2",
|
||||||
"@deck.gl/mapbox": "^9.2.6",
|
"@deck.gl/layers": "^9.3.2",
|
||||||
"@deck.gl/react": "^9.0.0",
|
"@deck.gl/mapbox": "^9.3.2",
|
||||||
"@plausible-analytics/tracker": "^0.4.4",
|
"@deck.gl/mesh-layers": "^9.3.2",
|
||||||
"@protomaps/basemaps": "^5.7.0",
|
"@deck.gl/react": "^9.3.2",
|
||||||
"@radix-ui/react-select": "^2.0.0",
|
"@deck.gl/widgets": "^9.3.2",
|
||||||
"@radix-ui/react-slider": "^1.1.0",
|
"@plausible-analytics/tracker": "^0.4.5",
|
||||||
|
"@protomaps/basemaps": "^5.7.2",
|
||||||
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
|
"@radix-ui/react-slider": "^1.3.6",
|
||||||
"@types/supercluster": "^7.1.3",
|
"@types/supercluster": "^7.1.3",
|
||||||
"i18next": "^26.0.3",
|
"i18next": "^26.0.10",
|
||||||
"maplibre-gl": "^4.0.0",
|
"maplibre-gl": "^5.24.0",
|
||||||
"pocketbase": "^0.26.8",
|
"pocketbase": "^0.26.8",
|
||||||
"react": "^18.2.0",
|
"react": "^19.2.6",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^19.2.6",
|
||||||
"react-i18next": "^17.0.2",
|
"react-i18next": "^17.0.7",
|
||||||
"react-joyride": "^2.9.3",
|
"react-joyride": "^3.1.0",
|
||||||
"react-map-gl": "^7.1.0",
|
"react-map-gl": "^8.1.1",
|
||||||
"supercluster": "^8.0.1"
|
"supercluster": "^8.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.0",
|
"@babel/preset-env": "^7.29.5",
|
||||||
"@babel/preset-react": "^7.28.5",
|
"@babel/preset-react": "^7.28.5",
|
||||||
"@babel/preset-typescript": "^7.28.5",
|
"@babel/preset-typescript": "^7.28.5",
|
||||||
|
"@eslint/js": "^9.39.4",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||||
|
"@tailwindcss/postcss": "^4.2.4",
|
||||||
"@testing-library/react": "^16.3.2",
|
"@testing-library/react": "^16.3.2",
|
||||||
"@types/react": "^18.2.0",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^18.2.0",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.59.2",
|
||||||
"@typescript-eslint/parser": "^7.0.0",
|
"@typescript-eslint/parser": "^8.59.2",
|
||||||
"autoprefixer": "^10.4.0",
|
"autoprefixer": "^10.5.0",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.1.1",
|
||||||
"copy-webpack-plugin": "^13.0.1",
|
"copy-webpack-plugin": "^14.0.0",
|
||||||
"css-loader": "^7.0.0",
|
"css-loader": "^7.1.4",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.39.4",
|
||||||
"eslint-plugin-react": "^7.34.0",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^7.1.1",
|
||||||
"favicons": "^7.2.0",
|
"favicons": "^7.2.0",
|
||||||
"favicons-webpack-plugin": "^6.0.1",
|
"favicons-webpack-plugin": "^6.0.1",
|
||||||
"html-webpack-plugin": "^5.6.0",
|
"globals": "^17.6.0",
|
||||||
|
"html-webpack-plugin": "^5.6.7",
|
||||||
"jsdom": "^29.1.1",
|
"jsdom": "^29.1.1",
|
||||||
"mini-css-extract-plugin": "^2.9.0",
|
"mini-css-extract-plugin": "^2.10.2",
|
||||||
"postcss": "^8.4.0",
|
"postcss": "^8.5.14",
|
||||||
"postcss-loader": "^8.0.0",
|
"postcss-loader": "^8.2.1",
|
||||||
"prettier": "^3.2.0",
|
"prettier": "^3.8.3",
|
||||||
"puppeteer": "^24.0.0",
|
"puppeteer": "^24.43.0",
|
||||||
"react-refresh": "^0.18.0",
|
"react-refresh": "^0.18.0",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^4.2.4",
|
||||||
"ts-loader": "^9.5.0",
|
"ts-loader": "^9.5.7",
|
||||||
"typescript": "^5.4.0",
|
"typescript": "^6.0.3",
|
||||||
"vitest": "^4.1.5",
|
"vitest": "^4.1.5",
|
||||||
"webpack": "^5.90.0",
|
"webpack": "^5.106.2",
|
||||||
"webpack-cli": "^5.1.0",
|
"webpack-cli": "^7.0.2",
|
||||||
"webpack-dev-server": "^5.0.0"
|
"webpack-dev-server": "^5.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
'@tailwindcss/postcss': {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
1
frontend/src/global.d.ts
vendored
Normal file
1
frontend/src/global.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
declare module '*.css';
|
||||||
|
|
@ -261,12 +261,13 @@ const de: Translations = {
|
||||||
// ── Area Pane ──────────────────────────────────────
|
// ── Area Pane ──────────────────────────────────────
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: 'Gebietsstatistiken',
|
areaStatistics: 'Gebietsstatistiken',
|
||||||
|
areaOverview: 'Übersicht',
|
||||||
statsFor: 'Statistiken für alle Immobilien in diesem {{type}}',
|
statsFor: 'Statistiken für alle Immobilien in diesem {{type}}',
|
||||||
matchingFilters: ', die allen aktiven Filtern entsprechen',
|
matchingFilters: ', die allen aktiven Filtern entsprechen',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'Filter im linken Bereich werden hier angewendet: Werte, Diagramme und Immobilienzahlen nutzen die {{count}} aktiven Filter.',
|
'Filter werden hier angewendet: Werte, Diagramme und Immobilienzahlen nutzen die {{count}} aktiven Filter.',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats:
|
||||||
'Filter im linken Bereich aktualisieren diesen Bereich: Fügen Sie Filter hinzu, um diese Werte für passende Immobilien neu zu berechnen.',
|
'Fügen Sie Filter hinzu, um diese Werte für passende Immobilien neu zu berechnen.',
|
||||||
noFilteredMatches: 'Keine Immobilien in diesem Gebiet entsprechen Ihren Filtern.',
|
noFilteredMatches: 'Keine Immobilien in diesem Gebiet entsprechen Ihren Filtern.',
|
||||||
unfilteredAreaCount:
|
unfilteredAreaCount:
|
||||||
'{{count}} Immobilien gibt es hier vor den Filtern; der Ort ist gültig, wird aber herausgefiltert.',
|
'{{count}} Immobilien gibt es hier vor den Filtern; der Ort ist gültig, wird aber herausgefiltert.',
|
||||||
|
|
@ -274,6 +275,7 @@ const de: Translations = {
|
||||||
'In diesem ausgewählten Gebiet wurden auch vor den Filtern keine Immobilien gefunden.',
|
'In diesem ausgewählten Gebiet wurden auch vor den Filtern keine Immobilien gefunden.',
|
||||||
relaxFiltersHint: 'Lockern oder löschen Sie Filter, um Immobilien in diesem Gebiet zu sehen.',
|
relaxFiltersHint: 'Lockern oder löschen Sie Filter, um Immobilien in diesem Gebiet zu sehen.',
|
||||||
viewProperties: '{{count}} Immobilien ansehen',
|
viewProperties: '{{count}} Immobilien ansehen',
|
||||||
|
viewPropertiesShort: 'Immobilien ansehen',
|
||||||
priceHistory: 'Preisentwicklung',
|
priceHistory: 'Preisentwicklung',
|
||||||
journeysFrom: 'Verbindungen ab {{label}}',
|
journeysFrom: 'Verbindungen ab {{label}}',
|
||||||
to: 'Nach {{destination}}',
|
to: 'Nach {{destination}}',
|
||||||
|
|
@ -497,7 +499,7 @@ const de: Translations = {
|
||||||
dsIodName: 'English Indices of Deprivation 2025',
|
dsIodName: 'English Indices of Deprivation 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse:
|
dsIodUse:
|
||||||
'Relative Benachteiligungswerte für Einkommen, Beschäftigung, Bildung, Gesundheit, Kriminalität und Wohnumfeld für jedes Viertel in England.',
|
'Nationale Benachteiligungsperzentile für Einkommen, Beschäftigung, Bildung, Gesundheit, Kriminalität und Wohnumfeld für jedes Viertel in England.',
|
||||||
dsEthnicityName: 'Bevölkerung nach Ethnie (Zensus 2021)',
|
dsEthnicityName: 'Bevölkerung nach Ethnie (Zensus 2021)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -914,12 +916,15 @@ const de: Translations = {
|
||||||
// ─ POI group names ─
|
// ─ POI group names ─
|
||||||
'Public Transport': 'Öffentlicher Nahverkehr',
|
'Public Transport': 'Öffentlicher Nahverkehr',
|
||||||
Leisure: 'Freizeit',
|
Leisure: 'Freizeit',
|
||||||
|
'Food & Drink': 'Essen & Trinken',
|
||||||
|
'Green Space': 'Grünflächen',
|
||||||
Health: 'Gesundheit',
|
Health: 'Gesundheit',
|
||||||
'Emergency Services': 'Rettungsdienste',
|
'Emergency Services': 'Rettungsdienste',
|
||||||
Groceries: 'Lebensmittel',
|
Groceries: 'Lebensmittel',
|
||||||
'Local Businesses': 'Lokale Geschäfte',
|
'Local Businesses': 'Lokale Geschäfte',
|
||||||
Culture: 'Kultur',
|
Culture: 'Kultur',
|
||||||
Services: 'Dienstleistungen',
|
Services: 'Dienstleistungen',
|
||||||
|
Practical: 'Praktisches',
|
||||||
Shops: 'Geschäfte',
|
Shops: 'Geschäfte',
|
||||||
|
|
||||||
// ─ POI categories ─
|
// ─ POI categories ─
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ const en = {
|
||||||
generatingFilters: 'Generating filters...',
|
generatingFilters: 'Generating filters...',
|
||||||
refiningResults: 'Refining results...',
|
refiningResults: 'Refining results...',
|
||||||
weeklyLimitReached:
|
weeklyLimitReached:
|
||||||
'You’ve reached the weekly AI usage limit. It will reset automatically next week.',
|
'You’ve reached the weekly AI usage limit. It’ll reset automatically next week.',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Map Legend ─────────────────────────────────────
|
// ── Map Legend ─────────────────────────────────────
|
||||||
|
|
@ -257,18 +257,19 @@ const en = {
|
||||||
// ── Area Pane ──────────────────────────────────────
|
// ── Area Pane ──────────────────────────────────────
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: 'Area Statistics',
|
areaStatistics: 'Area Statistics',
|
||||||
|
areaOverview: 'Overview',
|
||||||
statsFor: 'Stats for all properties in this {{type}}',
|
statsFor: 'Stats for all properties in this {{type}}',
|
||||||
matchingFilters: ' matching all active filters',
|
matchingFilters: ' matching all active filters',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'Left-pane filters are applied here: values, charts, and property counts use the {{count}} active filters.',
|
'Filters are applied here: values, charts, and property counts use the {{count}} active filters.',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats: 'Add filters to recalculate these values for matching properties.',
|
||||||
'Left-pane filters update this pane: add filters to recalculate these values for matching properties.',
|
|
||||||
noFilteredMatches: 'No properties match your filters in this area.',
|
noFilteredMatches: 'No properties match your filters in this area.',
|
||||||
unfilteredAreaCount:
|
unfilteredAreaCount:
|
||||||
'{{count}} properties exist here before filters, so the location is valid but filtered out.',
|
'{{count}} properties exist here before filters, so the location is valid but filtered out.',
|
||||||
noUnfilteredAreaProperties: 'No properties were found in this selected area before filters.',
|
noUnfilteredAreaProperties: 'No properties were found in this selected area before filters.',
|
||||||
relaxFiltersHint: 'Relax or clear filters to see properties in this area.',
|
relaxFiltersHint: 'Relax or clear filters to see properties in this area.',
|
||||||
viewProperties: 'View {{count}} Properties',
|
viewProperties: 'View {{count}} Properties',
|
||||||
|
viewPropertiesShort: 'View properties',
|
||||||
priceHistory: 'Price History',
|
priceHistory: 'Price History',
|
||||||
journeysFrom: 'Journeys from {{label}}',
|
journeysFrom: 'Journeys from {{label}}',
|
||||||
to: 'To {{destination}}',
|
to: 'To {{destination}}',
|
||||||
|
|
@ -321,7 +322,7 @@ const en = {
|
||||||
searchLabel: 'Search places or postcodes',
|
searchLabel: 'Search places or postcodes',
|
||||||
locateMe: 'Go to my location',
|
locateMe: 'Go to my location',
|
||||||
geolocationUnsupported: 'Geolocation not supported by your browser',
|
geolocationUnsupported: 'Geolocation not supported by your browser',
|
||||||
geolocationFailed: 'Could not determine your location',
|
geolocationFailed: 'Couldn’t determine your location',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Mobile Drawer ──────────────────────────────────
|
// ── Mobile Drawer ──────────────────────────────────
|
||||||
|
|
@ -338,7 +339,7 @@ const en = {
|
||||||
heroSubtitle:
|
heroSubtitle:
|
||||||
'From London boroughs to commuter towns and regional cities, England has too many places to research one by one.',
|
'From London boroughs to commuter towns and regional cities, England has too many places to research one by one.',
|
||||||
heroDescription:
|
heroDescription:
|
||||||
'Set your budget, commute, schools, safety, noise, broadband, and lifestyle needs. Perfect Postcode scans England’s postcodes and reveals the places that actually fit, including areas you would never have typed into a listing portal.',
|
'Set your budget, commute, schools, safety, noise, broadband, and lifestyle needs. Perfect Postcode scans England’s postcodes and reveals the places that actually fit, including areas you’d never have typed into a listing portal.',
|
||||||
exploreTheMap: 'Find my matching postcodes',
|
exploreTheMap: 'Find my matching postcodes',
|
||||||
seeTheDifference: 'See how it works',
|
seeTheDifference: 'See how it works',
|
||||||
showcaseHeader: 'How it works',
|
showcaseHeader: 'How it works',
|
||||||
|
|
@ -356,7 +357,7 @@ const en = {
|
||||||
showcaseStep1Chip3: 'Under £500k',
|
showcaseStep1Chip3: 'Under £500k',
|
||||||
showcaseStep1VennCenter: 'Postcodes that meet all three',
|
showcaseStep1VennCenter: 'Postcodes that meet all three',
|
||||||
showcaseStep2Tab: 'Match',
|
showcaseStep2Tab: 'Match',
|
||||||
showcaseStep2Title: 'Let the map surface places you would not have typed',
|
showcaseStep2Title: 'Let the map surface places you wouldn’t have typed',
|
||||||
showcaseStep2Body:
|
showcaseStep2Body:
|
||||||
'Scan England by fit instead of starting from familiar area names. Hidden pockets become visible before listing portals narrow your imagination.',
|
'Scan England by fit instead of starting from familiar area names. Hidden pockets become visible before listing portals narrow your imagination.',
|
||||||
showcaseStep2Region: 'Greater London',
|
showcaseStep2Region: 'Greater London',
|
||||||
|
|
@ -393,9 +394,9 @@ const en = {
|
||||||
statPostcodeInEngland: 'postcode in England',
|
statPostcodeInEngland: 'postcode in England',
|
||||||
ourPhilosophy: 'Start with your life, not a postcode',
|
ourPhilosophy: 'Start with your life, not a postcode',
|
||||||
philosophyP1:
|
philosophyP1:
|
||||||
'Most property sites ask where you want to live. In London that is painfully hard, but the same problem shows up across England: buyers choose from the few places they know, then cross-check commute tools, Ofsted, police data, Street View, broadband checkers, and sold prices in separate tabs.',
|
'Most property sites ask where you want to live. In London that’s painfully hard, but the same problem shows up across England: buyers choose from the few places they know, then cross-check commute tools, Ofsted, police data, Street View, broadband checkers, and sold prices in separate tabs.',
|
||||||
philosophyP2:
|
philosophyP2:
|
||||||
'Perfect Postcode flips the search. Tell the map what matters and it shows the postcodes that qualify, with evidence for why they are worth inspecting. Data first, then go test the vibe.',
|
'Perfect Postcode flips the search. Tell the map what matters and it shows the postcodes that qualify, with evidence for why they’re worth inspecting. Data first, then go test the vibe.',
|
||||||
streetTitle: 'Places change street by street',
|
streetTitle: 'Places change street by street',
|
||||||
streetIntro:
|
streetIntro:
|
||||||
'Broad area names hide the details that matter: the station side, the road noise, the school mix, the exact commute, and what similar homes actually sold for.',
|
'Broad area names hide the details that matter: the station side, the road noise, the school mix, the exact commute, and what similar homes actually sold for.',
|
||||||
|
|
@ -490,7 +491,7 @@ const en = {
|
||||||
dsIodName: 'English Indices of Deprivation 2025',
|
dsIodName: 'English Indices of Deprivation 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse:
|
dsIodUse:
|
||||||
'Relative deprivation scores across income, employment, education, health, crime, and living environment for every neighbourhood in England.',
|
'National deprivation percentiles across income, employment, education, health, crime, and living environment for every neighbourhood in England.',
|
||||||
dsEthnicityName: 'Population by Ethnicity (2021 Census)',
|
dsEthnicityName: 'Population by Ethnicity (2021 Census)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -554,8 +555,8 @@ const en = {
|
||||||
// FAQ items — Finding Your Area
|
// FAQ items — Finding Your Area
|
||||||
faqFinding1Q: 'Where should I look once the obvious areas are too expensive?',
|
faqFinding1Q: 'Where should I look once the obvious areas are too expensive?',
|
||||||
faqFinding1A:
|
faqFinding1A:
|
||||||
'Start with the things you cannot compromise on: budget, home type, space, commute, schools, safety, noise, broadband, parks, and anything else that matters. The map hides places that do not fit, so less obvious areas can surface before you start scrolling listings.',
|
'Start with the things you can’t compromise on: budget, home type, space, commute, schools, safety, noise, broadband, parks, and anything else that matters. The map hides places that don’t fit, so less obvious areas can surface before you start scrolling listings.',
|
||||||
faqFinding2Q: 'How do I find good postcodes in places I do not know well?',
|
faqFinding2Q: 'How do I find good postcodes in places I don’t know well?',
|
||||||
faqFinding2A:
|
faqFinding2A:
|
||||||
'Set your must-haves across the whole map, then look closely at the groups of places that remain. You can compare unfamiliar postcodes by commute, sold prices, schools, crime, broadband, noise, and shops or parks nearby instead of relying on reputation.',
|
'Set your must-haves across the whole map, then look closely at the groups of places that remain. You can compare unfamiliar postcodes by commute, sold prices, schools, crime, broadband, noise, and shops or parks nearby instead of relying on reputation.',
|
||||||
faqFinding3Q: 'What should I do when my search returns too many or too few areas?',
|
faqFinding3Q: 'What should I do when my search returns too many or too few areas?',
|
||||||
|
|
@ -567,14 +568,14 @@ const en = {
|
||||||
'Travel times are calculated in advance for each saved destination. We work out which postcodes can reach that destination by car, bike, walking, or public transport, then save those results so the map can respond quickly while you filter.',
|
'Travel times are calculated in advance for each saved destination. We work out which postcodes can reach that destination by car, bike, walking, or public transport, then save those results so the map can respond quickly while you filter.',
|
||||||
faqCommute2Q: 'What should I know about the travel-time numbers?',
|
faqCommute2Q: 'What should I know about the travel-time numbers?',
|
||||||
faqCommute2A:
|
faqCommute2A:
|
||||||
'Public transport times are based on a weekday morning commute, using departures between 07:30 and 08:30. The normal setting shows a typical journey in that window. These are planning estimates, so they do not include live delays, traffic, or last-minute platform changes.',
|
'Public transport times are based on a weekday morning commute, using departures between 07:30 and 08:30. The normal setting shows a typical journey in that window. These are planning estimates, so they don’t include live delays, traffic, or last-minute platform changes.',
|
||||||
faqCommute3Q: 'When should I use the Best case button?',
|
faqCommute3Q: 'When should I use the Best case button?',
|
||||||
faqCommute3A:
|
faqCommute3A:
|
||||||
'Use the Best case button on public-transport searches when you want to see the journey with a well-timed departure and good connections. Leave it off for the everyday comparison, because the normal setting is closer to what you should expect most days.',
|
'Use the Best case button on public-transport searches when you want to see the journey with a well-timed departure and good connections. Leave it off for the everyday comparison, because the normal setting is closer to what you should expect most days.',
|
||||||
// FAQ items — Budget and Value
|
// FAQ items — Budget and Value
|
||||||
faqBudget1Q: 'How do you estimate current property prices?',
|
faqBudget1Q: 'How do you estimate current property prices?',
|
||||||
faqBudget1A:
|
faqBudget1A:
|
||||||
'The estimate starts with the home’s last recorded sale price from HM Land Registry. We bring that sale up to today’s market by looking at how similar homes have changed in value over time, especially homes of the same type nearby. Where there are fewer local sales, the estimate leans more on wider area trends. It is then checked against nearby recent sales and floor area so the result is useful for comparison.',
|
'The estimate starts with the home’s last recorded sale price from HM Land Registry. We bring that sale up to today’s market by looking at how similar homes have changed in value over time, especially homes of the same type nearby. Where there are fewer local sales, the estimate leans more on wider area trends. It’s then checked against nearby recent sales and floor area so the result is useful for comparison.',
|
||||||
faqBudget2Q: 'Why use estimated current price instead of last sold price?',
|
faqBudget2Q: 'Why use estimated current price instead of last sold price?',
|
||||||
faqBudget2A:
|
faqBudget2A:
|
||||||
'Last sold price can be years or decades old, while asking prices only cover homes listed today. Estimated current price puts older sales into today’s market, so you can compare more homes and spot areas that may offer better value before listings appear. Treat it as a guide for shortlisting, not a bank valuation.',
|
'Last sold price can be years or decades old, while asking prices only cover homes listed today. Estimated current price puts older sales into today’s market, so you can compare more homes and spot areas that may offer better value before listings appear. Treat it as a guide for shortlisting, not a bank valuation.',
|
||||||
|
|
@ -584,12 +585,12 @@ const en = {
|
||||||
'Crime is broken down by type, including violence, burglary, robbery, vehicle crime, antisocial behaviour, shoplifting, drugs, and public order. You can focus on the risks that matter to you instead of relying on one vague safety score.',
|
'Crime is broken down by type, including violence, burglary, robbery, vehicle crime, antisocial behaviour, shoplifting, drugs, and public order. You can focus on the risks that matter to you instead of relying on one vague safety score.',
|
||||||
faqSafety2Q: 'What should I check before viewing an unfamiliar street?',
|
faqSafety2Q: 'What should I check before viewing an unfamiliar street?',
|
||||||
faqSafety2A:
|
faqSafety2A:
|
||||||
'Check crime, road noise, broadband, parks, food shops, schools, and commute before you book. Listing photos are useful, but they should not be the first time you learn what the street is like.',
|
'Check crime, road noise, broadband, parks, food shops, schools, and commute before you book. Listing photos are useful, but they shouldn’t be the first time you learn what the street is like.',
|
||||||
// FAQ items — Families and Schools
|
// FAQ items — Families and Schools
|
||||||
faqFamilies1Q: 'Which areas have the right mix of schools, space, safety, and commute?',
|
faqFamilies1Q: 'Which areas have the right mix of schools, space, safety, and commute?',
|
||||||
faqFamilies1A:
|
faqFamilies1A:
|
||||||
'Put school ratings, crime, parks, commute, space, home type, and budget on one map. The result is a practical family shortlist instead of a pile of separate school, crime, listing, and transport searches.',
|
'Put school ratings, crime, parks, commute, space, home type, and budget on one map. The result is a practical family shortlist instead of a pile of separate school, crime, listing, and transport searches.',
|
||||||
faqFamilies2Q: 'Does this prove I am inside a school catchment?',
|
faqFamilies2Q: 'Does this prove I’m inside a school catchment?',
|
||||||
faqFamilies2A:
|
faqFamilies2A:
|
||||||
'No. We show nearby school quality and local education data, but admissions areas and priority rules can change. Use Perfect Postcode to shortlist places, then check catchments and admissions with the school or local council.',
|
'No. We show nearby school quality and local education data, but admissions areas and priority rules can change. Use Perfect Postcode to shortlist places, then check catchments and admissions with the school or local council.',
|
||||||
// FAQ items — Environment and Quality of Life
|
// FAQ items — Environment and Quality of Life
|
||||||
|
|
@ -601,14 +602,14 @@ const en = {
|
||||||
'Not today. We show things such as road noise, energy rating, building age, and the local environment around the postcode. Flood risk, legal issues, structural issues, mortgage concerns, and survey findings still need to be checked separately before you buy.',
|
'Not today. We show things such as road noise, energy rating, building age, and the local environment around the postcode. Flood risk, legal issues, structural issues, mortgage concerns, and survey findings still need to be checked separately before you buy.',
|
||||||
faqEnv3Q: 'What running-cost checks can I do before viewing?',
|
faqEnv3Q: 'What running-cost checks can I do before viewing?',
|
||||||
faqEnv3A:
|
faqEnv3A:
|
||||||
'You can check energy rating, floor area, building age, council tax area, broadband, and noise before viewing. It will not predict your exact bills, but it helps you avoid obvious mismatches early.',
|
'You can check energy rating, floor area, building age, council tax area, broadband, and noise before viewing. It won’t predict your exact bills, but it helps you avoid obvious mismatches early.',
|
||||||
// FAQ items — Listing Portals and Due Diligence
|
// FAQ items — Listing Portals and Due Diligence
|
||||||
faqDueDiligence1Q: 'Should I use this before or after checking Rightmove?',
|
faqDueDiligence1Q: 'Should I use this before or after checking Rightmove?',
|
||||||
faqDueDiligence1A:
|
faqDueDiligence1A:
|
||||||
'Use Perfect Postcode before and alongside listing sites. Rightmove, Zoopla, and OnTheMarket are still where you check what is for sale now, photos, agents, viewings, and alerts. Perfect Postcode helps you decide which postcodes are worth searching in the first place.',
|
'Use Perfect Postcode before and alongside listing sites. Rightmove, Zoopla, and OnTheMarket are still where you check what’s for sale now, photos, agents, viewings, and alerts. Perfect Postcode helps you decide which postcodes are worth searching in the first place.',
|
||||||
faqDueDiligence2Q: 'Why can’t I filter by garden, garage, or layout?',
|
faqDueDiligence2Q: 'Why can’t I filter by garden, garage, or layout?',
|
||||||
faqDueDiligence2A:
|
faqDueDiligence2A:
|
||||||
'Those details are not available consistently for every home. Perfect Postcode can filter by things such as floor area, home type, ownership type, energy rating, sold prices, and local area data. Gardens, garages, aspect, room layout, and estate-agent wording still need to be checked in the listing and at the viewing.',
|
'Those details aren’t available consistently for every home. Perfect Postcode can filter by things such as floor area, home type, ownership type, energy rating, sold prices, and local area data. Gardens, garages, aspect, room layout, and estate-agent wording still need to be checked in the listing and at the viewing.',
|
||||||
faqDueDiligence3Q: 'Do you track listing price cuts and time on market?',
|
faqDueDiligence3Q: 'Do you track listing price cuts and time on market?',
|
||||||
faqDueDiligence3A:
|
faqDueDiligence3A:
|
||||||
'Not currently. Perfect Postcode is built around sold prices, energy ratings, postcode data, travel times, and neighbourhood data rather than live listing changes. You can still use sale history, estimated current value, and price per square metre to judge whether an asking price looks stretched.',
|
'Not currently. Perfect Postcode is built around sold prices, energy ratings, postcode data, travel times, and neighbourhood data rather than live listing changes. You can still use sale history, estimated current value, and price per square metre to judge whether an asking price looks stretched.',
|
||||||
|
|
@ -618,9 +619,9 @@ const en = {
|
||||||
// FAQ items — Privacy and Data Protection
|
// FAQ items — Privacy and Data Protection
|
||||||
faqPrivacy1Q: 'Do you store personal data about me?',
|
faqPrivacy1Q: 'Do you store personal data about me?',
|
||||||
faqPrivacy1A:
|
faqPrivacy1A:
|
||||||
'The property and neighbourhood data does not contain your personal details. If you create an account, we store only what is needed to run the service, such as your email address, access status, newsletter choice, saved searches, saved properties, and payment records handled by Stripe. We handle account data under UK privacy law.',
|
'The property and neighbourhood data doesn’t contain your personal details. If you create an account, we store only what’s needed to run the service, such as your email address, access status, newsletter choice, saved searches, saved properties, and payment records handled by Stripe. We handle account data under UK privacy law.',
|
||||||
// FAQ items — Why Perfect Postcode
|
// FAQ items — Why Perfect Postcode
|
||||||
faqWhy1Q: 'What does this show that listing portals usually do not?',
|
faqWhy1Q: 'What does this show that listing portals usually don’t?',
|
||||||
faqWhy1A:
|
faqWhy1A:
|
||||||
'Listing sites start from homes that are for sale right now. Perfect Postcode starts from the places that fit your life and budget, using sold prices, space, commute, schools, crime, noise, broadband, energy rating, ownership type, and local amenities before you open the listings.',
|
'Listing sites start from homes that are for sale right now. Perfect Postcode starts from the places that fit your life and budget, using sold prices, space, commute, schools, crime, noise, broadband, energy rating, ownership type, and local amenities before you open the listings.',
|
||||||
faqWhy2Q: 'How much manual research does this save?',
|
faqWhy2Q: 'How much manual research does this save?',
|
||||||
|
|
@ -628,14 +629,14 @@ const en = {
|
||||||
'You could do the research yourself, but it means checking sold prices, energy ratings, crime, schools, broadband, local facts, environment details, travel times, and maps one postcode at a time. Perfect Postcode puts those sources in one searchable map for England.',
|
'You could do the research yourself, but it means checking sold prices, energy ratings, crime, schools, broadband, local facts, environment details, travel times, and maps one postcode at a time. Perfect Postcode puts those sources in one searchable map for England.',
|
||||||
faqWhy3Q: 'How reliable is the data?',
|
faqWhy3Q: 'How reliable is the data?',
|
||||||
faqWhy3A:
|
faqWhy3A:
|
||||||
'The main sources are official or widely used public data, including sold prices, energy ratings, local facts, school ratings, broadband, crime, environment, map, and street data. They are useful for shortlisting and comparison, but any purchase decision still needs current checks and expert advice where needed.',
|
'The main sources are official or widely used public data, including sold prices, energy ratings, local facts, school ratings, broadband, crime, environment, map, and street data. They’re useful for shortlisting and comparison, but any purchase decision still needs current checks and expert advice where needed.',
|
||||||
// FAQ items — Pricing and Access
|
// FAQ items — Pricing and Access
|
||||||
faqPricing1Q: 'Why pay when postcode reports are free?',
|
faqPricing1Q: 'Why pay when postcode reports are free?',
|
||||||
faqPricing1A:
|
faqPricing1A:
|
||||||
'Free postcode tools are useful once you already know what to check. Perfect Postcode is for scanning every postcode in England against your needs, combining filters, comparing options, saving searches, and exporting a shortlist before you spend weekends on viewings.',
|
'Free postcode tools are useful once you already know what to check. Perfect Postcode is for scanning every postcode in England against your needs, combining filters, comparing options, saving searches, and exporting a shortlist before you spend weekends on viewings.',
|
||||||
faqPricing2Q: 'What does lifetime access mean?',
|
faqPricing2Q: 'What does lifetime access mean?',
|
||||||
faqPricing2A:
|
faqPricing2A:
|
||||||
'Lifetime access means one payment gives your account ongoing access to the paid Perfect Postcode map for as long as the service runs. It is not a monthly or annual subscription, and normal data updates are included. You can use it during this search, come back later, and still have access if you move again.',
|
'Lifetime access means one payment gives your account ongoing access to the paid Perfect Postcode map for as long as the service runs. It isn’t a monthly or annual subscription, and normal data updates are included. You can use it during this search, come back later, and still have access if you move again.',
|
||||||
faqPricing3Q: 'What can I access on the free tier?',
|
faqPricing3Q: 'What can I access on the free tier?',
|
||||||
faqPricing3A:
|
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.',
|
'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.',
|
||||||
|
|
@ -649,7 +650,7 @@ const en = {
|
||||||
'Click the i info button next to a filter or feature to open a short explanation of what it means and how to read it. Some areas of the map, such as travel-time cards, also have their own info button.',
|
'Click the i info button next to a filter or feature to open a short explanation of what it means and how to read it. Some areas of the map, such as travel-time cards, also have their own info button.',
|
||||||
faqTips3Q: 'How do I refresh the map colours?',
|
faqTips3Q: 'How do I refresh the map colours?',
|
||||||
faqTips3A:
|
faqTips3A:
|
||||||
'When an eye preview is colouring the map, use Reset colour scale in the map legend to refresh the colours for the results you are looking at now. This is useful after moving the map, zooming, or changing filters.',
|
'When an eye preview is colouring the map, use Reset colour scale in the map legend to refresh the colours for the results you’re looking at now. This is useful after moving the map, zooming, or changing filters.',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Account Page ───────────────────────────────────
|
// ── Account Page ───────────────────────────────────
|
||||||
|
|
@ -676,11 +677,10 @@ const en = {
|
||||||
clickToRename: 'Click to rename',
|
clickToRename: 'Click to rename',
|
||||||
notesPlaceholder: 'Jot down your thoughts...',
|
notesPlaceholder: 'Jot down your thoughts...',
|
||||||
deleteSearch: 'Delete search',
|
deleteSearch: 'Delete search',
|
||||||
deleteSearchConfirm:
|
deleteSearchConfirm: 'Are you sure you want to delete this saved search? This can’t be undone.',
|
||||||
'Are you sure you want to delete this saved search? This cannot be undone.',
|
|
||||||
deleteProperty: 'Delete property',
|
deleteProperty: 'Delete property',
|
||||||
deletePropertyConfirm:
|
deletePropertyConfirm:
|
||||||
'Are you sure you want to delete this saved property? This cannot be undone.',
|
'Are you sure you want to delete this saved property? This can’t be undone.',
|
||||||
bed: 'bed',
|
bed: 'bed',
|
||||||
epc: 'EPC',
|
epc: 'EPC',
|
||||||
},
|
},
|
||||||
|
|
@ -710,7 +710,7 @@ const en = {
|
||||||
specialOffer: 'Special offer!',
|
specialOffer: 'Special offer!',
|
||||||
invitedByFree: '{{name}} has invited you to get free lifetime access.',
|
invitedByFree: '{{name}} has invited you to get free lifetime access.',
|
||||||
invitedByDiscount: '{{name}} has shared a 30% discount on lifetime access.',
|
invitedByDiscount: '{{name}} has shared a 30% discount on lifetime access.',
|
||||||
genericFreeInvite: 'You have been invited to get free lifetime access.',
|
genericFreeInvite: 'You’ve been invited to get free lifetime access.',
|
||||||
genericDiscount: 'A friend has shared a 30% discount on lifetime access.',
|
genericDiscount: 'A friend has shared a 30% discount on lifetime access.',
|
||||||
exploreEvery: 'Find postcodes that fit your life',
|
exploreEvery: 'Find postcodes that fit your life',
|
||||||
propertyInfo: 'Prices, commute, schools, crime, noise, broadband, EPC and more',
|
propertyInfo: 'Prices, commute, schools, crime, noise, broadband, EPC and more',
|
||||||
|
|
@ -900,12 +900,15 @@ const en = {
|
||||||
// ─ POI group names ─
|
// ─ POI group names ─
|
||||||
'Public Transport': 'Public Transport',
|
'Public Transport': 'Public Transport',
|
||||||
Leisure: 'Leisure',
|
Leisure: 'Leisure',
|
||||||
|
'Food & Drink': 'Food & Drink',
|
||||||
|
'Green Space': 'Green Space',
|
||||||
Health: 'Health',
|
Health: 'Health',
|
||||||
'Emergency Services': 'Emergency Services',
|
'Emergency Services': 'Emergency Services',
|
||||||
Groceries: 'Groceries',
|
Groceries: 'Groceries',
|
||||||
'Local Businesses': 'Local Businesses',
|
'Local Businesses': 'Local Businesses',
|
||||||
Culture: 'Culture',
|
Culture: 'Culture',
|
||||||
Services: 'Services',
|
Services: 'Services',
|
||||||
|
Practical: 'Practical',
|
||||||
Shops: 'Shops',
|
Shops: 'Shops',
|
||||||
|
|
||||||
// ─ POI categories ─
|
// ─ POI categories ─
|
||||||
|
|
|
||||||
|
|
@ -264,12 +264,13 @@ const fr: Translations = {
|
||||||
// ── Area Pane ──────────────────────────────────────
|
// ── Area Pane ──────────────────────────────────────
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: 'Statistiques de la zone',
|
areaStatistics: 'Statistiques de la zone',
|
||||||
|
areaOverview: 'Vue d’ensemble',
|
||||||
statsFor: 'Statistiques pour toutes les propriétés de ce/cette {{type}}',
|
statsFor: 'Statistiques pour toutes les propriétés de ce/cette {{type}}',
|
||||||
matchingFilters: ' correspondant à tous les filtres actifs',
|
matchingFilters: ' correspondant à tous les filtres actifs',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'Les filtres du panneau de gauche sont appliqués ici : valeurs, graphiques et nombres de propriétés utilisent les {{count}} filtres actifs.',
|
'Les filtres sont appliqués ici : valeurs, graphiques et nombres de propriétés utilisent les {{count}} filtres actifs.',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats:
|
||||||
'Les filtres du panneau de gauche mettent ce panneau à jour : ajoutez des filtres pour recalculer ces valeurs pour les propriétés correspondantes.',
|
'Ajoutez des filtres pour recalculer ces valeurs pour les propriétés correspondantes.',
|
||||||
noFilteredMatches: 'Aucune propriété de cette zone ne correspond à vos filtres.',
|
noFilteredMatches: 'Aucune propriété de cette zone ne correspond à vos filtres.',
|
||||||
unfilteredAreaCount:
|
unfilteredAreaCount:
|
||||||
'{{count}} propriétés existent ici avant les filtres ; le lieu est valide, mais filtré.',
|
'{{count}} propriétés existent ici avant les filtres ; le lieu est valide, mais filtré.',
|
||||||
|
|
@ -277,6 +278,7 @@ const fr: Translations = {
|
||||||
'Aucune propriété n’a été trouvée dans cette zone sélectionnée avant les filtres.',
|
'Aucune propriété n’a été trouvée dans cette zone sélectionnée avant les filtres.',
|
||||||
relaxFiltersHint: 'Assouplissez ou effacez les filtres pour voir les propriétés de cette zone.',
|
relaxFiltersHint: 'Assouplissez ou effacez les filtres pour voir les propriétés de cette zone.',
|
||||||
viewProperties: 'Voir {{count}} propriétés',
|
viewProperties: 'Voir {{count}} propriétés',
|
||||||
|
viewPropertiesShort: 'Voir les propriétés',
|
||||||
priceHistory: 'Historique des prix',
|
priceHistory: 'Historique des prix',
|
||||||
journeysFrom: 'Trajets depuis {{label}}',
|
journeysFrom: 'Trajets depuis {{label}}',
|
||||||
to: 'Vers {{destination}}',
|
to: 'Vers {{destination}}',
|
||||||
|
|
@ -499,7 +501,7 @@ const fr: Translations = {
|
||||||
dsIodName: 'English Indices of Deprivation 2025',
|
dsIodName: 'English Indices of Deprivation 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse:
|
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.',
|
'Percentiles nationaux de défaveur 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)',
|
dsEthnicityName: 'Population par ethnie (recensement 2021)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -913,12 +915,15 @@ const fr: Translations = {
|
||||||
// ─ POI group names ─
|
// ─ POI group names ─
|
||||||
'Public Transport': 'Transports en commun',
|
'Public Transport': 'Transports en commun',
|
||||||
Leisure: 'Loisirs',
|
Leisure: 'Loisirs',
|
||||||
|
'Food & Drink': 'Restauration',
|
||||||
|
'Green Space': 'Espaces verts',
|
||||||
Health: 'Santé',
|
Health: 'Santé',
|
||||||
'Emergency Services': 'Services d’urgence',
|
'Emergency Services': 'Services d’urgence',
|
||||||
Groceries: 'Alimentation',
|
Groceries: 'Alimentation',
|
||||||
'Local Businesses': 'Commerces de proximité',
|
'Local Businesses': 'Commerces de proximité',
|
||||||
Culture: 'Culture',
|
Culture: 'Culture',
|
||||||
Services: 'Services',
|
Services: 'Services',
|
||||||
|
Practical: 'Services pratiques',
|
||||||
Shops: 'Boutiques',
|
Shops: 'Boutiques',
|
||||||
|
|
||||||
// ─ POI categories ─
|
// ─ POI categories ─
|
||||||
|
|
|
||||||
|
|
@ -244,18 +244,20 @@ const hi: Translations = {
|
||||||
|
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: 'क्षेत्र आंकड़े',
|
areaStatistics: 'क्षेत्र आंकड़े',
|
||||||
|
areaOverview: 'अवलोकन',
|
||||||
statsFor: 'इस {{type}} की सभी संपत्तियों के आंकड़े',
|
statsFor: 'इस {{type}} की सभी संपत्तियों के आंकड़े',
|
||||||
matchingFilters: ' सभी सक्रिय फिल्टर से मेल खाते हुए',
|
matchingFilters: ' सभी सक्रिय फिल्टर से मेल खाते हुए',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'बाएं पैनल के फिल्टर यहां लागू होते हैं: मान, चार्ट और संपत्ति संख्या {{count}} सक्रिय फिल्टर का उपयोग करते हैं.',
|
'फिल्टर यहां लागू होते हैं: मान, चार्ट और संपत्ति संख्या {{count}} सक्रिय फिल्टर का उपयोग करते हैं.',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats:
|
||||||
'बाएं पैनल के फिल्टर इस पैनल को अपडेट करते हैं: मेल खाने वाली संपत्तियों के लिए ये मान फिर गणना करने हेतु फिल्टर जोड़ें.',
|
'मेल खाने वाली संपत्तियों के लिए ये मान फिर गणना करने हेतु फिल्टर जोड़ें.',
|
||||||
noFilteredMatches: 'इस क्षेत्र में कोई संपत्ति आपके फिल्टर से मेल नहीं खाती.',
|
noFilteredMatches: 'इस क्षेत्र में कोई संपत्ति आपके फिल्टर से मेल नहीं खाती.',
|
||||||
unfilteredAreaCount:
|
unfilteredAreaCount:
|
||||||
'फिल्टर से पहले यहां {{count}} संपत्तियां हैं, इसलिए स्थान वैध है लेकिन फिल्टर से बाहर हो गया है.',
|
'फिल्टर से पहले यहां {{count}} संपत्तियां हैं, इसलिए स्थान वैध है लेकिन फिल्टर से बाहर हो गया है.',
|
||||||
noUnfilteredAreaProperties: 'फिल्टर से पहले इस चुने हुए क्षेत्र में कोई संपत्ति नहीं मिली.',
|
noUnfilteredAreaProperties: 'फिल्टर से पहले इस चुने हुए क्षेत्र में कोई संपत्ति नहीं मिली.',
|
||||||
relaxFiltersHint: 'इस क्षेत्र की संपत्तियां देखने के लिए फिल्टर ढीले करें या साफ करें.',
|
relaxFiltersHint: 'इस क्षेत्र की संपत्तियां देखने के लिए फिल्टर ढीले करें या साफ करें.',
|
||||||
viewProperties: '{{count}} संपत्तियां देखें',
|
viewProperties: '{{count}} संपत्तियां देखें',
|
||||||
|
viewPropertiesShort: 'संपत्तियां देखें',
|
||||||
priceHistory: 'कीमत इतिहास',
|
priceHistory: 'कीमत इतिहास',
|
||||||
journeysFrom: '{{label}} से यात्राएं',
|
journeysFrom: '{{label}} से यात्राएं',
|
||||||
to: '{{destination}} तक',
|
to: '{{destination}} तक',
|
||||||
|
|
@ -466,7 +468,7 @@ const hi: Translations = {
|
||||||
dsIodName: 'English Indices of Deprivation 2025',
|
dsIodName: 'English Indices of Deprivation 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse:
|
dsIodUse:
|
||||||
'इंग्लैंड के हर neighbourhood के लिए income, employment, education, health, crime और living environment में relative deprivation scores.',
|
'इंग्लैंड के हर neighbourhood के लिए income, employment, education, health, crime और living environment में national deprivation percentiles.',
|
||||||
dsEthnicityName: 'Population by Ethnicity (2021 Census)',
|
dsEthnicityName: 'Population by Ethnicity (2021 Census)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -829,12 +831,15 @@ const hi: Translations = {
|
||||||
'Political vote share': 'राजनीतिक वोट शेयर',
|
'Political vote share': 'राजनीतिक वोट शेयर',
|
||||||
'Public Transport': 'सार्वजनिक परिवहन',
|
'Public Transport': 'सार्वजनिक परिवहन',
|
||||||
Leisure: 'अवकाश',
|
Leisure: 'अवकाश',
|
||||||
|
'Food & Drink': 'खाना-पीना',
|
||||||
|
'Green Space': 'हरित क्षेत्र',
|
||||||
Health: 'स्वास्थ्य',
|
Health: 'स्वास्थ्य',
|
||||||
'Emergency Services': 'आपातकालीन सेवाएं',
|
'Emergency Services': 'आपातकालीन सेवाएं',
|
||||||
Groceries: 'किराना',
|
Groceries: 'किराना',
|
||||||
'Local Businesses': 'स्थानीय व्यवसाय',
|
'Local Businesses': 'स्थानीय व्यवसाय',
|
||||||
Culture: 'संस्कृति',
|
Culture: 'संस्कृति',
|
||||||
Services: 'सेवाएं',
|
Services: 'सेवाएं',
|
||||||
|
Practical: 'व्यावहारिक',
|
||||||
Shops: 'दुकानें',
|
Shops: 'दुकानें',
|
||||||
Airport: 'हवाई अड्डा',
|
Airport: 'हवाई अड्डा',
|
||||||
Ferry: 'फेरी',
|
Ferry: 'फेरी',
|
||||||
|
|
|
||||||
|
|
@ -259,18 +259,20 @@ const hu: Translations = {
|
||||||
// ── Area Pane ──────────────────────────────────────
|
// ── Area Pane ──────────────────────────────────────
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: 'Területi statisztikák',
|
areaStatistics: 'Területi statisztikák',
|
||||||
|
areaOverview: 'Áttekintés',
|
||||||
statsFor: 'Statisztikák a(z) {{type}} összes ingatlanáról',
|
statsFor: 'Statisztikák a(z) {{type}} összes ingatlanáról',
|
||||||
matchingFilters: ' az összes aktív szűrőnek megfelelően',
|
matchingFilters: ' az összes aktív szűrőnek megfelelően',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'A bal oldali panel szűrői itt is érvényesek: az értékek, diagramok és ingatlanszámok a(z) {{count}} aktív szűrőt használják.',
|
'A szűrők itt is érvényesek: az értékek, diagramok és ingatlanszámok a(z) {{count}} aktív szűrőt használják.',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats:
|
||||||
'A bal oldali panel szűrői frissítik ezt a panelt: adjon hozzá szűrőket, hogy ezek az értékek az illeszkedő ingatlanokra számolódjanak újra.',
|
'Adjon hozzá szűrőket, hogy ezek az értékek az illeszkedő ingatlanokra számolódjanak újra.',
|
||||||
noFilteredMatches: 'Ezen a területen egyetlen ingatlan sem felel meg a szűrőknek.',
|
noFilteredMatches: 'Ezen a területen egyetlen ingatlan sem felel meg a szűrőknek.',
|
||||||
unfilteredAreaCount:
|
unfilteredAreaCount:
|
||||||
'{{count}} ingatlan található itt szűrők nélkül, tehát a hely érvényes, csak a szűrők kizárják.',
|
'{{count}} ingatlan található itt szűrők nélkül, tehát a hely érvényes, csak a szűrők kizárják.',
|
||||||
noUnfilteredAreaProperties: 'A kiválasztott területen szűrők nélkül sem található ingatlan.',
|
noUnfilteredAreaProperties: 'A kiválasztott területen szűrők nélkül sem található ingatlan.',
|
||||||
relaxFiltersHint: 'Lazítson vagy törölje a szűrőket, hogy lássa a terület ingatlanjait.',
|
relaxFiltersHint: 'Lazítson vagy törölje a szűrőket, hogy lássa a terület ingatlanjait.',
|
||||||
viewProperties: '{{count}} ingatlan megtekintése',
|
viewProperties: '{{count}} ingatlan megtekintése',
|
||||||
|
viewPropertiesShort: 'Ingatlanok megtekintése',
|
||||||
priceHistory: 'Ártörténet',
|
priceHistory: 'Ártörténet',
|
||||||
journeysFrom: 'Utazások innen: {{label}}',
|
journeysFrom: 'Utazások innen: {{label}}',
|
||||||
to: 'Ide: {{destination}}',
|
to: 'Ide: {{destination}}',
|
||||||
|
|
@ -493,7 +495,7 @@ const hu: Translations = {
|
||||||
dsIodName: 'Angol Deprivációs Mutatók 2025',
|
dsIodName: 'Angol Deprivációs Mutatók 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse:
|
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.',
|
'Országos deprivációs percentilisek 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)',
|
dsEthnicityName: 'Népesség etnikai megoszlás szerint (2021-es népszámlálás)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -907,12 +909,15 @@ const hu: Translations = {
|
||||||
// ─ POI group names ─
|
// ─ POI group names ─
|
||||||
'Public Transport': 'Tömegközlekedés',
|
'Public Transport': 'Tömegközlekedés',
|
||||||
Leisure: 'Szabadidő',
|
Leisure: 'Szabadidő',
|
||||||
|
'Food & Drink': 'Étel és ital',
|
||||||
|
'Green Space': 'Zöldterületek',
|
||||||
Health: 'Egészségügy',
|
Health: 'Egészségügy',
|
||||||
'Emergency Services': 'Sürgősségi szolgálatok',
|
'Emergency Services': 'Sürgősségi szolgálatok',
|
||||||
Groceries: 'Élelmiszer',
|
Groceries: 'Élelmiszer',
|
||||||
'Local Businesses': 'Helyi vállalkozások',
|
'Local Businesses': 'Helyi vállalkozások',
|
||||||
Culture: 'Kultúra',
|
Culture: 'Kultúra',
|
||||||
Services: 'Szolgáltatások',
|
Services: 'Szolgáltatások',
|
||||||
|
Practical: 'Praktikus',
|
||||||
Shops: 'Üzletek',
|
Shops: 'Üzletek',
|
||||||
|
|
||||||
// ─ POI categories ─
|
// ─ POI categories ─
|
||||||
|
|
|
||||||
|
|
@ -256,17 +256,18 @@ const zh: Translations = {
|
||||||
// ── Area Pane ──────────────────────────────────────
|
// ── Area Pane ──────────────────────────────────────
|
||||||
areaPane: {
|
areaPane: {
|
||||||
areaStatistics: '区域统计',
|
areaStatistics: '区域统计',
|
||||||
|
areaOverview: '概览',
|
||||||
statsFor: '该{{type}}内所有房产的统计数据',
|
statsFor: '该{{type}}内所有房产的统计数据',
|
||||||
matchingFilters: ',满足所有当前筛选条件',
|
matchingFilters: ',满足所有当前筛选条件',
|
||||||
filtersAffectStats:
|
filtersAffectStats:
|
||||||
'左侧面板的筛选条件会应用到这里:数值、图表和房产数量都会使用 {{count}} 个当前筛选条件。',
|
'筛选条件会应用到这里:数值、图表和房产数量都会使用 {{count}} 个当前筛选条件。',
|
||||||
noFiltersAffectStats:
|
noFiltersAffectStats: '添加筛选条件后,这些值会按匹配的房产重新计算。',
|
||||||
'左侧面板的筛选条件会更新此面板:添加筛选条件后,这些值会按匹配的房产重新计算。',
|
|
||||||
noFilteredMatches: '该区域没有房产符合当前筛选条件。',
|
noFilteredMatches: '该区域没有房产符合当前筛选条件。',
|
||||||
unfilteredAreaCount: '筛选前这里有 {{count}} 处房产;位置有效,但被筛选条件排除了。',
|
unfilteredAreaCount: '筛选前这里有 {{count}} 处房产;位置有效,但被筛选条件排除了。',
|
||||||
noUnfilteredAreaProperties: '筛选前该选定区域内也没有找到房产。',
|
noUnfilteredAreaProperties: '筛选前该选定区域内也没有找到房产。',
|
||||||
relaxFiltersHint: '放宽或清除筛选条件即可查看该区域的房产。',
|
relaxFiltersHint: '放宽或清除筛选条件即可查看该区域的房产。',
|
||||||
viewProperties: '查看 {{count}} 处房产',
|
viewProperties: '查看 {{count}} 处房产',
|
||||||
|
viewPropertiesShort: '查看房产',
|
||||||
priceHistory: '价格历史',
|
priceHistory: '价格历史',
|
||||||
journeysFrom: '从 {{label}} 出发的路线',
|
journeysFrom: '从 {{label}} 出发的路线',
|
||||||
to: '到 {{destination}}',
|
to: '到 {{destination}}',
|
||||||
|
|
@ -482,7 +483,7 @@ const zh: Translations = {
|
||||||
dsNsplUse: '将邮编映射到坐标和统计区域代码,用于将所有区域级数据集关联到各个房产。',
|
dsNsplUse: '将邮编映射到坐标和统计区域代码,用于将所有区域级数据集关联到各个房产。',
|
||||||
dsIodName: 'English Indices of Deprivation 2025',
|
dsIodName: 'English Indices of Deprivation 2025',
|
||||||
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
dsIodOrigin: 'Ministry of Housing, Communities & Local Government',
|
||||||
dsIodUse: '英格兰每个社区在收入、就业、教育、健康、犯罪和居住环境方面的相对贫困指数。',
|
dsIodUse: '英格兰每个社区在收入、就业、教育、健康、犯罪和居住环境方面的全国贫困百分位。',
|
||||||
dsEthnicityName: '按族裔划分的人口(2021 年人口普查)',
|
dsEthnicityName: '按族裔划分的人口(2021 年人口普查)',
|
||||||
dsEthnicityOrigin: 'ONS',
|
dsEthnicityOrigin: 'ONS',
|
||||||
dsEthnicityUse:
|
dsEthnicityUse:
|
||||||
|
|
@ -880,12 +881,15 @@ const zh: Translations = {
|
||||||
// ─ POI group names ─
|
// ─ POI group names ─
|
||||||
'Public Transport': '公共交通',
|
'Public Transport': '公共交通',
|
||||||
Leisure: '休闲',
|
Leisure: '休闲',
|
||||||
|
'Food & Drink': '餐饮',
|
||||||
|
'Green Space': '绿地',
|
||||||
Health: '健康',
|
Health: '健康',
|
||||||
'Emergency Services': '紧急服务',
|
'Emergency Services': '紧急服务',
|
||||||
Groceries: '食品杂货',
|
Groceries: '食品杂货',
|
||||||
'Local Businesses': '本地商业',
|
'Local Businesses': '本地商业',
|
||||||
Culture: '文化',
|
Culture: '文化',
|
||||||
Services: '服务',
|
Services: '服务',
|
||||||
|
Practical: '实用服务',
|
||||||
Shops: '商店',
|
Shops: '商店',
|
||||||
|
|
||||||
// ─ POI categories ─
|
// ─ POI categories ─
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ dependencies = [
|
||||||
"rasterio>=1.5.0",
|
"rasterio>=1.5.0",
|
||||||
"pyproj>=3.7.2",
|
"pyproj>=3.7.2",
|
||||||
"pyshp>=2.3.0",
|
"pyshp>=2.3.0",
|
||||||
|
"pillow>=12.0.0",
|
||||||
"folium>=0.20.0",
|
"folium>=0.20.0",
|
||||||
"httpx",
|
"httpx",
|
||||||
"polars",
|
"polars",
|
||||||
|
|
@ -41,8 +42,8 @@ dev = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.deptry]
|
[tool.deptry]
|
||||||
# analyses/ and scripts/ use transitive deps
|
# analyses/ and scripts/ use transitive deps; video/tts has its own UV project.
|
||||||
exclude = ["\\.venv", "analyses", "scripts"]
|
exclude = ["\\.venv", "analyses", "scripts", "video/tts"]
|
||||||
|
|
||||||
[tool.deptry.per_rule_ignores]
|
[tool.deptry.per_rule_ignores]
|
||||||
# pyarrow/fastexcel: runtime backends for polars parquet/Excel I/O
|
# pyarrow/fastexcel: runtime backends for polars parquet/Excel I/O
|
||||||
|
|
|
||||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -1376,6 +1376,7 @@ dependencies = [
|
||||||
{ name = "numpy", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "numpy", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
{ name = "osmium", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "osmium", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
{ name = "pandas", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "pandas", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
|
{ name = "pillow", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
{ name = "plotly", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "plotly", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
{ name = "polars", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "polars", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
{ name = "pyarrow", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
{ name = "pyarrow", marker = "python_full_version < '3.14' and sys_platform == 'linux'" },
|
||||||
|
|
@ -1407,6 +1408,7 @@ requires-dist = [
|
||||||
{ name = "numpy", specifier = ">=1.26.0" },
|
{ name = "numpy", specifier = ">=1.26.0" },
|
||||||
{ name = "osmium", specifier = ">=4.0.0" },
|
{ name = "osmium", specifier = ">=4.0.0" },
|
||||||
{ name = "pandas", specifier = ">=2.0.0" },
|
{ name = "pandas", specifier = ">=2.0.0" },
|
||||||
|
{ name = "pillow", specifier = ">=12.0.0" },
|
||||||
{ name = "plotly", specifier = ">=6.5.2" },
|
{ name = "plotly", specifier = ">=6.5.2" },
|
||||||
{ name = "polars" },
|
{ name = "polars" },
|
||||||
{ name = "polars", specifier = ">=1.37.1" },
|
{ name = "polars", specifier = ">=1.37.1" },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue