| |
| |
| |
| |
|
|
| import type { SupportedLanguage } from "./types.js"; |
|
|
| const LANGUAGE_DISPLAY_NAMES: Record<SupportedLanguage, string> = { |
| id: "Bahasa Indonesia", |
| zh: "Chinese", |
| ja: "Japanese", |
| fr: "French", |
| es: "Spanish", |
| }; |
|
|
| const SITE_NAME = "Hadad Darajat"; |
|
|
| export const siteConfig = { |
| name: SITE_NAME, |
| title: SITE_NAME, |
| description: "Personal Blog", |
| siteUrl: process.env.SITE_URL, |
| locale: "en-US", |
|
|
| author: { |
| name: SITE_NAME, |
| linkedin: "https://linkedin.com/in/hadadrjt", |
| }, |
|
|
| keywords: [ |
| "hadad", |
| "hadad darajat", |
| "hadadrjt", |
| "hadadxyz", |
| "hadad blog", |
| "hadad darajat blog", |
| "hadad personal blog", |
| "hadad darajat personal blog", |
| "blog", |
| "personal blog", |
| "technology", |
| "programming", |
| "software development", |
| "tech articles", |
| "huggingface", |
| "hugging face", |
| "hf", |
| "hugging face space", |
| ], |
|
|
| footer: { |
| copyrightYear: "2025", |
| copyrightText: "Copyright", |
| }, |
|
|
| messages: { |
| loading: "Please wait a moment...", |
| noPosts: "There are no post available at this time", |
| postNotFound: "Article not found", |
| backToHome: "Back to Home", |
| readMore: "Read more", |
| latestArticles: "Latest", |
| shareArticle: "Share this article", |
| previous: "Previous", |
| next: "Next", |
| analyzeWithAI: "Analyze this article", |
| analyzing: "Analyzing...", |
| analysisInfo: "AI Analysis", |
| analysisComplete: "Analysis Complete", |
| reasoning: "Reasoning", |
| closeAnalysis: "Close", |
| analysisError: "Failed to analyze article", |
| translateArticle: "Translate this article", |
| translating: "Translating...", |
| translationComplete: "Translation Complete", |
| translationInfo: "AI Translation", |
| selectLanguage: "Select Language", |
| translationError: "Failed to translate article", |
| closeTranslation: "Close", |
| translationReady: "Translation", |
| }, |
|
|
| defaults: { |
| postTitle: "Untitled", |
| postAuthor: "Anonymous", |
| postDescription: "", |
| }, |
|
|
| dateFormat: { |
| year: "numeric" as const, |
| month: "long" as const, |
| day: "numeric" as const, |
| }, |
|
|
| features: { |
| enableAIAnalysis: true, |
| enableTranslation: true, |
| }, |
|
|
| languages: { |
| id: { name: LANGUAGE_DISPLAY_NAMES["id"], flag: "🇮🇩" }, |
| zh: { name: LANGUAGE_DISPLAY_NAMES["zh"], flag: "🇨🇳" }, |
| ja: { name: LANGUAGE_DISPLAY_NAMES["ja"], flag: "🇯🇵" }, |
| fr: { name: LANGUAGE_DISPLAY_NAMES["fr"], flag: "🇫🇷" }, |
| es: { name: LANGUAGE_DISPLAY_NAMES["es"], flag: "🇪🇸" }, |
| }, |
|
|
| server: { |
| host: "0.0.0.0", |
| port: 3000, |
| jsonLimit: "2mb", |
| corsOptions: { |
| origin: true, |
| methods: [ |
| "GET", |
| "POST" |
| ], |
| allowedHeaders: ["Content-Type"], |
| }, |
| }, |
|
|
| files: { |
| markdownExtensionPattern: /\.(md|markdown)$/, |
| postsDirectoryName: "post", |
| indexHtmlFileName: "index.html", |
| clientDirectoryName: "client", |
| }, |
|
|
| fileWatcher: { |
| persistent: true, |
| ignoreInitial: true, |
| stabilityThreshold: 300, |
| pollInterval: 100, |
| }, |
|
|
| ai: { |
| model: "nvidia/nemotron-3-nano-30b-a3b", |
| maxTokens: 131072, |
| streamEnabled: true, |
| apiEndpoint: "/v1/chat/completions", |
|
|
| systemPrompts: { |
| analysis: `You are an AI content analysis engine embedded in a blog platform. |
| |
| Your role is to analyze articles objectively for readers. |
| |
| Do NOT address or speak to the author. |
| |
| Do NOT use second-person language such as "you", "your", or any motivational or coaching tone. |
| |
| Do NOT claim to represent the reader's perspective. Remember that you are only an AI assistant whose role is to help readers understand the content of the article. |
| |
| Position yourself as a reader who happens to come across an article from the blog, and provide an explanation stating whether the article is accurate or whether it needs to be rechecked. |
| |
| Your task is to analyze the article as a reader, not as someone providing improvements, and not as an instructor.`, |
|
|
| translation: (targetLanguage: SupportedLanguage): string => { |
| const targetLanguageName = LANGUAGE_DISPLAY_NAMES[targetLanguage]; |
| return `You are a professional translator specializing in translating English blog posts into "${targetLanguageName}". |
| |
| Your task is to translate the following article accurately while maintaining: |
| |
| 1. The original meaning and tone |
| |
| 2. Markdown formatting |
| |
| 3. Technical terms appropriately translated or kept in English if commonly used |
| |
| 4. Natural flow and readability in "${targetLanguageName}" |
| |
| Do NOT add explanations about the translation process. |
| |
| Do NOT modify the structure of the content. |
| |
| Translate only the text content, preserve all markdown syntax. |
| |
| Output the translated content directly. |
| |
| Exclude the following metadata when translating : |
| |
| --- |
| title: ..... |
| date: ..... |
| description: ..... |
| author: ..... |
| tags: ..... |
| --- |
| `; |
| }, |
| }, |
|
|
| userPrompts: { |
| analysis: (title: string, content: string): string => |
| `Please analyze this blog post titled "${title}":\n\n article:\n\n\n${content}`, |
| translation: (title: string, languageName: string, content: string): string => |
| `Please translate this blog post titled "${title}" into "${languageName}":\n\n article:\n\n\n${content}`, |
| }, |
| }, |
|
|
| seo: { |
| googleVerification: "m0OGuzVr40bOdghFXVbs3AYpfEcXnrRZ83RR1DCDvoc", |
| bingVerification: "867CFEF9C4B44CFD5EEA1A6C65ED3F6B", |
| defaultRobots: "index, follow", |
| articleRobots: "index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1", |
| twitterCardType: "summary_large_image", |
| maxKeywords: 10, |
| sitemapChangeFrequency: { |
| home: "daily", |
| post: "weekly", |
| }, |
| sitemapPriority: { |
| home: "1.0", |
| post: "0.8", |
| }, |
| metaTagPlaceholder: "<!--SEO_META_TAGS-->", |
| }, |
|
|
| logs: { |
| postsLoaded: (count: number): string => `Loaded ${count} posts`, |
| postRemoved: (slug: string): string => `Removed post: ${slug}`, |
| watchingDirectory: (path: string): string => `Watching for post changes in: ${path}`, |
| serverRunning: (host: string, port: number): string => `Server running at http://${host}:${port}`, |
| siteUrl: (url: string): string => `Site URL: ${url}`, |
| environment: (env: string): string => `Environment: ${env}`, |
| renderingPost: (slug: string): string => `Rendering post page for slug: ${slug}`, |
| foundIndexHtml: (path: string): string => `Found index.html at: ${path}`, |
| newPostDetected: "New post detected", |
| postUpdated: "Post updated", |
| postDeleted: "Post deleted", |
| }, |
|
|
| errors: { |
| aiServiceNotConfigured: "AI service is not configured", |
| aiResponseFailed: "Failed to get AI response", |
| noResponseAvailable: "No response available", |
| connectionError: "Connection error occurred", |
| articleRequired: "Article is required", |
| targetLanguageRequired: "Target language is required", |
| methodNotAllowed: "Method not allowed", |
| articleNotFound: "Article not found", |
| unknownError: "Unknown error", |
| internalServerError: "Internal Server Error", |
| templateNotLoaded: "Template not loaded", |
| emptyHtmlReturned: "Empty HTML returned from render post page", |
| serverErrorCouldNotRender: "Server Error: Could not render page", |
| errorGeneratingSitemap: "Error generating sitemap", |
| couldNotFindIndexHtml: "Could not find index.html in any expected location", |
| searchedPaths: "Searched paths:", |
| couldNotFindMetaTagInsertionPoint: "Could not find insertion point for meta tags", |
| loadingPosts: (error: unknown): string => |
| `Error loading posts: ${error instanceof Error ? error.message : String(error)}`, |
| loadingPost: (filename: string, error: unknown): string => |
| `Error loading post ${filename}: ${error instanceof Error ? error.message : String(error)}`, |
| renderingPostPage: (error: unknown): string => |
| `Error in renderPostPage: ${error instanceof Error ? error.message : String(error)}`, |
| renderingHomePage: (error: unknown): string => |
| `Error rendering home page: ${error instanceof Error ? error.message : String(error)}`, |
| fallbackRenderFailed: (error: unknown): string => |
| `Fallback render also failed: ${error instanceof Error ? error.message : String(error)}`, |
| aiApiError: (errorText: string): string => `AI API Error: ${errorText}`, |
| aiStreamError: (error: unknown): string => |
| `AI Stream Error: ${error instanceof Error ? error.message : String(error)}`, |
| serverError: (message: string): string => `Server Error: ${message}`, |
| readingIndexHtml: (path: string, error: unknown): string => |
| `Failed to read index.html from ${path}: ${error instanceof Error ? error.message : String(error)}`, |
| postNotFoundForSlug: (slug: string): string => `Post not found for slug: ${slug}`, |
| }, |
|
|
| streamEvents: { |
| done: "done", |
| error: "error", |
| reasoning: "reasoning", |
| content: "content", |
| }, |
|
|
| contentTypes: { |
| eventStream: "text/event-stream", |
| html: "text/html", |
| plain: "text/plain", |
| xml: "application/xml", |
| json: "application/json", |
| }, |
|
|
| httpHeaders: { |
| accelBuffering: "X-Accel-Buffering", |
| contentType: "Content-Type", |
| authorization: "Authorization", |
| }, |
| }; |
|
|
| export const getLanguageDisplayName = (language: SupportedLanguage): string => { |
| return LANGUAGE_DISPLAY_NAMES[language]; |
| }; |