feat(pcat): translate part descriptions + vehicle attrs (body/engine/transmission)

PCAT was missing parity with EMEX in two places:
- Part `description` (notice) was stored raw English alongside translated `name`.
  Now batched into translateMany so users see Turkish notices.
- Vehicle body/engine/transmission attrs from VIN decode were raw upstream
  values. Wire up the existing emex.mapper dictionaries on the PCAT
  single-car, PCAT resolveById, and EMEX single-vehicle result paths.
- emex.mapper translateToTurkish now falls back to the original term on
  dictionary miss instead of null — upstream values are heterogeneous
  (engine codes, multi-word descriptors); losing them was worse than
  leaving them untranslated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 10:45:12 +03:00
parent 543be19261
commit d14bcb7dca
3 changed files with 32 additions and 14 deletions

View File

@@ -705,9 +705,10 @@ export class CategoriesService {
}
}
const trMap = await this.translationsService.translateMany(
rawParts.map((p) => p.name).filter(Boolean),
);
const trMap = await this.translationsService.translateMany([
...rawParts.map((p) => p.name).filter(Boolean),
...rawParts.map((p) => p.notice).filter((n): n is string => !!n),
]);
const allParts: Array<typeof parts.$inferInsert> = rawParts.map((p) => ({
vehicleId: vehicle.id,
@@ -715,7 +716,7 @@ export class CategoriesService {
oemCode: p.number,
name: trMap.get(p.name) ?? p.name ?? "Unknown",
nameOriginal: p.name || null,
description: p.notice,
description: p.notice ? (trMap.get(p.notice) ?? p.notice) : null,
quantity: null,
position: p.positionNumber,
hotspotIndex: p.positionNumber

View File

@@ -91,7 +91,11 @@ function translateToTurkish(
): string | null {
if (!term) return null;
const normalized = term.toLowerCase().trim();
return dictionary[normalized] || null;
// Dictionary miss → return original term unchanged. Upstream values are
// often heterogeneous (engine codes, multi-word descriptors) that the
// single-word dictionary can't cover; losing them would be worse than
// leaving them untranslated.
return dictionary[normalized] ?? term;
}
export function translateBodyType(bodyType: string | null): string | null {

View File

@@ -21,6 +21,11 @@ import {
vehicles,
} from "../database/schema/core";
import { CorgiService } from "../integrations/corgi/corgi.service";
import {
translateBodyType,
translateEngineType,
translateTransmission,
} from "../integrations/emex/emex.mapper";
import { EmexService } from "../integrations/emex/emex.service";
import { EmexCandidate } from "../integrations/emex/emex.service";
import { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
@@ -581,9 +586,11 @@ export class VehiclesService {
brandName,
model: car.name || null,
year: this.extractYearFromPcatCar(car) || null,
engine: this.extractParamFromPcatCar(car, "engine") || null,
transmission: this.extractParamFromPcatCar(car, "transmission") || null,
bodyType: this.extractParamFromPcatCar(car, "body") || null,
engine: translateEngineType(this.extractParamFromPcatCar(car, "engine") || null),
transmission: translateTransmission(
this.extractParamFromPcatCar(car, "transmission") || null,
),
bodyType: translateBodyType(this.extractParamFromPcatCar(car, "body") || null),
rawData: {
source: "parts-catalogs",
catalogId: car.catalogId,
@@ -611,9 +618,13 @@ export class VehiclesService {
brandName,
model: emexSingleVehicle.model || null,
year: emexSingleVehicle.year || null,
engine: emexSingleVehicle.engineCode || emexSingleVehicle.engineType || null,
transmission: emexSingleVehicle.transmission || null,
bodyType: emexSingleVehicle.bodyType || null,
// engineCode is alphanumeric (e.g. "N20B20") — leave raw; only the
// engineType fallback ("Petrol"/"Diesel") goes through the dict.
engine:
emexSingleVehicle.engineCode ||
translateEngineType(emexSingleVehicle.engineType || null),
transmission: translateTransmission(emexSingleVehicle.transmission || null),
bodyType: translateBodyType(emexSingleVehicle.bodyType || null),
rawData: emexSingleVehicle.raw || null,
source: "emex",
corgiKnown,
@@ -738,9 +749,11 @@ export class VehiclesService {
brandName,
model: car.name || null,
year: this.extractYearFromPcatCar(car) || null,
engine: this.extractParamFromPcatCar(car, "engine") || null,
transmission: this.extractParamFromPcatCar(car, "transmission") || null,
bodyType: this.extractParamFromPcatCar(car, "body") || null,
engine: translateEngineType(this.extractParamFromPcatCar(car, "engine") || null),
transmission: translateTransmission(
this.extractParamFromPcatCar(car, "transmission") || null,
),
bodyType: translateBodyType(this.extractParamFromPcatCar(car, "body") || null),
rawData: {
source: "parts-catalogs",
catalogId: car.catalogId,