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,
|
reconcileFurniDataWithItemsBase,
|
||||||
verifyAndFixSpriteIds,
|
verifyAndFixSpriteIds,
|
||||||
} from "@/lib/services/furni-data";
|
} from "@/lib/services/furni-data";
|
||||||
|
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||||
import {
|
import {
|
||||||
ensureFurniOwnership,
|
ensureFurniOwnership,
|
||||||
reconcileImportedOfferIds,
|
reconcileImportedOfferIds,
|
||||||
@@ -147,6 +148,11 @@ export const POST = withAdmin(
|
|||||||
await rebuildCatalogOfferIds();
|
await rebuildCatalogOfferIds();
|
||||||
await verifyAndFixSpriteIds();
|
await verifyAndFixSpriteIds();
|
||||||
await verifyAndFixInteractionModesCount();
|
await verifyAndFixInteractionModesCount();
|
||||||
|
try {
|
||||||
|
await buildLocalizedFurniDataFiles();
|
||||||
|
} catch {
|
||||||
|
/* localized build failures are non-fatal */
|
||||||
|
}
|
||||||
await ensureFurniOwnership();
|
await ensureFurniOwnership();
|
||||||
await syncAssetsToGamedataBundle();
|
await syncAssetsToGamedataBundle();
|
||||||
await rcon.updateCatalog();
|
await rcon.updateCatalog();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
reconcileFurniDataWithItemsBase,
|
reconcileFurniDataWithItemsBase,
|
||||||
verifyAndFixSpriteIds,
|
verifyAndFixSpriteIds,
|
||||||
} from "@/lib/services/furni-data";
|
} from "@/lib/services/furni-data";
|
||||||
|
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||||
import {
|
import {
|
||||||
downloadFile,
|
downloadFile,
|
||||||
ensureDirectories,
|
ensureDirectories,
|
||||||
@@ -277,6 +278,18 @@ export const POST = withAdmin(
|
|||||||
// Repair any sprite_id drift so the client renders the right item
|
// Repair any sprite_id drift so the client renders the right item
|
||||||
const spriteVerify = await verifyAndFixSpriteIds();
|
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({
|
send({
|
||||||
type: "regen_complete",
|
type: "regen_complete",
|
||||||
succeeded,
|
succeeded,
|
||||||
@@ -293,6 +306,7 @@ export const POST = withAdmin(
|
|||||||
offerRebuildFixed: offerRebuild.fixed,
|
offerRebuildFixed: offerRebuild.fixed,
|
||||||
spriteChecked: spriteVerify.checked,
|
spriteChecked: spriteVerify.checked,
|
||||||
spriteFixed: spriteVerify.fixed,
|
spriteFixed: spriteVerify.fixed,
|
||||||
|
languages,
|
||||||
duration: Date.now() - startTime,
|
duration: Date.now() - startTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
reconcileFurniDataWithItemsBase,
|
reconcileFurniDataWithItemsBase,
|
||||||
verifyAndFixSpriteIds,
|
verifyAndFixSpriteIds,
|
||||||
} from "@/lib/services/furni-data";
|
} from "@/lib/services/furni-data";
|
||||||
|
import { buildLocalizedFurniDataFiles } from "@/lib/services/furni-data-i18n";
|
||||||
import {
|
import {
|
||||||
ensureDirectories,
|
ensureDirectories,
|
||||||
ensureFurniOwnership,
|
ensureFurniOwnership,
|
||||||
@@ -232,6 +233,9 @@ export const POST = withAdmin(
|
|||||||
let offerRebuildFixed = 0;
|
let offerRebuildFixed = 0;
|
||||||
let spriteFixed = 0;
|
let spriteFixed = 0;
|
||||||
let interactionFixed = 0;
|
let interactionFixed = 0;
|
||||||
|
let languages: Awaited<
|
||||||
|
ReturnType<typeof buildLocalizedFurniDataFiles>
|
||||||
|
> = [];
|
||||||
try {
|
try {
|
||||||
offerIdsFixed = (await reconcileImportedOfferIds()).fixed;
|
offerIdsFixed = (await reconcileImportedOfferIds()).fixed;
|
||||||
ownershipFixed = (await ensureFurniOwnership()).fixed;
|
ownershipFixed = (await ensureFurniOwnership()).fixed;
|
||||||
@@ -246,6 +250,14 @@ export const POST = withAdmin(
|
|||||||
offerRebuildFixed = (await rebuildCatalogOfferIds()).fixed;
|
offerRebuildFixed = (await rebuildCatalogOfferIds()).fixed;
|
||||||
spriteFixed = (await verifyAndFixSpriteIds()).fixed;
|
spriteFixed = (await verifyAndFixSpriteIds()).fixed;
|
||||||
interactionFixed = (await verifyAndFixInteractionModesCount()).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) {
|
} catch (err) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"[import-furni] Post-import reconcile/ownership failed:",
|
"[import-furni] Post-import reconcile/ownership failed:",
|
||||||
@@ -269,6 +281,7 @@ export const POST = withAdmin(
|
|||||||
offerRebuildFixed,
|
offerRebuildFixed,
|
||||||
spriteFixed,
|
spriteFixed,
|
||||||
interactionFixed,
|
interactionFixed,
|
||||||
|
languages,
|
||||||
duration: Date.now() - startTime,
|
duration: Date.now() - startTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import {
|
|||||||
reconcileFurniDataWithItemsBase,
|
reconcileFurniDataWithItemsBase,
|
||||||
verifyAndFixSpriteIds,
|
verifyAndFixSpriteIds,
|
||||||
} from "@/lib/services/furni-data";
|
} from "@/lib/services/furni-data";
|
||||||
|
import {
|
||||||
|
buildLocalizedFurniDataFiles,
|
||||||
|
patchLocalizedFurniDataEntries,
|
||||||
|
} from "@/lib/services/furni-data-i18n";
|
||||||
import {
|
import {
|
||||||
autoPriceFurni,
|
autoPriceFurni,
|
||||||
CATEGORY_PAGE,
|
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
|
// Missing nitro endpoint: returns only imported items missing .nitro files
|
||||||
if (action === "missing-nitro") {
|
if (action === "missing-nitro") {
|
||||||
const pageNum = parseInt(
|
const pageNum = parseInt(
|
||||||
@@ -471,6 +480,18 @@ export const POST = withAdmin(
|
|||||||
// Verify + fix interaction_modes_count once for the whole batch
|
// Verify + fix interaction_modes_count once for the whole batch
|
||||||
const verification = await verifyAndFixInteractionModesCount();
|
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 succeeded = results.filter((r) => r.ok).length;
|
||||||
const failed = 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,
|
spriteChecked: spriteVerify.checked,
|
||||||
spriteFixed: spriteVerify.fixed,
|
spriteFixed: spriteVerify.fixed,
|
||||||
interactionFixed: verification.fixed,
|
interactionFixed: verification.fixed,
|
||||||
|
languages,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,6 +584,21 @@ export const POST = withAdmin(
|
|||||||
// Verify + fix interaction_modes_count (single consolidated pass)
|
// Verify + fix interaction_modes_count (single consolidated pass)
|
||||||
const verification = await verifyAndFixInteractionModesCount();
|
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({
|
logAudit({
|
||||||
userId: ctx.session.user.id,
|
userId: ctx.session.user.id,
|
||||||
action: "furni_import",
|
action: "furni_import",
|
||||||
@@ -595,6 +632,7 @@ export const POST = withAdmin(
|
|||||||
spriteChecked: spriteVerify.checked,
|
spriteChecked: spriteVerify.checked,
|
||||||
spriteFixed: spriteVerify.fixed,
|
spriteFixed: spriteVerify.fixed,
|
||||||
interactionFixed: verification.fixed,
|
interactionFixed: verification.fixed,
|
||||||
|
languages: languagesPatch,
|
||||||
spriteCount: result.spriteCount,
|
spriteCount: result.spriteCount,
|
||||||
warnings: result.warnings,
|
warnings: result.warnings,
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user