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,placeholderandaria-label - Skip rules: Leaves
<code>,<pre>,.no-translateandcontenteditablecontent 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
βββββββββββββββ
- The user enters a URL
- The website fetches the page through the CORS proxy
- The HTML is written into a sandboxed iframe
translateDOMfrom@ingglish/domwalks the page's text and translates it- 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
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
In-place format switching: Switching between Ingglish and IPA updates the existing spans directly instead of restoring and re-translating the whole page
Chunked DOM updates: Applies translations 50 elements at a time with
requestAnimationFrame, so the page stays responsivePre-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.