{error}
}Content contains inappropriate language
Content contains inappropriate language
{/if} ``` ## Edge Runtime Support [#edge-runtime-support] ### Vercel Edge Functions [#vercel-edge-functions] ```typescript import { checkBannedWords } from "@visulima/content-safety"; export const config = { runtime: "edge", }; export default async function handler(request: Request) { const { text } = await request.json(); const result = checkBannedWords(text); return new Response(JSON.stringify(result), { headers: { "content-type": "application/json" }, }); } ``` ### Cloudflare Workers [#cloudflare-workers] ```typescript import { checkBannedWords } from "@visulima/content-safety"; export default { async fetch(request: Request) { const { text } = await request.json(); const result = checkBannedWords(text); return new Response(JSON.stringify(result), { headers: { "content-type": "application/json" }, }); }, }; ``` ## Troubleshooting [#troubleshooting] ### Module Not Found [#module-not-found] If you encounter module resolution issues: 1. Clear your package manager cache: ```bash npm cache clean --force # or yarn cache clean # or pnpm store prune ``` 2. Delete `node_modules` and reinstall: ```bash rm -rf node_modules npm install ``` ### TypeScript Errors [#typescript-errors] If TypeScript can't find types: 1. Ensure `moduleResolution` is set to `"bundler"` or `"node16"` in `tsconfig.json`: ```json { "compilerOptions": { "moduleResolution": "bundler" } } ``` 2. Check that your TypeScript version is 5.0+: ```bash npx tsc --version ``` ### Build Errors [#build-errors] If you encounter build errors in older browsers: 1. Ensure your bundler is configured to transpile `node_modules`: ```javascript // vite.config.js export default { build: { target: "es2020", }, }; ``` ## Next Steps [#next-steps] * [Usage Guide](/docs/packages/content-safety/usage) - Learn how to use the library * [API Reference](/docs/packages/content-safety/api) - Detailed API documentation * [Supported Languages](/docs/packages/content-safety/languages) - View language support # Supported Languages (/docs/packages/content-safety/languages) # Supported Languages [#supported-languages] @visulima/content-safety includes banned word lists for **19 languages**, covering major languages from around the world. All languages are checked simultaneously when analyzing text. ## Language Coverage [#language-coverage] ### European Languages [#european-languages] #### English (en) [#english-en] * **Script**: Latin * **Coverage**: Comprehensive profanity, slurs, hate speech * **Variants**: Includes leet-speak (b1tch, etc.) * **Multi-word phrases**: Yes (e.g., "white trash") #### German (de) [#german-de] * **Script**: Latin (with umlauts) * **Coverage**: German profanity and slurs * **Regional variants**: Standard German #### Spanish (es) [#spanish-es] * **Script**: Latin (with diacritics) * **Coverage**: Spanish profanity and slurs * **Regional variants**: Multiple Spanish-speaking regions #### French (fr) [#french-fr] * **Script**: Latin (with diacritics) * **Coverage**: French profanity and slurs * **Regional variants**: European French #### Italian (it) [#italian-it] * **Script**: Latin * **Coverage**: Italian profanity and slurs #### Dutch (nl) [#dutch-nl] * **Script**: Latin * **Coverage**: Dutch profanity and slurs #### Polish (pl) [#polish-pl] * **Script**: Latin (with Polish diacritics) * **Coverage**: Polish profanity and slurs #### Portuguese (pt) [#portuguese-pt] * **Script**: Latin (with diacritics) * **Coverage**: Portuguese profanity and slurs * **Regional variants**: Both European and Brazilian Portuguese #### Swedish (sv) [#swedish-sv] * **Script**: Latin (with Swedish characters) * **Coverage**: Swedish profanity and slurs #### Russian (ru) [#russian-ru] * **Script**: Cyrillic * **Coverage**: Russian profanity and slurs * **Unicode support**: Full Cyrillic character handling #### Irish (ga) [#irish-ga] * **Script**: Latin * **Coverage**: Irish profanity and slurs ### Middle Eastern Languages [#middle-eastern-languages] #### Arabic (ar) [#arabic-ar] * **Script**: Arabic (RTL) * **Coverage**: Arabic profanity and slurs * **Script features**: Right-to-left text support * **Regional variants**: Modern Standard Arabic #### Persian/Farsi (fa) [#persianfarsi-fa] * **Script**: Perso-Arabic (RTL) * **Coverage**: Persian profanity and slurs * **Script features**: Right-to-left text support #### Turkish (tr) [#turkish-tr] * **Script**: Latin (with Turkish-specific letters) * **Coverage**: Turkish profanity and slurs #### Azerbaijani (az) [#azerbaijani-az] * **Script**: Latin * **Coverage**: Azerbaijani profanity and slurs ### Asian Languages [#asian-languages] #### Japanese (ja) [#japanese-ja] * **Script**: Mixed (Hiragana, Katakana, Kanji) * **Coverage**: Japanese profanity and slurs * **Word boundaries**: No word boundaries (CJK handling) #### Korean (ko) [#korean-ko] * **Script**: Hangul * **Coverage**: Korean profanity and slurs * **Word boundaries**: No word boundaries (CJK handling) #### Chinese (zh) [#chinese-zh] * **Script**: Simplified and Traditional Chinese * **Coverage**: Chinese profanity and slurs * **Word boundaries**: No word boundaries (CJK handling) #### Hindi (hi) [#hindi-hi] * **Script**: Devanagari * **Coverage**: Hindi profanity and slurs * **Script features**: Complex script support ## Language Detection [#language-detection] ### Automatic Multi-Language Checking [#automatic-multi-language-checking] The library automatically checks all languages simultaneously: ```typescript import { checkBannedWords } from "@visulima/content-safety"; // Mixed language text - all languages checked const result = checkBannedWords(` English bad word German bad word Japanese bad word `); // All languages will be detected console.log(result.matches.map((m) => m.language)); // ['en', 'de', 'ja'] ``` ### Language Attribution [#language-attribution] Each match includes the language code it was detected from: ```typescript const result = checkBannedWords("text with badword"); result.matches.forEach((match) => { console.log(`Found "${match.word}" from language: ${match.language}`); }); ``` ## Script Support [#script-support] ### Latin Scripts [#latin-scripts] Standard Latin alphabet with full diacritic support: * English, Spanish, French, Portuguese, etc. * Accents: é, ñ, ü, ø, etc. ### Cyrillic [#cyrillic] Full support for Cyrillic script: * Russian (Ru): А-Я, а-я * Unicode-aware word boundaries ### Arabic Script [#arabic-script] Right-to-left (RTL) text support: * Arabic (ar): ا-ي * Persian (fa): Perso-Arabic extensions * Proper handling of RTL text direction ### CJK (Chinese, Japanese, Korean) [#cjk-chinese-japanese-korean] Special handling for CJK scripts: * No word boundaries required (continuous text) * Character-level matching * Support for mixed Hiragana, Katakana, Kanji, Hangul ### Complex Scripts [#complex-scripts] Support for complex scripts with combining characters: * Devanagari (Hindi) * Thai * Other Indic scripts ## Unicode Normalization [#unicode-normalization] All text is normalized to NFC (Canonical Composition) form for consistent matching: ```typescript // Both forms will match identically checkBannedWords("café"); // NFC form checkBannedWords("café"); // NFD form (combining accent) ``` This ensures: * Consistent matching across different Unicode representations * Proper handling of accented characters * Correct detection regardless of input normalization ## Word Boundary Handling [#word-boundary-handling] ### Western Scripts (Latin, Cyrillic, Arabic, etc.) [#western-scripts-latin-cyrillic-arabic-etc] Uses Unicode-aware word boundaries: * `\p{L}` - Unicode letter property * `\p{N}` - Unicode number property * Respects word boundaries in all scripts ```typescript // Matches (standalone word) checkBannedWords("badword here"); // ✓ detected // May not match (part of compound word) checkBannedWords("notabadwordhere"); // depends on word list ``` ### CJK Scripts [#cjk-scripts] No word boundaries applied: * Characters can appear anywhere in continuous text * More sensitive matching for CJK content ```typescript // CJK example checkBannedWords("前badword後"); // ✓ detected ``` ## Data Sources [#data-sources] The banned word lists are curated from multiple sources: * **Manual curation**: Carefully reviewed lists * [google-profanity-words](https://github.com/coffee-and-fun/google-profanity-words): Community-maintained lists * [safetext](https://github.com/viddexa/safetext): Multi-language profanity database ## Adding Custom Words [#adding-custom-words] While the library doesn't support runtime customization, you can access the word lists: ```typescript import { BANNED_WORDS } from "@visulima/content-safety"; // View all languages console.log(Object.keys(BANNED_WORDS)); // ['ar', 'az', 'de', 'en', 'es', 'fa', 'fr', 'ga', 'hi', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'] // View English words (sensitive content - be careful) console.log(BANNED_WORDS.en); ``` ## Language Codes (ISO 639-1) [#language-codes-iso-639-1] | Code | Language | Native Name | | ---- | ----------- | ------------ | | ar | Arabic | العربية | | az | Azerbaijani | Azərbaycanca | | de | German | Deutsch | | en | English | English | | es | Spanish | Español | | fa | Persian | فارسی | | fr | French | Français | | ga | Irish | Gaeilge | | hi | Hindi | हिन्दी | | it | Italian | Italiano | | ja | Japanese | 日本語 | | ko | Korean | 한국어 | | nl | Dutch | Nederlands | | pl | Polish | Polski | | pt | Portuguese | Português | | ru | Russian | Русский | | sv | Swedish | Svenska | | tr | Turkish | Türkçe | | zh | Chinese | 中文 | ## Contributing Languages [#contributing-languages] To add support for additional languages: 1. Create a new file: `src/words/{language-code}.ts` 2. Export an array of banned words: ```typescript const words: readonly string[] = [ // word list here ]; export default words; ``` 3. Add to `src/banned-words.ts`: ```typescript import newLang from "./words/new-lang"; export const BANNED_WORDS = { // ... "new-lang": newLang, }; ``` 4. Submit a pull request to [visulima/visulima](https://github.com/visulima/visulima) ## Next Steps [#next-steps] * [Usage Guide](/docs/packages/content-safety/usage) - Learn how to use the library * [API Reference](/docs/packages/content-safety/api) - Complete API documentation * [Installation](/docs/packages/content-safety/installation) - Setup instructions # Usage Guide (/docs/packages/content-safety/usage) # Usage Guide [#usage-guide] ## Basic Checking [#basic-checking] The primary function is `checkBannedWords()`, which analyzes text and returns detailed match information: ```typescript import { checkBannedWords } from "@visulima/content-safety"; const result = checkBannedWords("Your text here"); console.log(result.hasBannedWords); // boolean console.log(result.matches); // BannedWordMatch[] ``` ### Clean Text [#clean-text] When text contains no banned words: ```typescript const result = checkBannedWords("Hello, how are you today?"); console.log(result); // { // hasBannedWords: false, // matches: [] // } ``` ### Text with Banned Words [#text-with-banned-words] When banned words are detected: ```typescript const result = checkBannedWords("This contains badword"); console.log(result); // { // hasBannedWords: true, // matches: [ // { // word: "badword", // startIndex: 14, // endIndex: 21, // language: "en" // } // ] // } ``` ## Content Moderation [#content-moderation] ### Simple Rejection [#simple-rejection] Reject content containing banned words: ```typescript import { checkBannedWords } from "@visulima/content-safety"; function moderateContent(userInput: string): { allowed: boolean; reason?: string } { const result = checkBannedWords(userInput); if (result.hasBannedWords) { return { allowed: false, reason: "Content contains inappropriate language", }; } return { allowed: true }; } // Usage const submission = moderateContent("User's message here"); if (!submission.allowed) { console.log(submission.reason); } ``` ### Detailed Feedback [#detailed-feedback] Provide specific feedback about violations: ```typescript function moderateWithDetails(text: string) { const result = checkBannedWords(text); if (!result.hasBannedWords) { return { allowed: true }; } return { allowed: false, reason: `Found ${result.matches.length} inappropriate word(s)`, matches: result.matches.map((m) => ({ position: m.startIndex, word: m.word, language: m.language, })), }; } ``` ### Multi-Language Support [#multi-language-support] The library automatically checks all languages simultaneously: ```typescript // English checkBannedWords("bad English word"); // detected // German checkBannedWords("bad German word"); // detected // Japanese checkBannedWords("bad Japanese word"); // detected // Mixed languages in same text checkBannedWords("English bad word and German bad word"); // both detected ``` ## Text Censoring [#text-censoring] ### Replace with Asterisks [#replace-with-asterisks] Replace banned words with asterisks: ```typescript import { checkBannedWords } from "@visulima/content-safety"; function censorText(text: string): string { const result = checkBannedWords(text); if (!result.hasBannedWords) { return text; } let censored = text; // Process matches in reverse order to maintain indices for (const match of [...result.matches].reverse()) { const replacement = "*".repeat(match.word.length); censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } return censored; } // Usage console.log(censorText("This is badword text")); // "This is ******* text" ``` ### Replace with \[CENSORED] [#replace-with-censored] Replace banned words with a placeholder: ```typescript function censorWithPlaceholder(text: string): string { const result = checkBannedWords(text); if (!result.hasBannedWords) { return text; } let censored = text; let offset = 0; for (const match of result.matches) { const before = censored.slice(0, match.startIndex + offset); const after = censored.slice(match.endIndex + offset); censored = before + "[CENSORED]" + after; // Adjust offset for length difference offset += "[CENSORED]".length - match.word.length; } return censored; } // Usage console.log(censorWithPlaceholder("This badword is bad")); // "This [CENSORED] is bad" ``` ### Partial Censoring [#partial-censoring] Censor only the middle characters: ```typescript function partialCensor(text: string): string { const result = checkBannedWords(text); if (!result.hasBannedWords) { return text; } let censored = text; for (const match of [...result.matches].reverse()) { const word = match.word; if (word.length <= 2) { // Fully censor short words const replacement = "*".repeat(word.length); censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } else { // Keep first and last character const replacement = word[0] + "*".repeat(word.length - 2) + word[word.length - 1]; censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } } return censored; } // Usage console.log(partialCensor("This badword is bad")); // "This b*****d is bad" ``` ## Real-Time Validation [#real-time-validation] ### Form Validation [#form-validation] Validate input as the user types: ```typescript function validateInput(text: string) { const result = checkBannedWords(text); return { isValid: !result.hasBannedWords, errors: result.hasBannedWords ? ["Content contains inappropriate language"] : [], matches: result.matches, }; } // Usage in a form handler const validation = validateInput(formData.comment); if (!validation.isValid) { showErrors(validation.errors); } ``` ### Highlighting Matches [#highlighting-matches] Create UI highlighting for banned words: ```typescript interface HighlightSegment { text: string; isBanned: boolean; language?: string; } function highlightBannedWords(text: string): HighlightSegment[] { const result = checkBannedWords(text); if (!result.hasBannedWords) { return [{ text, isBanned: false }]; } const segments: HighlightSegment[] = []; let lastIndex = 0; for (const match of result.matches) { // Add clean text before match if (match.startIndex > lastIndex) { segments.push({ text: text.slice(lastIndex, match.startIndex), isBanned: false }); } // Add banned word segments.push({ text: match.word, isBanned: true, language: match.language }); lastIndex = match.endIndex; } // Add remaining clean text if (lastIndex < text.length) { segments.push({ text: text.slice(lastIndex), isBanned: false }); } return segments; } // Usage in React function TextHighlighter({ text }: { text: string }) { const segments = highlightBannedWords(text); return (