强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Hunspell 拼写检查完全教程 / 第 10 章:最佳实践

第 10 章:最佳实践

10.1 词典维护最佳实践

10.1.1 词典版本控制

# 项目词典应纳入版本控制
# .gitignore 中不要忽略词典文件

# 词典目录结构推荐
.hunspell/
├── project.dic          # 项目主词典
├── project.aff          # 项目词缀文件(可选)
├── CHANGELOG.md         # 词典变更记录
├── scripts/
│   ├── validate.sh      # 验证脚本
│   ├── merge.sh         # 合并脚本
│   └── extract.sh       # 术语提取脚本
└── categories/
    ├── tech.dic          # 技术术语
    ├── company.dic       # 公司/产品名
    └── abbreviations.dic # 缩略语

10.1.2 词典命名规范

文件命名规则示例
主词典<项目名>.dicmyproject.dic
分类词典<类别>.dictech.dic, people.dic
个人词典~/.hunspell_personal系统级
临时词典.tmp.dic不提交到 Git

10.1.3 词典审核流程

#!/bin/bash
# review_dict.sh — 词典审核脚本
# 检查词典中的潜在问题

DICT_FILE="$1"

echo "=== 词典审核: $DICT_FILE ==="

# 1. 检查是否有过短的词(可能是误添加)
echo "[过短词检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
    awk '{if (length($0) <= 2) print "  警告: " $0}' | head -10

# 2. 检查是否有大写开头的词(可能是专有名词误加)
echo "[大写开头词检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
    grep "^[A-Z]" | head -10 | sed 's/^/  /'

# 3. 检查是否有包含特殊字符的词
echo "[特殊字符检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
    grep -P "[^a-zA-Z0-9/-]" | head -10 | sed 's/^/  /'

# 4. 检查 affix 标志是否有效
echo "[affix 标志检查]"
grep -v "^#" "$DICT_FILE" | grep -v "^$" | \
    grep "/[^A-Z0-9]" | head -5 | sed 's/^/  可能无效的标志: /'

echo ""
echo "=== 审核完成 ==="

10.1.4 定期词典清理

#!/bin/bash
# clean_dict.sh — 清理词典中的冗余/错误条目
# 用法: ./clean_dict.sh <词典文件>

DICT_FILE="$1"
BACKUP="${DICT_FILE}.bak.$(date +%Y%m%d)"

# 备份
cp "$DICT_FILE" "$BACKUP"
echo "已备份: $BACKUP"

# 清理步骤
{
    # 保留第一行(大小声明,如果有)
    head -1 "$DICT_FILE"
    
    # 清理其余行
    tail -n +2 "$DICT_FILE" | \
        grep -v "^$" |              # 删除空行
        grep -v "^#" |              # 删除注释
        sed 's/[[:space:]]*$//' |   # 删除行尾空格
        sort -u                     # 去重排序
} > "${DICT_FILE}.clean"

# 统计
ORIGINAL=$(grep -cv "^#\|^$" "$DICT_FILE")
CLEAN=$(grep -cv "^#\|^$" "${DICT_FILE}.clean")
REMOVED=$((ORIGINAL - CLEAN))

echo "清理前: $ORIGINAL 条"
echo "清理后: $CLEAN 条"
echo "移除:   $REMOVED 条"

# 替换
mv "${DICT_FILE}.clean" "$DICT_FILE"
echo "已更新: $DICT_FILE"

10.1.5 词典文档化

<!-- .hunspell/CHANGELOG.md -->

# 词典变更记录

## 2024-01-15
- 添加 42 个 Kubernetes 相关术语
- 添加 15 个内部产品名称
- 移除 3 个废弃产品名

## 2024-01-01
- 初始化项目词典
- 从项目文档中提取 120 个术语

## 统计
- 当前词条数: 247
- 技术术语: 189
- 产品名称: 35
- 缩略语: 23

10.2 性能优化

10.2.1 词典大小声明优化

# 第一行声明词典大小,影响哈希表初始化
# 应该尽量接近实际词条数

# 查看实际词条数
grep -cv "^#\|^$" project.dic

