This commit is contained in:
Andras Schmelczer 2026-07-12 15:03:33 +01:00
parent 982e0cc89c
commit cfaf58dfba
44 changed files with 793 additions and 201 deletions

View file

@ -16,7 +16,7 @@
// present in en.ts > server so they can be translated.
//
// The script parses the TypeScript source with the compiler API and walks the
// AST no runtime import, no transpilation, no temp files. Run it with:
// AST: no runtime import, no transpilation, no temp files. Run it with:
// node frontend/scripts/check-translations.mjs
import { readFileSync, readdirSync } from 'fs';
@ -106,7 +106,7 @@ function parseFile(path) {
}
// Recursively turn a TS literal expression into a plain JS value.
// Returns undefined for nodes we don't understand — callers must check.
// Returns undefined for nodes we don't understand. Callers must check.
function literalToJs(node) {
if (!node) return undefined;
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
@ -217,7 +217,7 @@ function readServerFeatureNames() {
// Names of the Enum/Numeric feature *configs* (each needs a description + detail
// translation). We take the FIRST `name:` field after every Feature::Enum( /
// Feature::Numeric( opening. This deliberately skips macro-generated configs
// whose name is a `concat!(...)` expression (the crime rates handled via
// whose name is a `concat!(...)` expression (the crime rates, handled via
// deriveLegacyCrimeKeys instead) and stops the lazy match from running past such
// a config into an unrelated FeatureGroup `name:` (which previously made the
// group name "Properties" look like a required feature).
@ -302,7 +302,7 @@ function checkLeafConsistency(path, enValue, trValue, lang) {
const got = tokenMultiset(trValue, re);
if (!multisetsEqual(want, got)) {
fail(
`[${lang}] ${path}: ${label} mismatch en=${JSON.stringify(want)} ` +
`[${lang}] ${path}: ${label} mismatch, en=${JSON.stringify(want)} ` +
`${lang}=${JSON.stringify(got)}`
);
}
@ -425,7 +425,7 @@ function checkRecordCoverage(file, varName, supportedCodes, serverKeys, required
// Every key here must also be a translatable feature name in en.ts > server,
// or a legacy crime key that maps onto the per-window server keys (see
// deriveLegacyCrimeKeys / legacyCrimeFeatureKey). Otherwise the description is
// unreachable ts() looks up server.${name}.
// unreachable: ts() looks up server.${name}.
for (const key of union) {
if (!serverKeys.has(key) && !legacyCrimeKeys.has(key)) {
fail(`${file}: key "${key}" has no matching entry in en.ts > server`);
@ -532,7 +532,7 @@ function main() {
console.error(`\n${errors.length} translation error(s).`);
process.exit(1);
}
console.log(`i18n OK ${supportedCodes.length} languages, ${warnings.length} warning(s).`);
console.log(`i18n OK: ${supportedCodes.length} languages, ${warnings.length} warning(s).`);
}
main();

View file

@ -35,7 +35,7 @@ const ROUTES = [
output: 'property-price-map/index.html',
title: 'Property price map for England - Compare postcodes before viewing',
description:
'Compare sold prices, estimated current value, price per square metre and local context across English postcodes before searching listings.',
'Compare sold prices, estimated current value, price per square metre and local context across English postcodes to find the underpriced ones.',
},
{
path: '/postcode-property-search',
@ -127,7 +127,7 @@ const FAQ_SCHEMA_ITEMS = [
{
question: 'Where should I look once the obvious areas are too expensive?',
answer:
'Set your budget, property type, floor area, commute, schools, crime, noise, broadband, parks, and other must-haves. The map removes postcodes that fail those tests, so overlooked areas can surface before you start searching listings.',
'Set your budget, property type, floor area, commute, schools, crime, noise, broadband, parks, and other must-haves. The map ranks every postcode in England on those measures, so overlooked, underpriced areas (the cheaper twins of the names everyone knows) rise to the top.',
},
{
question: 'What should I do when my search returns too many or too few areas?',
@ -145,9 +145,9 @@ const FAQ_SCHEMA_ITEMS = [
'The estimate starts with the last HM Land Registry sale price, adjusts it to current-market terms using repeat-sales modelling and fallback models, then blends that result with a nearest-neighbour estimate from nearby, recently sold, same-type homes.',
},
{
question: 'Does Perfect Postcode replace Rightmove, Zoopla and OnTheMarket?',
question: 'How is Perfect Postcode different from Rightmove, Zoopla and OnTheMarket?',
answer:
'No. Perfect Postcode helps you choose the right postcode from area data; the listing portals are still where you check live availability, photos, agent contact, viewings and alerts.',
'Listing portals show you individual homes that are for sale right now. Perfect Postcode is where you decide which postcode to buy in, ranking every postcode in England on 200+ data fields the portals never compare, from £/sqm to crime, schools and commute.',
},
];