Feature/post media translations (#42)
* chore: updated todo with translation ideas * feat: first take at the implementation of translations * fix: small addition for the translation feature * feat: support language switching in the editor and preview * feat: better handling of long bodies by not running them through a json envelope * fix: unknown macros have better fallback * feat: api for python to get translations * fix: strip dumb prefix of content in translation * feat: extend meta diff for translations * feat: hook up translations to rebuild-from-disk * feat: generation of the website prefers project language, falling back to canonical language * fix: crashes during rendering * feat: translation validation report * fix: made the translation validation actually work * chore: reorganization of menu * fix: some topics cleanup * chore: updated doc * feat: translations for media * feat: more aligned in UI/UX * feat: edit translations possible * chore: added full multi-language todo * chore: updated todo for clarity * feat: implementation of full multi-linguality * fix: page creation creates pages * fix: flags on every page * fix: better prompt * feat: made MCP server aware of language content * feat: python tools for translations * fix: better fill-in-translations * fix: better prompt for translation. maybe. * fix: losing posts from search due to translation process * fix: translation validation handles in-db content and fill-in of missing translations fixed to flush * fix: faster scanning for infilling of missing translations * chore: updated agent instructions * feat: calendar and tag cloud respect current language now * fix: retries going up * fix: got metadata-diff and rebuild into sync * fix: extended meta-diff for timestamps * fix: made website validation look at translated content, too * fix: multi-lingual search * chore: refactor Editor.tsx into two separate editors * feat: do language detection when no explicit language given --------- Co-authored-by: hugo <hugoms@me.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { eq } from 'drizzle-orm';
|
||||
import { getDatabase } from '../database';
|
||||
import { posts, projects } from '../database/schema';
|
||||
import { sanitizePicoTheme, type PicoThemeName } from '../shared/picoThemes';
|
||||
import { SUPPORTED_RENDER_LANGUAGES, type SupportedLanguage } from '../shared/i18n';
|
||||
import {
|
||||
normalizeTaxonomyTerm,
|
||||
normalizeNonEmptyTaxonomyTerm,
|
||||
@@ -29,6 +30,7 @@ export interface ProjectMetadata {
|
||||
categoryMetadata?: Record<string, CategoryMetadata>; // Per-category metadata for UI/rendering
|
||||
categorySettings?: Record<string, CategoryRenderSettings>; // Per-category list rendering preferences
|
||||
semanticSimilarityEnabled?: boolean; // Enable local ONNX embedding-based semantic similarity
|
||||
blogLanguages?: string[]; // Languages the blog is rendered in (mainLanguage is always included)
|
||||
}
|
||||
|
||||
export interface CategoryRenderSettings {
|
||||
@@ -103,6 +105,19 @@ function sanitizeCategoryTitle(value: unknown, fallback: string): string {
|
||||
|
||||
type RawCategoryMetadataInput = Record<string, CategoryMetadata | CategoryRenderSettings>;
|
||||
|
||||
const supportedLanguageSet = new Set<string>(SUPPORTED_RENDER_LANGUAGES);
|
||||
|
||||
function sanitizeBlogLanguages(value: unknown): string[] | undefined {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const filtered = value
|
||||
.filter((item): item is string => typeof item === 'string')
|
||||
.map((item) => item.trim().toLowerCase())
|
||||
.filter((item) => item.length > 0 && supportedLanguageSet.has(item));
|
||||
return filtered.length > 0 ? [...new Set(filtered)] : undefined;
|
||||
}
|
||||
|
||||
function normalizeProjectMetadata(metadata: ProjectMetadata): ProjectMetadata {
|
||||
const maxPostsPerPage = sanitizeMaxPostsPerPage(metadata.maxPostsPerPage);
|
||||
const publicUrl = sanitizePublicUrl(metadata.publicUrl);
|
||||
@@ -112,6 +127,7 @@ function normalizeProjectMetadata(metadata: ProjectMetadata): ProjectMetadata {
|
||||
const pythonRuntimeMode = metadata.pythonRuntimeMode === 'main-thread' ? 'main-thread' : 'webworker';
|
||||
const picoTheme = sanitizePicoTheme(metadata.picoTheme);
|
||||
const categoryMetadata = normalizeCategoryMetadata(metadata.categoryMetadata ?? metadata.categorySettings);
|
||||
const blogLanguages = sanitizeBlogLanguages(metadata.blogLanguages);
|
||||
return {
|
||||
...metadata,
|
||||
publicUrl,
|
||||
@@ -121,6 +137,7 @@ function normalizeProjectMetadata(metadata: ProjectMetadata): ProjectMetadata {
|
||||
picoTheme,
|
||||
categoryMetadata,
|
||||
categorySettings: undefined,
|
||||
blogLanguages,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -349,6 +366,7 @@ export class MetaEngine extends EventEmitter {
|
||||
picoTheme: normalizedUpdates.picoTheme,
|
||||
categoryMetadata: normalizedUpdates.categoryMetadata,
|
||||
semanticSimilarityEnabled: normalizedUpdates.semanticSimilarityEnabled,
|
||||
blogLanguages: normalizedUpdates.blogLanguages,
|
||||
});
|
||||
} else {
|
||||
this.projectMetadata = normalizeProjectMetadata({
|
||||
|
||||
Reference in New Issue
Block a user