Initial commit (unpacked platform)
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { schema, type Database } from "@app/database";
|
||||
import { loadLocalePreferences } from "./localeContext.js";
|
||||
|
||||
/**
|
||||
* Innehållsspråk (i18n-spec §11–14): svensk källtext är sanningen, publicerade
|
||||
* översättningar är vyer. Upplösning: exakt tagg ("en-US") → primärt språk
|
||||
* ("en") → svensk källa. Endast status=published serveras till användare.
|
||||
*/
|
||||
|
||||
/** "en-US" -> ["en-US", "en"], "sv" -> ["sv"]. */
|
||||
export function languageCandidates(languageTag: string): string[] {
|
||||
const primary = languageTag.split("-")[0] ?? languageTag;
|
||||
return primary === languageTag ? [languageTag] : [languageTag, primary];
|
||||
}
|
||||
|
||||
export async function userLanguageTag(db: Database, userId: string): Promise<string> {
|
||||
const prefs = await loadLocalePreferences(db, userId);
|
||||
return prefs.languageTag;
|
||||
}
|
||||
|
||||
export interface ResolvedRecipeText {
|
||||
language: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
storageGuidance: string | null;
|
||||
/** stepNumber -> { instruction, tip } */
|
||||
steps: Map<number, { instruction: string; tip: string | null }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hämtar publicerad receptöversättning för användarens språk, eller null om
|
||||
* svensk källa ska användas. En DB-runda för texterna + en för stegen.
|
||||
*/
|
||||
export async function resolveRecipeTranslation(
|
||||
db: Database,
|
||||
recipeId: string,
|
||||
languageTag: string,
|
||||
): Promise<ResolvedRecipeText | null> {
|
||||
const candidates = languageCandidates(languageTag);
|
||||
if (candidates.includes("sv")) return null;
|
||||
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(schema.recipeTranslations)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.recipeTranslations.recipeId, recipeId),
|
||||
inArray(schema.recipeTranslations.languageTag, candidates),
|
||||
eq(schema.recipeTranslations.status, "published"),
|
||||
),
|
||||
);
|
||||
if (rows.length === 0) return null;
|
||||
const best =
|
||||
candidates.map((c) => rows.find((r) => r.languageTag === c)).find(Boolean) ?? rows[0]!;
|
||||
|
||||
const stepRows = await db
|
||||
.select()
|
||||
.from(schema.recipeStepTranslations)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.recipeStepTranslations.recipeId, recipeId),
|
||||
eq(schema.recipeStepTranslations.languageTag, best.languageTag),
|
||||
),
|
||||
);
|
||||
return {
|
||||
language: best.languageTag,
|
||||
title: best.title,
|
||||
description: best.description,
|
||||
storageGuidance: best.storageGuidance,
|
||||
steps: new Map(stepRows.map((s) => [s.stepNumber, { instruction: s.instruction, tip: s.tip }])),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingrediensnamn för användarens språk: Map ingredientId -> namn.
|
||||
* Saknade id:n behåller svenskt namn (fallback sker hos anroparen).
|
||||
*/
|
||||
export async function resolveIngredientNames(
|
||||
db: Database,
|
||||
ingredientIds: string[],
|
||||
languageTag: string,
|
||||
): Promise<Map<string, string>> {
|
||||
const candidates = languageCandidates(languageTag);
|
||||
if (candidates.includes("sv") || ingredientIds.length === 0) return new Map();
|
||||
const rows = await db
|
||||
.select({
|
||||
ingredientId: schema.ingredientTranslations.ingredientId,
|
||||
languageTag: schema.ingredientTranslations.languageTag,
|
||||
name: schema.ingredientTranslations.name,
|
||||
})
|
||||
.from(schema.ingredientTranslations)
|
||||
.where(
|
||||
and(
|
||||
inArray(schema.ingredientTranslations.ingredientId, ingredientIds),
|
||||
inArray(schema.ingredientTranslations.languageTag, candidates),
|
||||
eq(schema.ingredientTranslations.status, "published"),
|
||||
),
|
||||
);
|
||||
const out = new Map<string, string>();
|
||||
for (const candidate of [...candidates].reverse()) {
|
||||
for (const r of rows) if (r.languageTag === candidate) out.set(r.ingredientId, r.name);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user