Architecture Overview

How the Ingglish project is organized: its packages, how they depend on each other, and how text flows through them.

Two terms come up throughout. Pronunciations come from the CMU Pronouncing Dictionary (Carnegie Mellon University's pronunciation list), which writes sounds in ARPAbet, a plain-ASCII phoneme notation. G2P (grapheme-to-phoneme) means guessing a word's pronunciation from its spelling.

Project Structure

ingglish/
β”œβ”€β”€ packages/
β”‚   β”œβ”€β”€ normalize/      # Text cleanup, case handling, tokenization, word patterns
β”‚   β”œβ”€β”€ phonemes/       # Phoneme data + conversion
β”‚   β”œβ”€β”€ dictionary/     # CMU dict, lookup, frequency
β”‚   β”œβ”€β”€ g2p/            # Rule-based grapheme-to-phoneme
β”‚   β”œβ”€β”€ ipa/            # IPA ↔ ARPAbet conversion
β”‚   β”œβ”€β”€ shavian/        # Shavian alphabet conversion
β”‚   β”œβ”€β”€ deseret/        # Deseret alphabet conversion
β”‚   β”œβ”€β”€ fallback/       # Unknown word strategies
β”‚   β”œβ”€β”€ core/           # Translation API (translate + reverse)
β”‚   β”œβ”€β”€ dom/            # DOM translation utilities
β”‚   β”œβ”€β”€ website/        # React web application
β”‚   β”œβ”€β”€ extension/      # Chrome extension
β”‚   └── cors-proxy/     # Cloudflare Worker proxy
β”œβ”€β”€ docs/               # Documentation
└── .github/            # CI/CD workflows

Package Dependencies

@ingglish/normalize (0 deps)
@ingglish/phonemes  (0 deps) ◄┐
@ingglish/dictionary (0 deps) ◄─
@ingglish/g2p ──► phonemes    ◄┼── @ingglish/fallback
                               β”‚
@ingglish/ipa ──► phonemes     β”‚
@ingglish/shavian ──► phonemes + dictionary
@ingglish/deseret ──► phonemes + dictionary
                               β”‚
ingglish ◄── all above packages
       β–²
@ingglish/dom ──► @ingglish/normalize (peer: core)
       β–²
@ingglish/website ──► @ingglish/dom + ingglish
@ingglish/extension ──► ingglish

Library Packages

@ingglish/normalize - Text cleanup, case handling, tokenization

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ case.ts             # Case pattern detection/application, splitCamelCase
β”œβ”€β”€ text.ts             # normalizeApostrophes, stripDiacritics, URL/email preservation
└── tokenize.ts         # WORD_SPLIT_REGEX, WORD_TEST_REGEX, tokenizeText, tokenizeIPA, etc.

@ingglish/phonemes - Phoneme data + conversion

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ arpabet.ts          # ARPAbet phoneme definitions
β”œβ”€β”€ phonotactics.ts     # English sound rules for stress
β”œβ”€β”€ types.ts            # OutputFormat type
β”œβ”€β”€ to-ingglish.ts      # ARPAbet β†’ Ingglish
β”œβ”€β”€ from-ingglish.ts    # Ingglish β†’ ARPAbet
β”œβ”€β”€ ingglish-maps.ts    # Phoneme mapping tables
β”œβ”€β”€ custom-format.ts    # Custom format registration
└── format-registry.ts  # Format registry for extensible output

@ingglish/dictionary - CMU dict, lookup, frequency

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ loader.ts           # Load and cache CMU dictionary
β”œβ”€β”€ lookup.ts           # Word pronunciation lookup
β”œβ”€β”€ reverse.ts          # Build reverse index (phoneme β†’ words)
β”œβ”€β”€ frequency.ts        # Word frequency ranking
β”œβ”€β”€ custom-words.ts     # Custom pronunciations (tech terms)
└── data/               # Generated dictionary and frequency data

@ingglish/g2p - Rule-based grapheme-to-phoneme

src/
β”œβ”€β”€ index.ts            # Public API
β”œβ”€β”€ g2p-rules.ts        # Core G2P conversion rules
β”œβ”€β”€ stress.ts           # Stress assignment
└── stress.test.ts      # Stress prediction tests

@ingglish/ipa - IPA ↔ ARPAbet conversion

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ to-ipa.ts           # ARPAbet β†’ IPA with stress
└── from-ipa.ts         # IPA β†’ ARPAbet

@ingglish/shavian - Shavian alphabet conversion

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ to-shavian.ts       # ARPAbet β†’ Shavian
β”œβ”€β”€ from-shavian.ts     # Shavian β†’ ARPAbet
β”œβ”€β”€ shavian-maps.ts     # Shavian mapping tables
└── tokenize.ts         # Shavian tokenization

@ingglish/deseret - Deseret alphabet conversion

src/
β”œβ”€β”€ index.ts            # Barrel exports
β”œβ”€β”€ to-deseret.ts       # ARPAbet β†’ Deseret
β”œβ”€β”€ from-deseret.ts     # Deseret β†’ ARPAbet
β”œβ”€β”€ deseret-maps.ts     # Deseret mapping tables
└── tokenize.ts         # Deseret tokenization

@ingglish/fallback - Unknown word strategies

src/
β”œβ”€β”€ index.ts            # Fallback orchestration
β”œβ”€β”€ acronyms.ts         # Acronym/initialism handling
β”œβ”€β”€ compounds.ts        # Compound word splitting
β”œβ”€β”€ stemming.ts         # Base word + suffix matching
└── british.ts          # British spelling variants

ingglish - Translation API

The core package is a thin layer that ties the packages above together and exports the public translation API.

src/
β”œβ”€β”€ index.ts            # Public API: translate, reverseTranslate, Sync variants
β”œβ”€β”€ dict-loader.ts      # Per-language dictionary registration/loading
β”œβ”€β”€ register-english.ts # Registers the English dictionary loader
└── translate/          # Translation logic
    β”œβ”€β”€ index.ts        # Re-exports the translate/reverse API
    β”œβ”€β”€ forward.ts      # English β†’ Ingglish/IPA (incl. contractions)
    β”œβ”€β”€ reverse.ts      # Ingglish/IPA β†’ English (incl. contractions)
    β”œβ”€β”€ pipeline.ts     # Shared tokenize β†’ map β†’ render stages
    └── preserved.ts    # URL/email preservation during translation

Contractions ("don't", "I'm") are handled directly in forward.ts and reverse.ts. There is no separate module for contractions or language detection.

Translation Flow

English Text
     β”‚
     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  translateText  β”‚ (format: 'ingglish' | 'ipa')
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ tokenize
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ translateWord   │────>β”‚ lookupPronunciation  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                       β”‚
         β”‚ found?                β”‚ CMU Dictionary
         β”‚                       β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”                  β”‚
    β”‚         β”‚                  β–Ό
    β–Ό         β–Ό           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 phonemes   unknown       β”‚   phonemes   β”‚
    β”‚         β”‚           β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚         β”‚                  β”‚
    β”‚    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”             β”‚
    β”‚    β”‚ stemmingβ”‚             β”‚
    β”‚    β”‚  rules  β”‚             β”‚
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜             β”‚
    β”‚         β”‚                  β”‚
    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                  β”‚
         β”‚                       β”‚
         β–Ό                       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
β”‚   Output Format?   β”‚<β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
    β”‚         β”‚
    β–Ό         β–Ό
Ingglish    IPA
    β”‚         β”‚
    β–Ό         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ phonemes  β”‚ β”‚phonemesTo β”‚
β”‚ToIngglish β”‚ β”‚   IPA     β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
      β”‚             β”‚
      β–Ό             β–Ό
 "haloh"       "/hΙ™Λˆloʊ/"

Reverse Translation Flow

Reverse translation accepts either Ingglish or IPA input:

Ingglish Text          IPA Text
     β”‚                     β”‚
     β–Ό                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚reverseTranslate β”‚  β”‚ reverseTranslate   β”‚
β”‚     Text        β”‚  β”‚     IPAText        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                     β”‚
         β–Ό                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ingglishToPhonemes  β”‚  β”‚   ipaToArpabet     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚                       β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  lookupByPhonemes   β”‚  Reverse dictionary lookup
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚   sortByFrequency   β”‚  Rank homophones
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  β–Ό
            English Text (most common match)

Key Data Structures

CMU Dictionary

// Word β†’ Phoneme array (pre-split at build time)
{
  "hello": ["HH", "AH0", "L", "OW1"],
  "world": ["W", "ER1", "L", "D"],
  ...
}

Reverse Dictionary (built at runtime)

// Phoneme key β†’ English words (sorted by frequency)
Map<string, string[]>
{
  "T UW": ["to", "too", "two"],
  "DH EH R": ["there", "their", "they're"],
  ...
}

Phoneme Map

// ARPAbet β†’ Ingglish spelling
{
  "HH": "h",
  "AH": "uh",   // stressed; unstressed AH0 β†’ "a"
  "L": "l",
  "OW": "oh",
  ...
}

DOM Library (@ingglish/dom)

Browser-only tools for translating the text of a live web page (its DOM).

Module Structure

src/
β”œβ”€β”€ index.ts                    # Public API exports
β”œβ”€β”€ types.ts                    # DOMTranslatorOptions interface
β”œβ”€β”€ translate/                  # DOM translation logic
β”‚   β”œβ”€β”€ index.ts                # translateDOM orchestration
β”‚   β”œβ”€β”€ translator.ts           # Core DOM translation algorithm
β”‚   β”œβ”€β”€ apply-map.ts            # Apply pre-computed translations
β”‚   β”œβ”€β”€ chunked.ts              # requestAnimationFrame chunked processing
β”‚   β”œβ”€β”€ restore.ts              # Restore original text
β”‚   └── tooltip-fragment.ts     # Hover tooltip HTML generation
└── traversal/                  # DOM traversal
    β”œβ”€β”€ index.ts                # Traversal exports
    β”œβ”€β”€ browser.ts              # Browser detection
    β”œβ”€β”€ extract.ts              # Word extraction from text nodes
    β”œβ”€β”€ skip-rules.ts           # Skip logic for tags/classes
    β”œβ”€β”€ text-nodes.ts           # TreeWalker and text node utilities
    └── tooltip.ts              # Tooltip styling utilities

Public API: translateDOM / translateDOMSync, restoreDOM, and applyTranslationsMap, plus traversal helpers (collectTextNodes, extractWordsFromNodes, injectTooltipStyles, injectTooltipBehavior).

Key Features

  • Chunked translation: Spreads work across frames with requestAnimationFrame, so large pages stay smooth
  • Tooltips: Wraps each translated word in a span that shows the original text on hover
  • Attribute translation: Also translates title, alt, placeholder and aria-label
  • Skip rules: Leaves <code>, <pre>, .no-translate and contenteditable content untouched
  • Pre-computed translations: applyTranslationsMap() applies translations produced elsewhere

Translating content that appears after the page loads (via a MutationObserver) happens in the Chrome extension's content script, not in this package.

Website (@ingglish/website)

A React single-page application. Each page is its own route, and every page except the tutorial is lazy-loaded:

Components

src/
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ Tutorial.tsx         # Home page: interactive introduction (sections in tutorial/)
β”‚   β”œβ”€β”€ TextTranslator.tsx   # Bidirectional text translation
β”‚   β”œβ”€β”€ UrlTranslator.tsx    # Web page translation
β”‚   β”œβ”€β”€ SpellingGuide.tsx    # Phoneme mapping reference
β”‚   β”œβ”€β”€ WordExplorer.tsx     # Per-word breakdown of spelling and sound
β”‚   β”œβ”€β”€ Experiment.tsx       # Custom mappings, scored by lib/mapping-metrics.ts
β”‚   β”œβ”€β”€ Games.tsx            # Reading and spelling games (games/)
β”‚   β”œβ”€β”€ Extension.tsx        # Chrome extension info page
β”‚   └── Docs.tsx             # Documentation viewer
β”œβ”€β”€ contexts/
β”‚   └── FormatContext.tsx    # Output format state (Ingglish/IPA)
β”œβ”€β”€ hooks/
β”‚   └── useUrlTranslator.ts  # URL fetching & translation logic
β”œβ”€β”€ routes-config.tsx        # Route table
└── AppLayout.tsx            # Tab navigation shell

URL Translation Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Browser   │────>β”‚ CORS Proxy  │────>β”‚ Target Site  β”‚
β”‚   (iframe)  β”‚     β”‚  (Worker)   β”‚     β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ translateDOMβ”‚  In-place DOM modification
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. The user enters a URL
  2. The website fetches the page through the CORS proxy
  3. The HTML is written into a sandboxed iframe
  4. translateDOM from @ingglish/dom walks the page's text and translates it
  5. Clicked links are intercepted so navigation stays inside the iframe

Chrome Extension (@ingglish/extension)

Components

src/
β”œβ”€β”€ manifest.json     # Extension configuration
β”œβ”€β”€ content-script.ts   # Content script (DOM walking + message passing)
β”œβ”€β”€ background.ts     # Service worker (holds dictionary ~5MB)
└── popup.ts          # Popup UI

Architecture

The extension keeps the script injected into each page small by passing messages to a background worker that does the heavy lifting:

  • Background service worker: Loads the full CMU dictionary (~5MB) once
  • Content script: Small (~11KB); walks the page and sends its words to the background worker for translation
  • Translation cache: The background worker keeps up to 50K translations in memory, so repeated words are fast

Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Popup UI   │────>β”‚   Message    β”‚
β”‚ (popup.ts)   β”‚     β”‚   Passing    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
                            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Content Script (content-script.ts)    β”‚
β”‚  β€’ Walks DOM, collects text nodes                β”‚
β”‚  β€’ Sends batches of words to background          β”‚
β”‚  β€’ Applies translations in chunks (RAF)          β”‚
β”‚  β€’ Debounced MutationObserver (100ms)            β”‚
β”‚  β€’ In-place span updates for format switching    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚ chrome.runtime.sendMessage
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Background (background.ts)          β”‚
β”‚  β€’ Loads CMU dictionary on startup               β”‚
β”‚  β€’ Caches translations (50K entries, FIFO)       β”‚
β”‚  β€’ Returns translated words                      β”‚
β”‚  β€’ Manages tab-specific translation state        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Performance Optimizations

  1. Debounced MutationObserver: Waits until page changes have been quiet for 100ms before translating them, so sites that update constantly (e.g., infinite scroll) don't freeze

  2. In-place format switching: Switching between Ingglish and IPA updates the existing spans directly instead of restoring and re-translating the whole page

  3. Chunked DOM updates: Applies translations 50 elements at a time with requestAnimationFrame, so the page stays responsive

  4. Pre-collected text nodes: Passes the text nodes it already collected to applyTranslationsMap(), so the page is not walked twice

CORS Proxy (@ingglish/cors-proxy)

A Cloudflare Worker that fetches pages on the website's behalf. Browsers block a site from reading most other sites' pages directly (CORS restrictions); the proxy adds the headers that allow it.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Website   │────>β”‚ Cloudflare Worker │────>β”‚ Target URL  β”‚
β”‚            β”‚     β”‚                   β”‚     β”‚             β”‚
β”‚            β”‚<────│ + CORS headers    β”‚<────│             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Security features:

  • Only accepts requests from allowlisted origins
  • Blocks requests to private IP ranges (127., 10., 172.16-31., 192.168., ::1), so it can't be used to reach internal servers (server-side request forgery, SSRF)
  • Only fetches HTTP/HTTPS URLs
  • Only returns HTML (checks Content-Type)
  • Sets cache headers (minimum 5 minutes)

Data Flow Summary

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Build Time                                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  CMU Dictionary (126K words) ──> bundled with @ingglish/dictionaryβ”‚
β”‚  SUBTLEX Frequencies (74K) ──> bundled with @ingglish/dictionary  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Runtime                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  loadDictionary() ──> parse & cache dictionary              β”‚
β”‚  translateText() ──> O(n) word lookup + phoneme conversion  β”‚
β”‚  reverseTranslate() ──> O(1) phoneme key lookup + frequency β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Every path runs in linear time or better; none is quadratic or exponential. Dictionary data is loaded only when needed, via dynamic imports.

See Performance for complexity tables, profiling scripts, and optimization guidelines.