0a9588abb7
- Add pagination support to findAll (page, limit query params) - Add findByTemplateId method to service - Add GET /by-template/:templateId endpoint to controller - Service already includes CRUD for QuestionBank and QuestionBankItem
348 lines
11 KiB
TypeScript
348 lines
11 KiB
TypeScript
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<string, any>,
|
||
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}`;
|
||
}
|
||
}
|
||
}
|