1+
2+ // spell-checker: words sampa alveolo postalveolar downstep conv noto pharyngealized rhotacized velarized linguolabial upstep
3+
4+
5+ caph . libraries . phonetics = ( ( ) => {
6+ const raWiki = [
7+ [ "ɓ" , "b_<" , "b_<" , "voiced bilabial implosive" ] ,
8+ [ "ɖ" , "d`" , "d`" , "voiced retroflex plosive" ] ,
9+ [ "ɗ" , "d_<" , "d_<" , "voiced alveolar implosive" ] ,
10+ [ "ɡ" , "g" , "g" , "voiced velar plosive" ] ,
11+ [ "ɠ" , "g_<" , "g_<" , "voiced velar implosive" ] ,
12+ [ "ɦ" , "h\\" , "h\\" , "voiced glottal fricative" ] ,
13+ [ "ʝ" , "j\\" , "j\\" , "voiced palatal fricative" ] ,
14+ [ "ɭ" , "l`" , "l`" , "retroflex lateral approximant" ] ,
15+ [ "ɺ" , "l\\" , "l\\" , "alveolar lateral flap" ] ,
16+ [ "ɳ" , "n`" , "n`" , "retroflex nasal" ] ,
17+ [ "ɸ" , "p\\" , "p\\" , "voiceless bilabial fricative" ] ,
18+ [ "ɽ" , "r`" , "r`" , "retroflex flap" ] ,
19+ [ "ɹ" , "r\\" , "r\\" , "alveolar approximant" ] ,
20+ [ "ɻ" , "r\\`" , "r\\`" , "retroflex approximant" ] ,
21+ [ "ʂ" , "s`" , "s`" , "voiceless retroflex fricative" ] ,
22+ [ "ɕ" , "s\\" , "s\\" , "voiceless alveolo-palatal fricative" ] ,
23+ [ "ʈ" , "t`" , "t`" , "voiceless retroflex plosive" ] ,
24+ [ "ʋ" , "v\\" , "v\\" , "labiodental approximant" ] ,
25+ [ "ɧ" , "x\\" , "x\\" , "voiceless palatal-velar fricative" ] ,
26+ [ "ʐ" , "z`" , "z`" , "voiced retroflex fricative" ] ,
27+ [ "ʑ" , "z\\" , "z\\" , "voiced alveolo-palatal fricative" ] ,
28+ [ "ʙ" , "B\\" , "B\\" , "bilabial trill" ] ,
29+ [ "ɢ" , "G\\" , "G\\" , "voiced uvular plosive" ] ,
30+ [ "ʛ" , "G\\_<" , "G\\_<" , "voiced uvular implosive" ] ,
31+ [ "ʜ" , "H\\" , "H\\" , "voiceless epiglottal fricative" ] ,
32+ [ "ɨ̞" , "I\\" , "I\\" , "near-close central unrounded vowel" ] ,
33+ [ "ɪ̈" , "I\\" , "I\\" , "near-close central unrounded vowel" ] ,
34+ [ "ɟ" , "J\\" , "J\\" , "voiced palatal plosive" ] ,
35+ [ "ʄ" , "J\\_<" , "J\\_<" , "voiced palatal implosive" ] ,
36+ [ "ɮ" , "K\\" , "K\\" , "voiced alveolar lateral fricative" ] ,
37+ [ "ʟ" , "L\\" , "L\\" , "velar lateral approximant" ] ,
38+ [ "ɰ" , "M\\" , "M\\" , "velar approximant" ] ,
39+ [ "ɴ" , "N\\" , "N\\" , "uvular nasal" ] ,
40+ [ "ʘ" , "O\\" , "O\\" , "bilabial click" ] ,
41+ [ "ʀ" , "R\\" , "R\\" , "uvular trill" ] ,
42+ [ "ʊ̈" , "U\\" , "U\\" , "near-close central rounded vowel" ] ,
43+ [ "ʉ̞" , "U\\" , "U\\" , "near-close central rounded vowel" ] ,
44+ [ "ħ" , "X\\" , "X\\" , "voiceless pharyngeal fricative" ] ,
45+ [ "ˑ" , ":\\" , ":\\" , "half long" ] ,
46+ [ "ɘ" , "@\\" , "@\\" , "close-mid central unrounded vowel" ] ,
47+ [ "ɞ" , "3\\" , "3\\" , "open-mid central rounded vowel" ] ,
48+ [ "ɶ" , "&" , "&\\" , "open front rounded vowel" ] ,
49+ [ "ʕ" , "?\\" , "?\\" , "voiced pharyngeal fricative" ] ,
50+ [ "ʢ" , "<\\" , "<\\" , "voiced epiglottal fricative" ] ,
51+ [ "ʡ" , ">\\" , ">\\" , "epiglottal plosive" ] ,
52+ [ "ǃ" , "!\\" , "!\\" , "postalveolar click" ] ,
53+ [ "ꜜ" , "!" , "!" , "downstep" ] ,
54+ [ "ǁ" , "|\\|\\" , "|\\|\\" , "alveolar lateral click" ] ,
55+ [ "ǀ" , "|\\" , "|\\" , "dental click" ] ,
56+ [ "‖" , "||" , "||" , "major intonation group" ] ,
57+ [ "ǂ" , "=\\" , "=\\" , "palatal click" ] ,
58+ [ "‿" , "-\\" , "-\\" , "linking mark" ] ,
59+ [ "̈" , "_\"" , "_\"" , "centralized" ] ,
60+ [ "̟" , "_+" , "_+" , "advanced" ] ,
61+ [ "̠" , "_-" , "_-" , "retracted" ] ,
62+ [ "̌" , "_/" , "_/" , "rising tone" ] ,
63+ [ "̥" , "_0" , "_0" , "voiceless" ] ,
64+ [ "ʼ" , "_>" , "_>" , "ejective" ] ,
65+ [ "ˤ" , "_?\\" , "_?\\" , "pharyngealized" ] ,
66+ [ "̂" , "_\\" , "_\\" , "falling tone" ] ,
67+ [ "̯" , "_^" , "_^" , "non-syllabic" ] ,
68+ [ "̚" , "_}" , "_}" , "no audible release" ] ,
69+ [ "˞" , "`" , "`" , "rhotacized" ] ,
70+ [ "̃" , "~" , "~" , "nasalized" ] ,
71+ [ "̃" , "~" , "~" , "nasalized" ] ,
72+ [ "̃" , "_~" , "_~" , "nasalized" ] ,
73+ [ "̘" , "_A" , "_A" , "advanced tongue root" ] ,
74+ [ "̺" , "_a" , "_a" , "apical" ] ,
75+ [ " ᷅" , "_B_L" , "_B_L" , "low rising tone" ] ,
76+ [ "̏" , "_B" , "_B" , "extra low tone" ] ,
77+ [ "̜" , "_c" , "_c" , "less rounded" ] ,
78+ [ "̪" , "_d" , "_d" , "dental" ] ,
79+ [ "̴" , "_e" , "_e" , "velarized or pharyngealized; also see 5" ] ,
80+ [ "↘" , "<F>" , "<F>" , "global fall" ] ,
81+ [ "ˠ" , "_G" , "_G" , "velarized" ] ,
82+ [ " ᷄" , "_H_T" , "_H_T" , "high rising tone" ] ,
83+ [ "́" , "_H" , "_H" , "high tone" ] ,
84+ [ "ʰ" , "_h" , "_h" , "aspirated" ] ,
85+ [ "̰" , "_k" , "_k" , "creaky voice" ] ,
86+ [ "̀" , "_L" , "_L" , "low tone" ] ,
87+ [ "ˡ" , "_l" , "_l" , "lateral release" ] ,
88+ [ "̄" , "_M" , "_M" , "mid tone" ] ,
89+ [ "̻" , "_m" , "_m" , "laminal" ] ,
90+ [ "̼" , "_N" , "_N" , "linguolabial" ] ,
91+ [ "ⁿ" , "_n" , "_n" , "nasal release" ] ,
92+ [ "̹" , "_O" , "_O" , "more rounded" ] ,
93+ [ "̞" , "_o" , "_o" , "lowered" ] ,
94+ [ "̙" , "_q" , "_q" , "retracted tongue root" ] ,
95+ [ "↗" , "<R>" , "<R>" , "global rise" ] ,
96+ [ " ᷈" , "_R_F" , "_R_F" , "rising falling tone" ] ,
97+ [ "̌" , "_R" , "_R" , "rising tone" ] ,
98+ [ "̂" , "_F" , "_F" , "falling tone" ] ,
99+ [ "̝" , "_r" , "_r" , "raised" ] ,
100+ [ "̋" , "_T" , "_T" , "extra high tone" ] ,
101+ [ "̤" , "_t" , "_t" , "breathy voice" ] ,
102+ [ "̬" , "_v" , "_v" , "voiced" ] ,
103+ [ "ʷ" , "_w" , "_w" , "labialized" ] ,
104+ [ "̆" , "_X" , "_X" , "extra-short" ] ,
105+ [ "̽" , "_x" , "_x" , "mid-centralized" ] ,
106+ [ "ꜛ" , "^" , "^" , "upstep" ] ,
107+ [ "ː" , ":" , ":" , "long" ] ,
108+ [ "|" , "|" , "|" , "minor (foot) group" ] ,
109+ [ "̩" , "=" , "=" , "syllabic" ] ,
110+ [ "̩" , "=" , "=" , "syllabic" ] ,
111+ [ "ˈ" , "\"" , "'" , "primary stress" ] ,
112+ [ "ˌ" , "%" , "," , "secondary stress" ] ,
113+ [ "ə" , "@" , "@" , "schwa" ] ,
114+ [ "ʲ" , "_j" , "_j" , "palatalized" ] ,
115+ [ "æ" , "{" , "&" , "near-open front unrounded vowel" ] ,
116+ [ "ʉ" , "}" , "u\\" , "close central rounded vowel" ] ,
117+ [ "ʔ" , "?" , "?" , "glottal stop" ] ,
118+ [ "ɨ" , "1" , "1" , "close central unrounded vowel" ] ,
119+ [ "ø" , "2" , "2" , "close-mid front rounded vowel" ] ,
120+ [ "ɜ" , "3" , "3" , "open-mid central unrounded vowel" ] ,
121+ [ "ɾ" , "4" , "4" , "alveolar flap" ] ,
122+ [ "ɫ" , "5" , "5" , "velarized alveolar lateral approximant; also see _e" ] ,
123+ [ "ɐ" , "6" , "6" , "near-open central vowel" ] ,
124+ [ "ɤ" , "7" , "7" , "close-mid back unrounded vowel" ] ,
125+ [ "ɵ" , "8" , "8" , "close-mid central rounded vowel" ] ,
126+ [ "œ" , "9" , "9" , "open-mid front rounded vowel" ] ,
127+ [ "ɑ" , "A" , "A" , "open back unrounded vowel" ] ,
128+ [ "β" , "B" , "B" , "voiced bilabial fricative" ] ,
129+ [ "ç" , "C" , "C" , "voiceless palatal fricative" ] ,
130+ [ "ð" , "D" , "D" , "voiced dental fricative" ] ,
131+ [ "ɛ" , "E" , "E" , "open-mid front unrounded vowel" ] ,
132+ [ "ɱ" , "F" , "F" , "labiodental nasal" ] ,
133+ [ "ɣ" , "G" , "G" , "voiced velar fricative" ] ,
134+ [ "ɥ" , "H" , "H" , "labial-palatal approximant" ] ,
135+ [ "ɪ" , "I" , "I" , "near-close front unrounded vowel" ] ,
136+ [ "ɲ" , "J" , "J" , "palatal nasal" ] ,
137+ [ "ɬ" , "K" , "K" , "voiceless alveolar lateral fricative" ] ,
138+ [ "ʎ" , "L" , "L" , "palatal lateral approximant" ] ,
139+ [ "ɯ" , "M" , "M" , "close back unrounded vowel" ] ,
140+ [ "ŋ" , "N" , "N" , "velar nasal" ] ,
141+ [ "ɔ" , "O" , "O" , "open-mid back rounded vowel" ] ,
142+ [ "ɒ" , "Q" , "Q" , "open back rounded vowel" ] ,
143+ [ "ʁ" , "R" , "R" , "voiced uvular fricative" ] ,
144+ [ "ʃ" , "S" , "S" , "voiceless postalveolar fricative" ] ,
145+ [ "θ" , "T" , "T" , "voiceless dental fricative" ] ,
146+ [ "ʊ" , "U" , "U" , "near-close back rounded vowel" ] ,
147+ [ "ʌ" , "V" , "V" , "open-mid back unrounded vowel" ] ,
148+ [ "ʍ" , "W" , "W" , "voiceless labial-velar fricative" ] ,
149+ [ "χ" , "X" , "X" , "voiceless uvular fricative" ] ,
150+ [ "ʏ" , "Y" , "Y" , "near-close front rounded vowel" ] ,
151+ [ "ʒ" , "Z" , "Z" , "voiced postalveolar fricative" ] ,
152+ [ "ʋ" , "P" , "P" , "labiodental approximant" ] ,
153+ [ "͡" , "_" , ")" , "affricate tie bar" ] ,
154+ ] ;
155+ const wiki = raWiki . map ( ( [ ipa , sampa , csx , name ] ) => ( { ipa, sampa, csx, name} ) ) ;
156+ const bySampa = Object . fromEntries ( wiki . map ( e => [ e . sampa , e ] ) ) ;
157+ const sampaMaxLength = wiki . map ( ( { sampa} ) => sampa . length ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
158+ const sampa2ipa = ( sampa ) => {
159+ const out = [ ] ;
160+ let i = 0 ;
161+ while ( i < sampa . length ) {
162+ let n ;
163+ for ( n = sampaMaxLength ; n >= 1 ; n -- ) if ( bySampa [ sampa . slice ( i , i + n ) ] ) break ;
164+ let ipa = bySampa [ sampa . slice ( i , i + n ) ] ?. ipa ;
165+ if ( ! ipa ) ipa = sampa [ i ] , n = 1 ;
166+ out . push ( ipa ) ;
167+ i += n ;
168+ }
169+ return out . join ( '' ) ;
170+ } ;
171+
172+ const easyScheme = `
173+ |=,
174+ p, b, t, d, k, g, t!=tʰ, d!=dʰ, k?=q, g?=ɢ, ?=ʔ,
175+ m, n, ~n=ñ=ɲ, -ng-=ŋ,
176+ l, L=ʎ, l?=l̴,
177+ r=ɾ, rh=ɹ, R=r, R?=ʀ,
178+ s, z, sh=ʃ, zh=ʒ, f, v, th=θ, dh=ð,
179+ s!=s̺, z!=z̺, xh=ç, Jh=jh=ʝ,
180+ h, x, gh=ɣ, X=χ, r?=ʁ, h?=ħ, h,
181+ ts=t͡s, dz=d͡z, ch=tsh=t͡ʃ, J=dzh=d͡ʒ, dr=d͡ɹ,
182+ j=i*=j, w=u*=w, y*=ɥ, H?=ʕ,
183+ a, e, i, o, u, @=ə,
184+ y=-iu-=ü=y, @@=-oe-=ö=ø,
185+ E=-ea-=è=ɛ, I=ɪ, O=oh=ɔ, U=uh=ʊ,
186+ A=ʌ, a?=ɑ, o?=ɒ, -ae-=æ, 3=ɜ, Y=ʏ,
187+ M=ı=ɯ, -oea-=œ,
188+ -an-=-a?n-=ɑ̃, -ain-=ɛ̃, -on-=ɔ̃, -un-=œ̃,
189+ @r=ɚ, 3r=ɝ,
190+ ` . replace ( / \s + / sg, ' ' ) ;
191+
192+ const easy2ipaDict = { } ;
193+ const ipa2easyDict = { } ;
194+ for ( let s of easyScheme . split ( ',' ) ) if ( s . trim ( ) . length ) {
195+ let values = s . trim ( ) . split ( '=' ) ;
196+ const last = values [ values . length - 1 ] ;
197+ ipa2easyDict [ last ] = values [ 0 ] ;
198+ for ( let x of values ) easy2ipaDict [ x ] = last ;
199+ }
200+ const caphMaxLength = Object . keys ( easy2ipaDict ) . map ( ( k ) => k . length ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
201+ const ipaMaxLength = Object . keys ( ipa2easyDict ) . map ( ( k ) => k . length ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
202+ const easy2ipa = ( easy ) => {
203+ const out = [ ] ;
204+ let i = 0 ;
205+ while ( i < easy . length ) {
206+ let n ;
207+ for ( n = caphMaxLength ; n >= 1 ; n -- ) if ( easy2ipaDict [ easy . slice ( i , i + n ) ] ) break ;
208+ let ipa = easy2ipaDict [ easy . slice ( i , i + n ) ] ;
209+ if ( ! ipa ) ipa = easy [ i ] , n = 1 ;
210+ out . push ( ipa ) ;
211+ i += n ;
212+ }
213+ return out . join ( "" ) ;
214+ }
215+ const ipa2easy = ( ipa ) => {
216+ const out = [ ] ;
217+ let i = 0 ;
218+ while ( i < ipa . length ) {
219+ let n ;
220+ for ( n = ipaMaxLength ; n >= 1 ; n -- ) if ( ipa2easyDict [ ipa . slice ( i , i + n ) ] ) break ;
221+ let easy = ipa2easyDict [ ipa . slice ( i , i + n ) ] ;
222+ if ( ! easy ) easy = ipa [ i ] , n = 1 ;
223+ else if ( ! n ) n = 1 ;
224+ out . push ( easy ) ;
225+ i += n ;
226+ }
227+ return out . join ( "" ) ;
228+ }
229+ return { wiki, sampa2ipa, easy2ipa, ipa2easy, easy2ipaDict, ipa2easyDict} ;
230+ } ) ( ) ;
231+
232+
233+ caph . injectStyle ( `
234+ .font-phonetics{ font-family: "Noto Serif", serif; }
235+ ` ) ;
236+ caph . defineFileComponent ( ( ( ) => {
237+ const { sampa2ipa, easy2ipa} = caph . libraries . phonetics ;
238+ return ( { sampa, easy, children} ) => {
239+ if ( children ) console . warn ( 'unexpected children in phonetics component' ) ;
240+ const ipa = easy ?easy2ipa ( easy ) : sampa2ipa ( sampa ) ;
241+ return caph . parse `<code class="font-phonetics">${ ipa } </code>` ;
242+ }
243+ } ) ( ) ) ;
0 commit comments