# 更新声明
sed -i "1s/.*/$(grep -cv '^#\|^$' project.dic)/" project.dic

10.2.2 大文件检查优化

# 方案 1: 并行检查
find . -name "*.md" -print0 | \
    xargs -0 -P $(nproc) -I{} \
    sh -c 'hunspell -l -d en_US -p .hunspell/project.dic "{}"'

# 方案 2: 先提取文本,再批量检查
cat *.md | \
    grep -v "^#\|^\`\`\`\|^$" | \
    hunspell -l -d en_US -p .hunspell/project.dic | \
    sort -u

# 方案 3: 缓存检查结果
CACHE_FILE="/tmp/spellcheck_cache.txt"
find . -name "*.md" -newer "$CACHE_FILE" -exec \
    hunspell -l -d en_US -p .hunspell/project.dic {} + \
    >> "$CACHE_FILE"
sort -u "$CACHE_FILE"

10.2.3 内存使用优化

# 对于大型词典,限制并发进程数
# 避免同时加载多个词典实例

# 使用 -B 参数限制缓冲区大小(如果支持)
# hunspell -B 4096 ...

10.2.4 选择性检查

#!/bin/bash
# smart_check.sh — 智能拼写检查
# 只检查自上次构建以来变更的文件

CHECKED_FILES="/tmp/checked_files.txt"
touch "$CHECKED_FILES"

find . -name "*.md" -newer "$CHECKED_FILES" -print0 | \
while IFS= read -r -d '' file; do
    echo "检查: $file"
    hunspell -l -d en_US -p .hunspell/project.dic "$file" | sort -u
    # 标记为已检查
    touch -r "$file" "$CHECKED_FILES"
done

10.2.5 批处理模式优化

# 合并多个文件一次检查(减少进程启动开销)
find . -name "*.md" -exec cat {} + | \
    hunspell -l -d en_US -p .hunspell/project.dic | \
    sort -u > /tmp/all_errors.txt

# 然后用 grep 定位每个文件中的错误
while read error; do
    grep -rl "$error" *.md
done < /tmp/all_errors.txt

10.3 误报处理

10.3.1 常见误报类型

类型示例处理方法
专有名词Hunspell, GitHub添加到项目词典
技术术语API, JSON, HTTP添加到项目词典
缩略语URL, HTML, CSS添加到项目词典
代码片段function(), const x = 1正则排除
URL 链接https://example.com正则排除
版本号v1.2.3, 2024.01正则排除
路径/usr/local/bin正则排除
邮箱user@example.com正则排除

10.3.2 正则排除策略

# 使用 grep 预处理,排除特定模式
cat document.md | \
    grep -v '```' |                  # 排除代码块
    grep -v '^\s*#' |                # 排除标题(可选)
    sed 's/`[^`]*`//g' |             # 排除行内代码
    sed 's/https\?:[^ )]*//g' |      # 排除 URL
    sed 's/[a-zA-Z0-9._%+-]\+@[a-zA-Z0-9.-]\+\.[a-zA-Z]\{2,\}//g' |  # 排除邮箱
    sed 's/v\?[0-9]\+\.[0-9]\+\.[0-9]\+//g' |  # 排除版本号
    hunspell -l -d en_US -p .hunspell/project.dic

10.3.3 Python 预处理

#!/usr/bin/env python3
"""拼写检查预处理 — 排除特定模式"""
import re
import subprocess

def preprocess_text(text: str) -> str:
    """预处理文本,排除不需检查的内容"""
    # 排除代码块
    text = re.sub(r'```[\s\S]*?```', '', text)
    # 排除行内代码
    text = re.sub(r'`[^`]+`', '', text)
    # 排除 URL
    text = re.sub(r'https?://\S+', '', text)
    # 排除邮箱
    text = re.sub(r'\S+@\S+\.\S+', '', text)
    # 排除版本号
    text = re.sub(r'v?\d+\.\d+\.\d+', '', text)
    # 排除路径
    text = re.sub(r'/\S+', '', text)
    # 排除 Markdown 链接文本中的 URL
    text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
    # 排除 HTML 标签
    text = re.sub(r'<[^>]+>', '', text)
    return text

