feat: auto-translate furnidata per import (nl/en/de/fr/es/tr/it)
Generate localized FurnitureData_<lang>.json files from the master FurnitureData.json + official Habbo hotel translations. Fetches per-hotel furnidata with in-memory cache (6h TTL), maps by classname (* variant aware), and overrides name/description where a translation exists. Full rebuild after every batch/batch-regen/clone import and as admin GET ?action=build-languages; single imports patch incrementally via patchLocalizedFurniDataEntries. Failures are best-effort and never fail the import itself. Files are written to every configured gamedata write path (primary + mirrors) so /gamedata/config/FurnitureData_<lang>.json is available for per-language Nitro clients. New service: src/lib/services/furni-data-i18n.ts with buildLocalizedFurniDataFiles, patchLocalizedFurniDataEntries, FURNIDATA_LANGUAGES (nl/en/de/fr/es/tr/it) and applyTranslationsForTest.
This commit is contained in:
1 parent
984b5bf793
commit
8bb339cc49
5 files changed
+519
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
reconcileFurniDataWithItemsBase,
|
||||
verifyAndFixSpriteIds,
|
||||
} from "@/lib/services/furni-data";
|
||||
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||
import {
|
||||
ensureFurniOwnership,
|
||||
reconcileImportedOfferIds,
|
||||
@@ -147,6 +148,11 @@ export const POST = withAdmin(
|
||||
await rebuildCatalogOfferIds();
|
||||
await verifyAndFixSpriteIds();
|
||||
await verifyAndFixInteractionModesCount();
|
||||
try {
|
||||
await buildLocalizedFurniDataFiles();
|
||||
} catch {
|
||||
/* localized build failures are non-fatal */
|
||||
}
|
||||
await ensureFurniOwnership();
|
||||
await syncAssetsToGamedataBundle();
|
||||
await rcon.updateCatalog();
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
reconcileFurniDataWithItemsBase,
|
||||
verifyAndFixSpriteIds,
|
||||
} from "@/lib/services/furni-data";
|
||||
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||
import {
|
||||
downloadFile,
|
||||
ensureDirectories,
|
||||
@@ -277,6 +278,18 @@ export const POST = withAdmin(
|
||||
// Repair any sprite_id drift so the client renders the right item
|
||||
const spriteVerify = await verifyAndFixSpriteIds();
|
||||
|
||||
let languages: Awaited<
|
||||
ReturnType<typeof buildLocalizedFurniDataFiles>
|
||||
> = [];
|
||||
try {
|
||||
languages = await buildLocalizedFurniDataFiles();
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[batch-regen] Localized furnidata build failed:",
|
||||
(err as Error).message,
|
||||
);
|
||||
}
|
||||
|
||||
send({
|
||||
type: "regen_complete",
|
||||
succeeded,
|
||||
@@ -293,6 +306,7 @@ export const POST = withAdmin(
|
||||
offerRebuildFixed: offerRebuild.fixed,
|
||||
spriteChecked: spriteVerify.checked,
|
||||
spriteFixed: spriteVerify.fixed,
|
||||
languages,
|
||||
duration: Date.now() - startTime,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
reconcileFurniDataWithItemsBase,
|
||||
verifyAndFixSpriteIds,
|
||||
} from "@/lib/services/furni-data";
|
||||
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||
import {
|
||||
ensureDirectories,
|
||||
ensureFurniOwnership,
|
||||
@@ -232,6 +233,9 @@ export const POST = withAdmin(
|
||||
let offerRebuildFixed = 0;
|
||||
let spriteFixed = 0;
|
||||
let interactionFixed = 0;
|
||||
let languages: Awaited<
|
||||
ReturnType<typeof buildLocalizedFurniDataFiles>
|
||||
> = [];
|
||||
try {
|
||||
offerIdsFixed = (await reconcileImportedOfferIds()).fixed;
|
||||
ownershipFixed = (await ensureFurniOwnership()).fixed;
|
||||
@@ -246,6 +250,14 @@ export const POST = withAdmin(
|
||||
offerRebuildFixed = (await rebuildCatalogOfferIds()).fixed;
|
||||
spriteFixed = (await verifyAndFixSpriteIds()).fixed;
|
||||
interactionFixed = (await verifyAndFixInteractionModesCount()).fixed;
|
||||
try {
|
||||
languages = await buildLocalizedFurniDataFiles();
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[import-furni] Localized furnidata build failed:",
|
||||
(err as Error).message,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[import-furni] Post-import reconcile/ownership failed:",
|
||||
@@ -269,6 +281,7 @@ export const POST = withAdmin(
|
||||
offerRebuildFixed,
|
||||
spriteFixed,
|
||||
interactionFixed,
|
||||
languages,
|
||||
duration: Date.now() - startTime,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
reconcileFurniDataWithItemsBase,
|
||||
verifyAndFixSpriteIds,
|
||||
} from "@/lib/services/furni-data";
|
||||
import {
|
||||
buildLocalizedFurniDataFiles,
|
||||
patchLocalizedFurniDataEntries,
|
||||
} from "@/lib/services/furni-data-i18n";
|
||||
import {
|
||||
autoPriceFurni,
|
||||
CATEGORY_PAGE,
|
||||
@@ -189,6 +193,11 @@ export const GET = withAdmin(
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "build-languages") {
|
||||
const languages = await buildLocalizedFurniDataFiles();
|
||||
return apiOk({ languages });
|
||||
}
|
||||
|
||||
// Missing nitro endpoint: returns only imported items missing .nitro files
|
||||
if (action === "missing-nitro") {
|
||||
const pageNum = parseInt(
|
||||
@@ -471,6 +480,18 @@ export const POST = withAdmin(
|
||||
// Verify + fix interaction_modes_count once for the whole batch
|
||||
const verification = await verifyAndFixInteractionModesCount();
|
||||
|
||||
// Rebuild all localized furnidata files (best effort)
|
||||
let languages: Awaited<ReturnType<typeof buildLocalizedFurniDataFiles>> =
|
||||
[];
|
||||
try {
|
||||
languages = await buildLocalizedFurniDataFiles();
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[import-furni] Localized furnidata build failed:",
|
||||
(err as Error).message,
|
||||
);
|
||||
}
|
||||
|
||||
const succeeded = results.filter((r) => r.ok).length;
|
||||
const failed = results.filter((r) => !r.ok).length;
|
||||
|
||||
@@ -492,6 +513,7 @@ export const POST = withAdmin(
|
||||
spriteChecked: spriteVerify.checked,
|
||||
spriteFixed: spriteVerify.fixed,
|
||||
interactionFixed: verification.fixed,
|
||||
languages,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -562,6 +584,21 @@ export const POST = withAdmin(
|
||||
// Verify + fix interaction_modes_count (single consolidated pass)
|
||||
const verification = await verifyAndFixInteractionModesCount();
|
||||
|
||||
// Patch localized furnidata files for this single furni (best effort)
|
||||
let languagesPatch: Awaited<
|
||||
ReturnType<typeof patchLocalizedFurniDataEntries>
|
||||
> = [];
|
||||
try {
|
||||
languagesPatch = await patchLocalizedFurniDataEntries([
|
||||
{ classname, name, description: description ?? "" },
|
||||
]);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[import-furni] Localized patch failed:",
|
||||
(err as Error).message,
|
||||
);
|
||||
}
|
||||
|
||||
logAudit({
|
||||
userId: ctx.session.user.id,
|
||||
action: "furni_import",
|
||||
@@ -595,6 +632,7 @@ export const POST = withAdmin(
|
||||
spriteChecked: spriteVerify.checked,
|
||||
spriteFixed: spriteVerify.fixed,
|
||||
interactionFixed: verification.fixed,
|
||||
languages: languagesPatch,
|
||||
spriteCount: result.spriteCount,
|
||||
warnings: result.warnings,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user