Files
Enterprise-Knowledge-Base/frontend/src/utils/chatMessageShared.ts
T
ochan.kwon db7cef5f17 feat(i18n): replace hardcoded Chinese strings with i18n calls across frontend
Replace all user-visible hardcoded Chinese strings in Vue/TS source files
with proper i18n function calls and add corresponding translation keys
to all 4 locale files (zh-CN, en-US, ko-KR, ru-RU).

Changes:
- Replace 80+ hardcoded Chinese strings across 30+ source files
- Add ~450 new translation keys per locale file
- Cover components, views, utils, hooks, and API modules
- Remove Chinese fallback patterns (|| '中文') from existing t() calls
- Add mermaid toolbar and ollama date format translations

Intentionally preserved (backend API values / backward compatibility):
- FAQ CSV column headers and parsing constants
- RelevanceLevel type and Record key mappings
- Graph database engine status comparison ('未启用')
- Language name labels ('简体中文')
2026-03-06 10:34:08 +08:00

57 lines
1.9 KiB
TypeScript

import i18n from '@/i18n';
const STREAMING_IMAGE_PLACEHOLDER = '<span class="streaming-image-loading"><span class="streaming-image-loading__skeleton"></span></span>';
export const replaceIncompleteImageWithPlaceholder = (content: string): string => {
if (!content) return '';
const lastImgStart = content.lastIndexOf('![');
if (lastImgStart < 0) return content;
const tail = content.slice(lastImgStart);
const hasImageOpen = tail.startsWith('![');
const hasBracketClose = tail.includes(']');
const hasParenOpen = tail.includes('(');
const hasParenClose = tail.includes(')');
if (!hasImageOpen) return content;
// Incomplete image syntax at stream tail, e.g. ![alt](local://...
if (!hasBracketClose || (hasParenOpen && !hasParenClose)) {
return content.slice(0, lastImgStart) + STREAMING_IMAGE_PLACEHOLDER;
}
return content;
};
export const formatManualTitle = (question?: string): string => {
if (!question) {
return i18n.global.t('chat.sessionExcerpt');
}
const condensed = question.replace(/\s+/g, ' ').trim();
if (!condensed) {
return i18n.global.t('chat.sessionExcerpt');
}
return condensed.length > 40 ? `${condensed.slice(0, 40)}...` : condensed;
};
export const buildManualMarkdown = (_question: string, answer: string): string => {
const safeAnswer = answer?.trim() || i18n.global.t('chat.noAnswerContent');
return `${safeAnswer}`;
};
export const copyTextToClipboard = async (content: string): Promise<void> => {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(content);
return;
}
const textArea = document.createElement('textarea');
textArea.value = content;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
};