import { Injectable } from '@nestjs/common'; import { errorMessages, logMessages, statusMessages } from './messages'; import { i18nStore } from './i18n.store'; import { DEFAULT_LANGUAGE } from '../common/constants'; // 使用常量定义的默认语言 @Injectable() export class I18nService { private readonly defaultLanguage = DEFAULT_LANGUAGE; // 使用常量定义的默认语言 public normalizeLanguage(lang?: string): string { let language = lang; if (!language) { const store = i18nStore.getStore(); language = store?.language; } if (!language) return this.defaultLanguage; // Normalize language codes (e.g., zh-CN -> zh, en-US -> en) const normalized = language.split('-')[0].toLowerCase(); return normalized; } getErrorMessage(key: string, language?: string): string { const lang = this.normalizeLanguage(language); return errorMessages[lang]?.[key] || errorMessages[this.defaultLanguage][key] || key; } getLogMessage(key: string, language?: string): string { const lang = this.normalizeLanguage(language); return logMessages[lang]?.[key] || logMessages[this.defaultLanguage][key] || key; } getStatusMessage(key: string, language?: string): string { const lang = this.normalizeLanguage(language); return statusMessages[lang]?.[key] || statusMessages[this.defaultLanguage][key] || key; } // 汎用メッセージ取得メソッド、順次検索 getMessage(key: string, language?: string): string { const lang = this.normalizeLanguage(language); // ステータスメッセージ、エラーメッセージ、ログメッセージの順に検索 return statusMessages[lang]?.[key] || statusMessages[this.defaultLanguage][key] || errorMessages[lang]?.[key] || errorMessages[this.defaultLanguage][key] || logMessages[lang]?.[key] || logMessages[this.defaultLanguage][key] || key; } // メッセージの取得とフォーマット formatMessage(key: string, args: Record, language?: string): string { let message = this.getMessage(key, language); for (const [argKey, argValue] of Object.entries(args)) { message = message.replace(new RegExp(`\\{${argKey}\\}`, 'g'), String(argValue)); } return message; } // サポートされている言語リストを取得 getSupportedLanguages(): string[] { return Object.keys(errorMessages); } // 言語がサポートされているか確認 isLanguageSupported(language: string): boolean { return this.getSupportedLanguages().includes(language); } // システムプロンプトを取得 getPrompt(lang: string = this.defaultLanguage, type: 'withContext' | 'withoutContext' = 'withContext', hasKnowledgeGroup: boolean = false): string { const language = this.normalizeLanguage(lang); const noMatchMsg = statusMessages[language]?.noMatchInKnowledgeGroup || statusMessages[this.defaultLanguage].noMatchInKnowledgeGroup; if (language === 'zh') { return type === 'withContext' ? ` 基于以下知识库内容回答用户问题。 ${hasKnowledgeGroup ? ` **重要提示**: 用户已选择特定知识组,请严格基于以下知识库内容回答。如果知识库中没有相关信息,请明确告知用户:"${noMatchMsg}",然后再提供答案。 ` : ''} 知识库内容: {context} 历史对话: {history} 用户问题:{question} 请用Chinese回答,并严格遵循以下 Markdown 格式要求: 1. **段落与结构**: - 使用清晰的段落分隔,每个要点之间空一行 - 使用标题(## 或 ###)组织长回答 2. **文本格式**: - 使用 **粗体** 强调重要概念和关键词 - 使用列表(- 或 1.)组织多个要点 - 使用 \`代码\` 标记技术术语、命令、文件名 3. **代码展示**: - 使用代码块展示代码,并指定语言: \`\`\`python def example(): return "示例" \`\`\` - 支持语言:python, javascript, typescript, java, bash, sql 等 4. **图表与可视化**: - 使用 Mermaid 语法绘制流程图、序列图等: \`\`\`mermaid graph LR A[开始] --> B[处理] B --> C[结束] \`\`\` - 适用场景:流程、架构、状态机、时序图 5. **其他要求**: - 回答精炼准确 - 多步骤操作使用有序列表 - 对比类信息建议用表格展示(如果适用) ` : ` 作为智能助手,请回答用户的问题。 历史对话: {history} 用户问题:{question} 请用Chinese回答。 `; } else if (language === 'ja') { return type === 'withContext' ? ` 以下のナレッジベースの内容に基づいて、ユーザーの質問に答えてください。 ${hasKnowledgeGroup ? ` **重要**: ユーザーが特定のナレッジグループを選択しました。以下のナレッジベースの内容に厳密に基づいて回答してください。関連情報がナレッジベースに見つからない場合は、回答を提供する前に、ユーザーに明示的に「${noMatchMsg}」と伝えてください。 ` : ''} ナレッジベースの内容: {context} 会話履歴: {history} ユーザーの質問:{question} 日本語で回答し、以下のMarkdown形式のガイドラインに厳密に従ってください。 1. **段落と構造**: - 明確な段落区切りを使用し、要点の間に空行を入れます - 見出し(## または ###)を使用して長い回答を整理します 2. **テキスト形式**: - **太字**を使用して重要な概念やキーワードを強調します - リスト(- または 1.)を使用して複数のポイントを整理します - \`コード\`を使用して技術用語、コマンド、ファイル名をマークします 3. **コード表示**: - 言語指定のあるコードブロックを使用します: \`\`\`python def example(): return "示例" \`\`\` - サポートされている言語:python, javascript, typescript, java, bash, sqlなど 4. **図とチャート**: - フローチャート、シーケンス図などにMermaid構文を使用します: \`\`\`mermaid graph LR A[開始] --> B[処理] B --> C[終了] \`\`\` - 使用例:プロセスフロー、アーキテクチャ図、状態遷移図、シーケンス図 5. **その他の要件**: - 回答は簡潔かつ明確にします - マルチステップ プロセスには番号付きリストを使用します - 比較情報には表を使用します(該当する場合) ` : ` インテリジェントなアシスタントとして、ユーザーの質問に答えてください。 会話履歴: {history} ユーザーの質問:{question} 日本語で回答してください。 `; } else { // Fallback to English for any other language return type === 'withContext' ? ` Answer the user's question based on the following knowledge base content. ${hasKnowledgeGroup ? ` **IMPORTANT**: The user has selected a specific knowledge group. Please answer strictly based on the knowledge base content below. If the relevant information is not found in the knowledge base, explicitly tell the user: "${noMatchMsg}", before providing an answer. ` : ''} Knowledge Base CONTENT: {context} Conversation history: {history} User question: {question} Please answer in English and strictly follow these Markdown formatting guidelines: 1. **Paragraphs & Structure**: - Use clear paragraph breaks with blank lines between key points - Use headings (## or ###) to organize longer answers 2. **Text Formatting**: - Use **bold** to emphasize important concepts and keywords - Use lists (- or 1.) to organize multiple points - Use \`code\` to mark technical terms, commands, file names 3. **Code Display**: - Use code blocks with language specification: \`\`\`python def example(): return "example" \`\`\` - Supported languages: python, javascript, typescript, java, bash, sql, etc. 4. **Diagrams & Charts**: - Use Mermaid syntax for flowcharts, sequence diagrams, etc.: \`\`\`mermaid graph LR A[Start] --> B[Process] B --> C[End] \`\`\` - Use cases: process flows, architecture diagrams, state diagrams, sequence diagrams 5. **Other Requirements**: - Keep answers concise and clear - Use numbered lists for multi-step processes - Use tables for comparison information (if applicable) ` : ` As an intelligent assistant, please answer the user's question. Conversation history: {history} User question: {question} Please answer in English. `; } } // タイトル生成用のプロンプトを取得 getDocumentTitlePrompt(lang: string = this.defaultLanguage, contentSample: string): string { const language = this.normalizeLanguage(lang); if (language === 'zh') { return `你是一个文档分析师。请阅读以下文本(文档开头部分),并生成一个简炼、专业的标题(不超过50个字符)。 只返回标题文本。不要包含任何解释性文字或前导词(如“标题是:”)。 语言:Chinese 文本内容: ${contentSample}`; } else if (language === 'ja') { return `あなたは文書分析の専門家です。以下のテキスト(文書の冒頭部分)を読み、簡潔で専門的なタイトル(50文字以内)を生成してください。 タイトルのみを返してください。前置きや説明は不要です。 言語:Japanese テキスト: ${contentSample}`; } else { return `You are a document analyzer. Read the following text (start of a document) and generate a concise, professional title (max 50 chars). Return ONLY the title text. No preamble like "The title is...". Language: English Text: ${contentSample}`; } } getChatTitlePrompt(lang: string = this.defaultLanguage, userMessage: string, aiResponse: string): string { const language = this.normalizeLanguage(lang); if (language === 'zh') { return `根据以下对话片段,生成一个简短、描述性的标题(不超过50个字符),总结讨论的主题。 只返回标题文本。不要包含任何前导词。 语言:Chinese 片段: 用户: ${userMessage} 助手: ${aiResponse}`; } else if (language === 'ja') { return `以下の会話のスニペットに基づいて、話題を要約する短く説明的なタイトル(50文字以内)を生成してください。 タイトルのみを返してください。前置きは不要です。 言語:Japanese スニペット: ユーザー: ${userMessage} アシスタント: ${aiResponse}`; } else { return `Based on the following conversation snippet, generate a short, descriptive title (max 50 chars) that summarizes the topic. Return ONLY the title. No preamble. Language: English Snippet: User: ${userMessage} Assistant: ${aiResponse}`; } } }