def spellcheck_with_preprocessing(file_path: str, dictionary: str = "en_US",
                                   personal_dict: str = None) -> list[str]:
    """带预处理的拼写检查"""
    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()
    
    processed = preprocess_text(text)
    
    cmd = ["hunspell", "-l", "-d", dictionary]
    if personal_dict:
        cmd.extend(["-p", personal_dict])
    
    result = subprocess.run(
        cmd, input=processed,
        capture_output=True, text=True
    )
    
    return [w for w in result.stdout.strip().split("\n") if w]

# 使用
errors = spellcheck_with_preprocessing(
    "document.md",
    personal_dict=".hunspell/project.dic"
)
print(f"发现 {len(errors)} 个错误")

10.3.4 项目词典增量更新

#!/bin/bash
# update_dict.sh — 增量更新项目词典
# 用法: ./update_dict.sh <新词文件> [词典文件]

NEW_WORDS="$1"
DICT_FILE="${2:-.hunspell/project.dic}"

echo "=== 增量更新词典 ==="
echo "新词文件: $NEW_WORDS"
echo "词典文件: $DICT_FILE"

# 备份
cp "$DICT_FILE" "${DICT_FILE}.bak"

# 添加新词(跳过已存在的)
ADDED=0
while read word; do
    # 跳过空行和注释
    [[ -z "$word" || "$word" =~ ^# ]] && continue
    
    # 检查是否已存在
    if ! grep -q "^${word}$" "$DICT_FILE"; then
        echo "$word" >> "$DICT_FILE"
        ADDED=$((ADDED + 1))
    fi
done < "$NEW_WORDS"

# 更新大小声明(如果有)
TOTAL=$(grep -cv "^#\|^$" "$DICT_FILE")
sed -i "1s/.*/$TOTAL/" "$DICT_FILE"

echo "新增: $ADDED 个词条"
echo "总计: $TOTAL 个词条"

10.3.5 误报白名单模式

// cspell.json 白名单配置
{
  "version": "0.2",
  
  // 全局忽略模式
  "ignoreRegExpList": [
    "/`[^`]+`/g",                    // 行内代码
    "/```[\\s\\S]*?```/g",            // 代码块
    "/https?:\\/\\/[^\\s]+/g",        // URL
    "/[a-f0-9]{7,40}/gi",            // Git commit hash
    "/v\\d+\\.\\d+\\.\\d+/g",        // 版本号
    "/\\b[A-Z][a-z]*[A-Z][a-z]*\\b/g" // camelCase(可选)
  ],
  
  // 文件级忽略
  "ignorePaths": [
    "node_modules",
    "package-lock.json",
    "*.min.js",
    "*.map",
    ".git"
  ],
  
  // 单词级忽略
  "words": [
    "Hunspell",
    "Docker",
    "Kubernetes"
  ],
  
  // 标记为错误的词(即使在词典中)
  "flagWords": [
    "teh",
    "adn",
    "hte"
  ]
}

10.4 多语言策略

10.4.1 多语言文档处理

#!/usr/bin/env python3
"""多语言文档拼写检查"""
import re
import subprocess
from langdetect import detect

def detect_language(text_segment: str) -> str:
    """检测文本段的语言"""
    try:
        lang = detect(text_segment)
        # 映射到 Hunspell 词典代码
        lang_map = {
            'en': 'en_US', 'fr': 'fr', 'de': 'de_DE',
            'es': 'es_ES', 'pt': 'pt_BR', 'it': 'it',
            'ru': 'ru_RU', 'zh-cn': 'zh_CN', 'ja': 'ja',
        }
        return lang_map.get(lang, 'en_US')
    except:
        return 'en_US'

def multilingual_spellcheck(file_path: str) -> dict[str, list[str]]:
    """多语言拼写检查"""
    with open(file_path, 'r', encoding='utf-8') as f:
        text = f.read()
    
    # 按段落分割
    paragraphs = re.split(r'\n\s*\n', text)
    
    all_errors = {}
    for i, para in enumerate(paragraphs):
        if len(para.strip()) < 10:
            continue
        
        # 检测语言
        lang = detect_language(para)
        
        # 检查拼写
        cmd = ["hunspell", "-l", "-d", lang]
        result = subprocess.run(cmd, input=para, capture_output=True, text=True)
        
        errors = [w for w in result.stdout.strip().split("\n") if w]
        if errors:
            if lang not in all_errors:
                all_errors[lang] = []
            all_errors[lang].extend(errors)
    
    # 去重
    for lang in all_errors:
        all_errors[lang] = sorted(set(all_errors[lang]))
    
    return all_errors

# 使用
errors = multilingual_spellcheck("multilingual_doc.md")
for lang, words in errors.items():
    print(f"\n{lang} 错误 ({len(words)} 个):")
    for word in words[:10]:
        print(f"  → {word}")

10.4.2 语言切换标记

# 使用 HTML lang 属性标记语言
# <span lang="en">English text</span>
# <span lang="zh">中文文本</span>

# 使用 Markdown 扩展
# <!-- lang:en -->
# This is English text.
# <!-- lang:fr -->
# Ceci est du texte français.

10.4.3 词典合并策略

#!/bin/bash
# merge_multilingual.sh — 合并多语言词典
# 用于混合语言文档

LANG1_DICT="$1"    # 如 en_US
LANG2_DICT="$2"    # 如 fr
OUTPUT_DICT="$3"   # 输出词典

echo "合并: $LANG1_DICT + $LANG2_DICT$OUTPUT_DICT"

# 提取两个词典的词根
{
    # 去掉 affix 标志,只保留词根
    grep -v "^#\|^$" "$LANG1_DICT.dic" | sed 's/\/.*//' | sort -u
    grep -v "^#\|^$" "$LANG2_DICT.dic" | sed 's/\/.*//' | sort -u
} | sort -u > "$OUTPUT_DICT.dic"

# 合并 affix 文件(使用第一个语言的 affix)
cp "$LANG1_DICT.aff" "$OUTPUT_DICT.aff"

# 更新大小声明
TOTAL=$(wc -l < "$OUTPUT_DICT.dic")
sed -i "1i $TOTAL" "$OUTPUT_DICT.dic"

echo "合并完成: $TOTAL 个词条"

10.4.4 多语言配置最佳实践

场景策略实现方式
单语言文档使用对应词典-d en_US
双语混合合并词典词典合并脚本
多语种文档语言检测 + 切换langdetect + hunspell
团队协作统一项目词典.hunspell/project.dic
CI/CD按文件类型检查文件模式匹配

10.5 生产环境部署

10.5.1 微服务架构

# docker-compose.yml — 拼写检查微服务
version: '3.8'
services:
  spellcheck:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - ./dictionaries:/opt/dictionaries:ro
    environment:
      - HUNSPELL_DICT_PATH=/opt/dictionaries
      - DEFAULT_LANG=en_US
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 256M
          cpus: '0.5'

10.5.2 缓存策略

#!/usr/bin/env python3
"""带缓存的拼写检查服务"""
from functools import lru_cache
import hashlib

class CachedSpellChecker:
    """缓存拼写检查结果,避免重复检查相同单词"""
    
    def __init__(self, dictionary: str = "en_US"):
        self.dictionary = dictionary
        self._cache = {}
        self._cache_hits = 0
        self._cache_misses = 0
    
    def _check_uncached(self, word: str) -> bool:
        """实际调用 Hunspell 检查"""
        import subprocess
        result = subprocess.run(
            ["hunspell", "-d", self.dictionary, "-l"],
            input=word, capture_output=True, text=True
        )
        return word not in result.stdout
    
    def check(self, word: str) -> bool:
        """带缓存的检查"""
        # 使用小写作为缓存键(如果不需要大小写敏感)
        key = word.lower()
        
        if key in self._cache:
            self._cache_hits += 1
            return self._cache[key]
        
        self._cache_misses += 1
        result = self._check_uncached(word)
        self._cache[key] = result
        return result
    
    @property
    def cache_stats(self) -> dict:
        total = self._cache_hits + self._cache_misses
        return {
            "size": len(self._cache),
            "hits": self._cache_hits,
            "misses": self._cache_misses,
            "hit_rate": self._cache_hits / total if total > 0 else 0
        }

# 使用
checker = CachedSpellChecker("en_US")

# 批量检查
words = ["hello", "world", "helo", "hello", "world"]  # hello/world 会命中缓存
for word in words:
    checker.check(word)

print(checker.cache_stats)
# {'size': 3, 'hits': 2, 'misses': 3, 'hit_rate': 0.4}

10.5.3 监控与告警

#!/usr/bin/env python3
"""拼写检查服务监控"""
import time
from dataclasses import dataclass, field
from typing import Optional

@dataclass
class SpellCheckMetrics:
    """拼写检查指标"""
    total_checks: int = 0
    total_errors: int = 0
    total_time_ms: float = 0
    cache_hits: int = 0
    cache_misses: int = 0
    errors_by_language: dict = field(default_factory=dict)
    
    @property
    def error_rate(self) -> float:
        return self.total_errors / self.total_checks if self.total_checks > 0 else 0
    
    @property
    def avg_check_time_ms(self) -> float:
        return self.total_time_ms / self.total_checks if self.total_checks > 0 else 0
    
    @property
    def cache_hit_rate(self) -> float:
        total = self.cache_hits + self.cache_misses
        return self.cache_hits / total if total > 0 else 0

class MonitoredSpellChecker:
    """带监控的拼写检查器"""
    
    def __init__(self, checker):
        self.checker = checker
        self.metrics = SpellCheckMetrics()
        self._start_time = None
    
    def check(self, word: str, language: str = "en") -> bool:
        start = time.time()
        result = self.checker.check(word)
        elapsed = (time.time() - start) * 1000
        
        self.metrics.total_checks += 1
        self.metrics.total_time_ms += elapsed
        
        if not result:
            self.metrics.total_errors += 1
            self.metrics.errors_by_language[language] = \
                self.metrics.errors_by_language.get(language, 0) + 1
        
        return result
    
    def get_metrics(self) -> dict:
        return {
            "total_checks": self.metrics.total_checks,
            "total_errors": self.metrics.total_errors,
            "error_rate": f"{self.metrics.error_rate:.2%}",
            "avg_time_ms": f"{self.metrics.avg_check_time_ms:.2f}",
            "cache_hit_rate": f"{self.metrics.cache_hit_rate:.2%}",
            "errors_by_language": self.metrics.errors_by_language
        }

# Prometheus 指标输出示例
def prometheus_metrics(metrics: dict) -> str:
    """生成 Prometheus 格式指标"""
    lines = [
        f'# HELP spellcheck_total Total spell checks performed',
        f'# TYPE spellcheck_total counter',
        f'spellcheck_total {metrics["total_checks"]}',
        f'# HELP spellcheck_errors_total Total spelling errors found',
        f'# TYPE spellcheck_errors_total counter',
        f'spellcheck_errors_total {metrics["total_errors"]}',
        f'# HELP spellcheck_duration_seconds Average check duration',
        f'# TYPE spellcheck_duration_seconds gauge',
        f'spellcheck_duration_seconds {float(metrics["avg_time_ms"]) / 1000:.6f}',
    ]
    return "\n".join(lines)

10.5.4 高可用部署

# Kubernetes 部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spellcheck-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spellcheck
  template:
    metadata:
      labels:
        app: spellcheck
    spec:
      containers:
        - name: spellcheck
          image: spellcheck:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          volumeMounts:
            - name: dictionaries
              mountPath: /opt/dictionaries
              readOnly: true
      volumes:
        - name: dictionaries
          configMap:
            name: spellcheck-dictionaries
---
apiVersion: v1
kind: Service
metadata:
  name: spellcheck-service
spec:
  selector:
    app: spellcheck
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

10.6 团队协作

10.6.1 词典所有权

# .github/CODEOWNERS
# 词典文件变更需要特定团队审核
.hunspell/ @docs-team @tech-leads
.hunspell/project.dic @docs-team

10.6.2 词典更新流程

# 1. 创建分支
git checkout -b feat/add-project-terms

# 2. 添加新词到词典
echo "Kubernetes" >> .hunspell/project.dic
echo "Helm" >> .hunspell/project.dic

# 3. 验证词典
./.hunspell/scripts/validate.sh .hunspell/project.dic

# 4. 测试拼写检查
hunspell -d en_US -p .hunspell/project.dic -l docs/*.md

# 5. 提交 PR
git add .hunspell/project.dic
git commit -m "feat: add Kubernetes and Helm to project dictionary"
git push origin feat/add-project-terms

10.6.3 团队词典规范

<!-- .hunspell/CONTRIBUTING.md -->

# 项目词典贡献指南

## 添加新词

1. 确认该词确实需要添加(不是临时拼写)
2. 检查该词是否已存在
3. 将词添加到正确的分类词典
4. 如果需要 affix 标志,参考现有模式

## 命名规范

- 公司/产品名:保持官方大小写(如 `GitHub` 而非 `github`- 技术术语:使用最常见的拼写形式
- 缩略语:使用大写形式(如 `API` 而非 `api`
## 不应添加的词

- 临时变量名
- 个人名字(除非频繁出现)
- 明显的拼写错误

10.7 安全考虑

10.7.1 词典文件安全

# 词典文件权限设置
chmod 644 .hunspell/project.dic   # 所有者可写,其他人只读
chmod 644 .hunspell/project.aff

# 避免将敏感信息放入词典
# 不要添加:密码、API 密钥、内部代号

10.7.2 输入验证

def sanitize_input(text: str, max_length: int = 10000) -> str:
    """清理用户输入,防止注入攻击"""
    # 限制输入长度
    if len(text) > max_length:
        text = text[:max_length]
    
    # 移除控制字符
    text = ''.join(c for c in text if c.isprintable() or c in '\n\r\t')
    
    return text

10.7.3 资源限制

import signal

class TimeoutError(Exception):
    pass

def timeout_handler(signum, frame):
    raise TimeoutError("拼写检查超时")

def check_with_timeout(text: str, timeout: int = 30) -> list[str]:
    """带超时的拼写检查"""
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout)
    
    try:
        result = subprocess.run(
            ["hunspell", "-l", "-d", "en_US"],
            input=text, capture_output=True, text=True,
            timeout=timeout
        )
        return [w for w in result.stdout.strip().split("\n") if w]
    except subprocess.TimeoutExpired:
        return ["错误:检查超时"]
    finally:
        signal.alarm(0)

10.8 故障排查

10.8.1 常见问题及解决方案

问题原因解决方案
找不到词典路径错误或未安装hunspell -D 查看搜索路径
编码错误文件编码与词典不匹配检查 SET UTF-8,用 file -i 验证
建议为空TRY 列表缺失.aff 文件中添加 TRY
性能差词典大小声明不准更新第一行数字
复合词误报复合词规则不完善添加 COMPOUNDRULE
专有名词报错未添加到词典添加到项目词典

10.8.2 诊断脚本

#!/bin/bash
# diagnose.sh — Hunspell 诊断脚本
echo "=== Hunspell 环境诊断 ==="

echo "[版本信息]"
hunspell --version 2>&1 | head -3

echo ""
echo "[词典搜索路径]"
hunspell -D 2>&1 | head -20

echo ""
echo "[可用词典]"
hunspell -D 2>&1 | grep -v "^搜索" | grep -v "^$" | head -10

echo ""
echo "[系统词典目录]"
ls -la /usr/share/hunspell/ 2>/dev/null | head -10

echo ""
echo "[用户词典]"
ls -la ~/.hunspell* 2>/dev/null || echo "  未找到用户词典"

echo ""
echo "[环境变量]"
echo "  DICPATH=${DICPATH:-未设置}"
echo "  LANG=${LANG:-未设置}"
echo "  LC_ALL=${LC_ALL:-未设置}"

echo ""
echo "[基本功能测试]"
echo "hello" | hunspell -d en_US -l 2>&1
if [ $? -eq 0 ]; then
    echo "  ✓ 基本检查正常"
else
    echo "  ✗ 基本检查失败"
fi

echo "helo" | hunspell -a -d en_US 2>&1 | grep "^&" | head -3
if [ $? -eq 0 ]; then
    echo "  ✓ 建议功能正常"
else
    echo "  ✗ 建议功能失败"
fi

echo ""
echo "=== 诊断完成 ==="

10.8.3 日志记录

import logging
import json
from datetime import datetime

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('spellcheck.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger('spellcheck')

def log_spellcheck_result(file_path: str, errors: list[str], duration_ms: float):
    """记录拼写检查结果"""
    logger.info(json.dumps({
        "event": "spellcheck_complete",
        "file": file_path,
        "error_count": len(errors),
        "duration_ms": round(duration_ms, 2),
        "errors_sample": errors[:5],
        "timestamp": datetime.now().isoformat()
    }))

10.9 持续改进

10.9.1 词典质量指标

def calculate_dict_quality(errors: list[str], total_words: int) -> dict:
    """计算词典质量指标"""
    error_count = len(errors)
    unique_errors = len(set(errors))
    
    return {
        "total_words": total_words,
        "total_errors": error_count,
        "unique_errors": unique_errors,
        "error_rate": f"{error_count / total_words:.2%}" if total_words > 0 else "0%",
        "unique_error_rate": f"{unique_errors / total_words:.2%}" if total_words > 0 else "0%",
        "quality_score": 1 - (unique_errors / total_words) if total_words > 0 else 1
    }

10.9.2 定期审查计划

频率活动负责人
每周检查 CI 拼写报告文档团队
每月清理冗余词典条目技术负责人
每季度审核词典分类产品经理
每年更新语言词典版本运维团队

10.9.3 词典演进策略

阶段 1: 初始建立
  - 从项目文档提取术语
  - 建立基本分类结构
  - 配置 CI/CD 集成

阶段 2: 持续优化
  - 根据误报调整词典
  - 细化分类
  - 添加 affix 规则

阶段 3: 成熟运营
  - 自动化词典更新
  - 质量指标监控
  - 团队协作流程

阶段 4: 智能化
  - 基于历史数据的自动建议
  - 上下文感知的误报过滤
  - 多语言智能切换

10.10 总结与展望

10.10.1 Hunspell 的优势

优势说明
广泛的语言支持100+ 语言词典
高性能基于词缀压缩,检查速度快
灵活的扩展性自定义词典、affix 规则
成熟的生态系统各平台/编辑器集成
开源许可LGPL/MPL/GPL,可自由集成

10.10.2 Hunspell 的局限

局限替代方案
无上下文检查LanguageTool
无语法检查Grammarly, LanguageTool
CJK 支持有限分词工具 + 自定义词典
不规则词需要手动处理补充词典

10.10.3 未来趋势

1. AI 辅助拼写检查
   - 大语言模型结合传统规则引擎
   - 上下文感知的自动纠正

2. 更好的多语言支持
   - 统一的多语言检查框架
   - 自动语言检测与切换

3. 实时协作
   - 团队共享词典
   - 云端同步

4. 领域特定优化
   - 垂直领域词典(医疗、法律、金融)
   - 行业术语自动提取

10.11 本章小结

实践领域关键要点
词典维护版本控制、分类管理、定期清理
性能优化大小声明、并行检查、缓存
误报处理正则排除、白名单、预处理
多语言语言检测、词典合并、切换策略
生产部署微服务、缓存、监控、高可用
团队协作所有权、更新流程、贡献规范
故障排查诊断脚本、日志记录、常见问题

扩展阅读


恭喜你完成了 Hunspell 完全教程! 🎉

本教程从 Hunspell 的历史背景讲起,覆盖了安装配置、基本使用、词典格式、词缀规则、自定义词典开发、编程接口、编辑器集成、形态学分析,直到生产环境的最佳实践。希望这些内容能帮助你在项目中高效地使用 Hunspell。

如有问题,欢迎参考 Hunspell 官方文档 或在项目中提出 Issue。