finish phonetics conversion

This commit is contained in:
adueck 2023-07-27 22:53:03 +04:00
parent 5df7a85003
commit d076fff05c
68 changed files with 20980 additions and 7701 deletions

View File

@ -17,7 +17,7 @@
"@formkit/auto-animate": "^1.0.0-beta.6",
"@fortawesome/fontawesome-free": "5.15.4",
"@lingdocs/lingdocs-main": "^0.3.3",
"@lingdocs/ps-react": "^5.7.13",
"@lingdocs/ps-react": "^6.0.2",
"@mdx-js/rollup": "^2.2.1",
"@stefanprobst/rehype-extract-toc": "^2.2.0",
"@types/mdx": "^2.0.3",

View File

@ -11,25 +11,26 @@ const allTs = [...nounAdjTs, ...verbs, ...adverbs];
console.log("getting words from dictionary...");
fetch("https://account.lingdocs.com/dictionary/entries", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ids: allTs }),
}).then(res => res.json()).then(data => {
const content = `
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ids: allTs }),
})
.then((res) => res.json())
.then((data) => {
const content = `
// @ts-ignore
const words: Word[] = ${JSON.stringify(data.results)};
export default words;`;
fs.writeFileSync(wordsFile, content);
const missingEc = data.results.filter(x => "entry" in x && !x.entry.ec);
if (missingEc.length) {
console.log("verbs missing ec");
console.log(missingEc);
}
if (data.notFound.length) {
console.log("entries not found:");
console.log(data.notFound);
}
});
fs.writeFileSync(wordsFile, content);
const missingEc = data.results.filter((x) => "entry" in x && !x.entry.ec);
if (missingEc.length) {
console.log("verbs missing ec");
console.log(missingEc);
}
if (data.notFound.length) {
console.log("entries not found:");
console.log(data.notFound);
}
});

View File

@ -1,295 +1,409 @@
import {
Types as T,
RootsAndStems,
conjugateVerb,
VerbTable,
renderVP,
compileVP,
ButtonSelect,
getEnglishVerb,
InlinePs,
removeFVarients,
isPastTense,
getPassiveRootsAndStems,
getAbilityRootsAndStems,
Types as T,
RootsAndStems,
conjugateVerb,
VerbTable,
renderVP,
compileVP,
ButtonSelect,
getEnglishVerb,
InlinePs,
removeFVarients,
isPastTense,
getPassiveRootsAndStems,
getAbilityRootsAndStems,
} from "@lingdocs/ps-react";
import { isImperativeTense, isPerfectTense } from "@lingdocs/ps-react/dist/lib/src/type-predicates";
import {
isImperativeTense,
isPerfectTense,
} from "@lingdocs/ps-react/dist/lib/src/type-predicates";
import { useState } from "react";
import Carousel from "./Carousel";
import { basicVerbs, intransitivePastVerbs } from "../content/verbs/basic-present-verbs";
import {
basicVerbs,
intransitivePastVerbs,
} from "../content/verbs/basic-present-verbs";
import { getLength } from "@lingdocs/ps-react/dist/lib/src/p-text-helpers";
import { isThirdPerson } from "@lingdocs/ps-react";
function BasicVerbShowCase({ opts, tense, passive, ability }: {
opts: T.TextOptions,
tense: T.VerbTense | T.ImperativeTense,
passive?: boolean,
ability?: boolean,
function BasicVerbShowCase({
opts,
tense,
passive,
ability,
}: {
opts: T.TextOptions;
tense: T.VerbTense | T.ImperativeTense;
passive?: boolean;
ability?: boolean;
}) {
const items = isPastTense(tense)
? intransitivePastVerbs
: (passive ? basicVerbs.filter(v => v.entry.p !== "کول") : basicVerbs);
return <Carousel stickyTitle items={items} render={(item) => {
const items = isPastTense(tense)
? intransitivePastVerbs
: passive
? basicVerbs.filter((v) => v.entry.p !== "کول")
: basicVerbs;
return (
<Carousel
stickyTitle
items={items}
render={(item) => {
return {
title: <InlinePs opts={opts}>{{
title: (
<InlinePs opts={opts}>
{{
...removeFVarients(item.entry),
e: undefined,
}}</InlinePs>,
body: <BasicVerbChart
passive={passive}
ability={ability}
verb={item}
opts={opts}
tense={tense}
/>,
}}
</InlinePs>
),
body: (
<BasicVerbChart
passive={passive}
ability={ability}
verb={item}
opts={opts}
tense={tense}
/>
),
};
}}/>
}}
/>
);
}
export default BasicVerbShowCase;
function BasicVerbChart({ verb, opts, tense, passive, ability }: {
verb: T.VerbEntry,
opts: T.TextOptions,
tense: T.VerbTense | T.ImperativeTense | T.PerfectTense,
passive?: boolean,
ability?: boolean,
function BasicVerbChart({
verb,
opts,
tense,
passive,
ability,
}: {
verb: T.VerbEntry;
opts: T.TextOptions;
tense: T.VerbTense | T.ImperativeTense | T.PerfectTense;
passive?: boolean;
ability?: boolean;
}) {
const [voice, setVoice] = useState<"active" | "passive">("active");
const [category, setCategory] = useState<"basic" | "ability">("basic");
const [negative, setNegative] = useState<boolean>(false);
const [length, setLength] = useState<"short" | "long">("short");
const c = conjugateVerb(verb.entry, verb.complement);
const conjugations = "stative" in c
? c.stative
: "grammaticallyTransitive" in c
? c.grammaticallyTransitive
: c;
const phrasesForTable = makeExamplePhrases(verb, tense, negative, length, voice, category);
return <div>
<div>
{getEnglishVerb(verb.entry)}
const [voice, setVoice] = useState<"active" | "passive">("active");
const [category, setCategory] = useState<"basic" | "ability">("basic");
const [negative, setNegative] = useState<boolean>(false);
const [length, setLength] = useState<"short" | "long">("short");
const c = conjugateVerb(verb.entry, verb.complement);
const conjugations =
"stative" in c
? c.stative
: "grammaticallyTransitive" in c
? c.grammaticallyTransitive
: c;
const phrasesForTable = makeExamplePhrases(
verb,
tense,
negative,
length,
voice,
category
);
return (
<div>
<div>{getEnglishVerb(verb.entry)}</div>
{passive && (
<div className="my-2">
<ButtonSelect
options={[
{
label: "Active",
value: "active",
},
{
label: "Passive",
value: "passive",
},
]}
value={voice}
handleChange={setVoice}
/>
</div>
{passive && <div className="my-2">
)}
{ability && (
<div className="my-2">
<ButtonSelect
options={[
{
label: "Basic",
value: "basic",
},
{
label: "Ability",
value: "ability",
},
]}
value={category}
handleChange={setCategory}
/>
</div>
)}
<RootsAndStems
textOptions={opts}
info={
category === "ability"
? getAbilityRootsAndStems(conjugations.info)
: voice === "passive"
? getPassiveRootsAndStems(conjugations.info) ||
/* type safety */ conjugations.info
: conjugations.info
}
hidePastParticiple={isPerfectTense(tense) ? false : true}
highlighted={[tenseToStem(tense)]}
/>
<div className="my-3 d-flex flex-row justify-content-center">
{((isPastTense(tense) && !isPerfectTense(tense)) ||
category === "ability") && (
<div className="mx-2">
<ButtonSelect
options={[{
label: "Active",
value: "active",
}, {
label: "Passive",
value: "passive",
}]}
value={voice}
handleChange={setVoice}
handleChange={setLength}
value={length}
small
options={[
{ value: "long", label: "long" },
{ value: "short", label: "short" },
]}
/>
</div>}
{ability && <div className="my-2">
<ButtonSelect
options={[{
label: "Basic",
value: "basic",
}, {
label: "Ability",
value: "ability",
}]}
value={category}
handleChange={setCategory}
/>
</div>}
<RootsAndStems
textOptions={opts}
info={category === "ability"
? (getAbilityRootsAndStems(conjugations.info))
: voice === "passive"
? (getPassiveRootsAndStems(conjugations.info) || /* type safety */ conjugations.info)
: conjugations.info
}
hidePastParticiple={isPerfectTense(tense) ? false : true}
highlighted={[tenseToStem(tense)]}
</div>
)}
<div className="mx-2">
<ButtonSelect
handleChange={(value) => setNegative(value === "true")}
value={String(negative)}
small
options={[
{ value: "true", label: "Neg." },
{ value: "false", label: "Pos." },
]}
/>
</div>
</div>
<div className="text-left">
<VerbTable
textOptions={opts}
block={phrasesForTable.ps}
english={phrasesForTable.e}
/>
<div className="my-3 d-flex flex-row justify-content-center">
{((isPastTense(tense) && !isPerfectTense(tense)) || category === "ability") && <div className="mx-2">
<ButtonSelect
handleChange={setLength}
value={length}
small
options={[
{ value: "long", label: "long" },
{ value: "short", label: "short" },
]}
/>
</div>}
<div className="mx-2">
<ButtonSelect
handleChange={(value) => setNegative(value === "true")}
value={String(negative)}
small
options={[
{ value: "true", label: "Neg." },
{ value: "false", label: "Pos." },
]}
/>
</div>
</div>
<div className="text-left">
<VerbTable
textOptions={opts}
block={phrasesForTable.ps}
english={phrasesForTable.e}
/>
</div>
</div>
</div>
);
}
function makeExamplePhrases(
verb: T.VerbEntry,
tense: T.VerbTense | T.ImperativeTense | T.PerfectTense,
negative: boolean,
length: "short" | "long",
voice: "active" | "passive",
category: "basic" | "ability",
): { ps: T.VerbBlock | T.ImperativeBlock, e: T.EnglishBlock } {
function tenseToModal(t: T.VerbTense | T.ImperativeTense | T.PerfectTense): T.ModalTense {
if (isImperativeTense(t)) {
throw new Error("can't have imperative tense with modal");
}
if (isPerfectTense(t)) {
throw new Error("cant' have perfect tense with modal");
}
return `${t}Modal`;
verb: T.VerbEntry,
tense: T.VerbTense | T.ImperativeTense | T.PerfectTense,
negative: boolean,
length: "short" | "long",
voice: "active" | "passive",
category: "basic" | "ability"
): { ps: T.VerbBlock | T.ImperativeBlock; e: T.EnglishBlock } {
function tenseToModal(
t: T.VerbTense | T.ImperativeTense | T.PerfectTense
): T.AbilityTense {
if (isImperativeTense(t)) {
throw new Error("can't have imperative tense with modal");
}
function makeSelection(person: T.Person): T.VPSelectionComplete{
return {
"blocks": [
{"key":Math.random(),"block":{"type":"subjectSelection","selection":{"type":"NP","selection":{"type":"pronoun","person": person,"distance":"far"}}}},
{
key: Math.random(),
// @ts-ignore
block: (verb.entry.c?.includes("intrans.") || voice === "passive")
? {"type":"objectSelection","selection":"none"}
: {"type":"objectSelection", "selection":{"type":"NP","selection":{"type":"noun","entry":{"ts":1527812817,"i":10011,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},"gender":"masc","genderCanChange":false,"number":"singular","numberCanChange":true,"adjectives":[]}}},
},
],
"verb":{
"type":"verb",
verb,
tense: category === "basic" ? tense : tenseToModal(tense),
"transitivity":"intransitive",
"isCompound":false,
voice,
negative,
"canChangeTransitivity":false,
"canChangeVoice":false,
"canChangeStatDyn":false,
if (isPerfectTense(t)) {
throw new Error("cant' have perfect tense with modal");
}
return `${t}Modal`;
}
function makeSelection(person: T.Person): T.VPSelectionComplete {
return {
blocks: [
{
key: Math.random(),
block: {
type: "subjectSelection",
selection: {
type: "NP",
selection: { type: "pronoun", person: person, distance: "far" },
},
"form":{"removeKing":false,"shrinkServant":false},
};
}
function makePhrase(person: T.Person): { ps: T.ArrayOneOrMore<T.PsString>, e: string } {
const selection = makeSelection(person);
const rendered = renderVP(selection);
const compiled = compileVP(rendered, rendered.form);
return {
ps: [modifyP(getLength(compiled.ps, length)[0])],
e: compiled.e ? modifyEnglish(compiled.e.join(" • "), tense, isThirdPerson(person)) : "",
};
}
return createVerbTable(makePhrase, isImperativeTense(tense) ? "imperative" : isPastTense(tense) ? "past" : "nonImperative");
},
},
{
key: Math.random(),
// @ts-ignore
block:
verb.entry.c?.includes("intrans.") || voice === "passive"
? { type: "objectSelection", selection: "none" }
: {
type: "objectSelection",
selection: {
type: "NP",
selection: {
type: "noun",
entry: {
ts: 1527812817,
i: 10011,
p: "کتاب",
f: "kitáab",
g: "kitaab",
e: "book",
c: "n. m.",
},
gender: "masc",
genderCanChange: false,
number: "singular",
numberCanChange: true,
adjectives: [],
},
},
},
},
],
verb: {
type: "verb",
verb,
tense: category === "basic" ? tense : tenseToModal(tense),
transitivity: "intransitive",
isCompound: false,
voice,
negative,
canChangeTransitivity: false,
canChangeVoice: false,
canChangeStatDyn: false,
},
form: { removeKing: false, shrinkServant: false },
};
}
function makePhrase(person: T.Person): {
ps: T.ArrayOneOrMore<T.PsString>;
e: string;
} {
const selection = makeSelection(person);
const rendered = renderVP(selection);
const compiled = compileVP(rendered, rendered.form);
return {
ps: [modifyP(getLength(compiled.ps, length)[0])],
e: compiled.e
? modifyEnglish(compiled.e.join(" • "), tense, isThirdPerson(person))
: "",
};
}
return createVerbTable(
makePhrase,
isImperativeTense(tense)
? "imperative"
: isPastTense(tense)
? "past"
: "nonImperative"
);
}
function modifyP(ps: T.PsString): T.PsString {
return {
p: ps.p.replace(" کتاب ", ""),
f: ps.f.replace(" kitáab ", ""),
};
return {
p: ps.p.replace(" کتاب ", ""),
f: ps.f.replace(" kitáab ", ""),
};
}
function modifyEnglish(e: string, tense: T.VerbTense | T.ImperativeTense | T.PerfectTense, isThirdPerson: boolean): string {
// "kitaab" used as a dummy object
const dummyObjectRemoved =
e.replace(/\(a\/the\) +book/ig, "")
return (isPerfectTense(tense) || (isPastTense(tense) && isThirdPerson))
? dummyObjectRemoved
: dummyObjectRemoved
.replace(/he\/it/ig, "he/she/it")
.replace(/We \(m\. pl\.\)/ig, "We ")
.replace(/They \(m\. pl\.\)/ig, "They ")
.replace(/\(m\. pl\.\)/ig, "(pl.)")
.replace(/\(m\.\)/ig, "");
function modifyEnglish(
e: string,
tense: T.VerbTense | T.ImperativeTense | T.PerfectTense,
isThirdPerson: boolean
): string {
// "kitaab" used as a dummy object
const dummyObjectRemoved = e.replace(/\(a\/the\) +book/gi, "");
return isPerfectTense(tense) || (isPastTense(tense) && isThirdPerson)
? dummyObjectRemoved
: dummyObjectRemoved
.replace(/he\/it/gi, "he/she/it")
.replace(/We \(m\. pl\.\)/gi, "We ")
.replace(/They \(m\. pl\.\)/gi, "They ")
.replace(/\(m\. pl\.\)/gi, "(pl.)")
.replace(/\(m\.\)/gi, "");
}
function tenseToStem(t: T.VerbTense | T.ImperativeTense | T.PerfectTense): "imperfective stem" | "perfective stem" | "imperfective root" | "perfective root" | "past participle" {
const stem = t === "presentVerb"
? "imperfective stem"
: t === "subjunctiveVerb"
? "perfective stem"
: t === "imperfectiveFuture"
? "imperfective stem"
: t === "perfectiveFuture"
? "perfective stem"
: t === "imperfectivePast"
? "imperfective root"
: t === "perfectivePast"
? "perfective root"
: t === "habitualImperfectivePast"
? "imperfective root"
: t === "habitualPerfectivePast"
? "perfective root"
: t === "imperfectiveImperative"
? "imperfective stem"
: t === "perfectiveImperative"
? "perfective stem"
: t.endsWith("Perfect")
? "past participle"
: "perfective root";
return stem;
function tenseToStem(
t: T.VerbTense | T.ImperativeTense | T.PerfectTense
):
| "imperfective stem"
| "perfective stem"
| "imperfective root"
| "perfective root"
| "past participle" {
const stem =
t === "presentVerb"
? "imperfective stem"
: t === "subjunctiveVerb"
? "perfective stem"
: t === "imperfectiveFuture"
? "imperfective stem"
: t === "perfectiveFuture"
? "perfective stem"
: t === "imperfectivePast"
? "imperfective root"
: t === "perfectivePast"
? "perfective root"
: t === "habitualImperfectivePast"
? "imperfective root"
: t === "habitualPerfectivePast"
? "perfective root"
: t === "imperfectiveImperative"
? "imperfective stem"
: t === "perfectiveImperative"
? "perfective stem"
: t.endsWith("Perfect")
? "past participle"
: "perfective root";
return stem;
}
function createVerbTable(f: (person: T.Person) => { ps: T.ArrayOneOrMore<T.PsString>, e: string }, type: "imperative" | "nonImperative" | "past"): { ps: T.VerbBlock | T.ImperativeBlock, e: T.EnglishBlock } {
if (type === "imperative") {
const b = [
[f(2), f(8)],
[f(3), f(9)],
];
return {
ps: [
[b[0][0].ps, b[0][1].ps],
[b[1][0].ps, b[1][1].ps],
],
e: [
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
],
};
}
function createVerbTable(
f: (person: T.Person) => { ps: T.ArrayOneOrMore<T.PsString>; e: string },
type: "imperative" | "nonImperative" | "past"
): { ps: T.VerbBlock | T.ImperativeBlock; e: T.EnglishBlock } {
if (type === "imperative") {
const b = [
[f(0), f(6)],
[f(1), f(7)],
[f(2), f(8)],
[f(3), f(9)],
[f(4), f(10)],
[f(5), f(11)],
[f(2), f(8)],
[f(3), f(9)],
];
return {
ps: [
[b[0][0].ps, b[0][1].ps],
[b[1][0].ps, b[1][1].ps],
[b[2][0].ps, b[2][1].ps],
[b[3][0].ps, b[3][1].ps],
[b[4][0].ps, b[4][1].ps],
[b[5][0].ps, b[5][1].ps],
],
e: [
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[2][0].e, b[2][1].e],
[b[3][0].e, b[3][1].e],
[b[4][0].e, b[4][1].e],
[b[5][0].e, b[5][1].e],
],
ps: [
[b[0][0].ps, b[0][1].ps],
[b[1][0].ps, b[1][1].ps],
],
e: [
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
],
};
}
}
const b = [
[f(0), f(6)],
[f(1), f(7)],
[f(2), f(8)],
[f(3), f(9)],
[f(4), f(10)],
[f(5), f(11)],
];
return {
ps: [
[b[0][0].ps, b[0][1].ps],
[b[1][0].ps, b[1][1].ps],
[b[2][0].ps, b[2][1].ps],
[b[3][0].ps, b[3][1].ps],
[b[4][0].ps, b[4][1].ps],
[b[5][0].ps, b[5][1].ps],
],
e: [
[b[0][0].e, b[0][1].e],
[b[1][0].e, b[1][1].e],
[b[2][0].e, b[2][1].e],
[b[3][0].e, b[3][1].e],
[b[4][0].e, b[4][1].e],
[b[5][0].e, b[5][1].e],
],
};
}

View File

@ -1,16 +1,17 @@
import { Component } from "react";
import classNames from "classnames";
import highlightExample from "./highlight-example";
import {
phonemes,
Phoneme,
PhonemeExample,
} from "./phonemes";
import { phonemes, Phoneme, PhonemeExample } from "./phonemes";
import playAudio from "../../lib/play-audio";
import views from "./views";
import Media from "react-media";
export type ViewOptions = "all" | "shortVowel" | "longVowel" | "fiveYs" | "specialConsonant";
export type ViewOptions =
| "all"
| "shortVowel"
| "longVowel"
| "fiveYs"
| "specialConsonant";
interface IAppState {
view: ViewOptions;
@ -28,100 +29,119 @@ class PhoneticsViewer extends Component<any, IAppState> {
const phonemesShowing =
this.state.view === "all"
? phonemes
// @ts-ignore
: phonemes.filter((p) => p[this.state.view]);
: // @ts-ignore
phonemes.filter((p) => p[this.state.view]);
const selectedOption = views.find((v) => v.value === this.state.view);
const generatePlayerFunction = (item: Phoneme | PhonemeExample) => {
if ("phoneme" in item && item.a) {
return () => { playAudio(item.a || ""); };
return () => {
playAudio(item.a || "");
};
}
if ("f" in item && item.a) {
// dumb typescript
return () => { playAudio(item.a || ""); };
// dumb typescript
return () => {
playAudio(item.a || "");
};
}
return () => null;
}
return <>
<div className="text-center mt-4">
<Media queries={{ small: "(max-width: 599px)" }}>
{matches => (
<div className={`btn-group${matches.small ? "-vertical" : ""} mb-3`}>
{views.map(({ label, value }) => (
<button
key={value}
type="button"
className={classNames("btn", "btn-outline-secondary", {
active: this.state.view === value,
})}
onClick={() => this.setState({ view: value })}
>
{label}
</button>
))}
</div>
)}
</Media>
<div className="small mb-2"><i className="fas fa-volume-down"></i> click the phonetic letter or examples to hear - not all sounds are available</div>
</div>
<div style={{ overflowX: "auto", marginBottom: "1em" }}>
<table className="table table-striped">
<thead>
<tr>
<th>Phonetic Letter</th>
{/* <th>IPA Letter</th> */}
<th>Short Explanation</th>
<th>Example</th>
<th>Pashto Letter(s)</th>
</tr>
</thead>
<tbody>
{phonemesShowing.map((phoneme) => (
<tr key={phoneme.phoneme}>
<td onClick={generatePlayerFunction(phoneme)}>
{phoneme.phoneme}
</td>
{/* <td>{phoneme.ipa.letter} </td> */}
<td>
{phoneme.quickExplanation}{" "}
{phoneme.ipa.video && (
<a href={phoneme.ipa.video} target="_blank" rel="noopener noreferrer">
<i className="fa fa-video" />
</a>
)}
</td>
<td onClick={generatePlayerFunction(phoneme.examples[0])}>
{highlightExample(
phoneme.examples[0].f,
phoneme.examples[0].fHighlight
)}
{` - `}
{highlightExample(
phoneme.examples[0].p,
phoneme.examples[0].pHighlight
)}
</td>
<td>
{phoneme.possibleLetters
? phoneme.possibleLetters.reduce(
(s, l) =>
`${s}${l.letter} ${
l.alternate ? ` (${l.alternate}) ` : ""
}`,
""
)
: ""}
{/* phoneme.diacritic && `(diacritic ◌${phoneme.diacritic})` */}
</td>
};
return (
<>
<div className="text-center mt-4">
<Media queries={{ small: "(max-width: 599px)" }}>
{(matches) => (
<div
className={`btn-group${matches.small ? "-vertical" : ""} mb-3`}
>
{views.map(({ label, value }) => (
<button
key={value}
type="button"
className={classNames("btn", "btn-outline-secondary", {
active: this.state.view === value,
})}
onClick={() => this.setState({ view: value })}
>
{label}
</button>
))}
</div>
)}
</Media>
<div className="small mb-2">
<i className="fas fa-volume-down"></i> click the phonetic letter or
examples to hear - not all sounds are available
</div>
</div>
<div style={{ overflowX: "auto", marginBottom: "1em" }}>
<table className="table table-striped">
<thead>
<tr>
<th>Phonetic Letter</th>
{/* <th>IPA Letter</th> */}
<th>Short Explanation</th>
<th>Example</th>
<th>Pashto Letter(s)</th>
</tr>
))}
</tbody>
</table>
</div>
{selectedOption?.notes && <div>
<p><strong>Notes about {selectedOption.label.toLowerCase()}:</strong></p>
{selectedOption.notes}
</div>}
</>;
</thead>
<tbody>
{phonemesShowing.map((phoneme) => (
<tr key={phoneme.phoneme}>
<td onClick={generatePlayerFunction(phoneme)}>
{phoneme.phoneme}
</td>
{/* <td>{phoneme.ipa.letter} </td> */}
<td>
{phoneme.quickExplanation}{" "}
{phoneme.ipa.video && (
<a
href={phoneme.ipa.video}
target="_blank"
rel="noopener noreferrer"
>
<i className="fa fa-video" />
</a>
)}
</td>
<td onClick={generatePlayerFunction(phoneme.examples[0])}>
{highlightExample(
phoneme.examples[0].f,
phoneme.examples[0].fHighlight
)}
{` - `}
{highlightExample(
phoneme.examples[0].p,
phoneme.examples[0].pHighlight
)}
</td>
<td>
{phoneme.possibleLetters
? phoneme.possibleLetters.reduce(
(s, l) =>
`${s}${l.letter} ${
l.alternate ? ` (${l.alternate}) ` : ""
}`,
""
)
: ""}
{/* phoneme.diacritic && `(diacritic ◌${phoneme.diacritic})` */}
</td>
</tr>
))}
</tbody>
</table>
</div>
{selectedOption?.notes && (
<div>
<p>
<strong>Notes about {selectedOption.label.toLowerCase()}:</strong>
</p>
{selectedOption.notes}
</div>
)}
</>
);
}
}

View File

@ -1,21 +1,24 @@
import React, { ReactNode } from "react";
export default function highlightExample(text: string, highlight: number[][]): ReactNode {
if (!highlight.length) {
return text;
}
let index = 0;
// @ts-ignore
const pText = highlight.reduce((acc, curr, i): ReactNode => {
const isLastElement = i === (highlight.length - 1);
const section = [
...acc,
curr[0] > 0 ? text.slice(index, curr[0]) : "",
<strong>{text.slice(curr[0], curr[1] + 1)}</strong>,
isLastElement ? text.slice(curr[1] + 1) : "",
];
index = curr[1] + 1;
return section;
}, []);
return pText;
export default function highlightExample(
text: string,
highlight: number[][]
): ReactNode {
if (!highlight.length) {
return text;
}
let index = 0;
// @ts-ignore
const pText = highlight.reduce((acc, curr, i): ReactNode => {
const isLastElement = i === highlight.length - 1;
const section = [
...acc,
curr[0] > 0 ? text.slice(index, curr[0]) : "",
<strong>{text.slice(curr[0], curr[1] + 1)}</strong>,
isLastElement ? text.slice(curr[1] + 1) : "",
];
index = curr[1] + 1;
return section;
}, []);
return pText;
}

View File

@ -105,7 +105,7 @@ export const letters: IAlphabet = {
export const diacritics = {
zwar: "َ",
zwarakey: "ٙ",
zwarakay: "ٙ",
zer: "ِ",
pesh: "ُ",
sukun: "ْ",
@ -116,30 +116,28 @@ export const diacritics = {
fathahan: "ً",
};
export type PhonemeExample = (
T.PsString &
{
pHighlight: number[][],
fHighlight: number[][],
a?: string,
audio?: { externalLink: string },
});
export type PhonemeExample = T.PsString & {
pHighlight: number[][];
fHighlight: number[][];
a?: string;
audio?: { externalLink: string };
};
export type Phoneme = {
phoneme: string,
a?: string,
quickExplanation: string | JSX.Element,
specialConsonant?: boolean,
shortVowel?: boolean,
longVowel?: boolean,
addAlefToStart?: boolean,
fiveYs?: boolean,
canBeIgnored?: boolean,
ipa: any,
onlyOnEnd?: boolean,
endingLetter?: ILetter,
examples: PhonemeExample[],
} & ({ possibleLetters: ILetter[] } | { diacritic: string })
phoneme: string;
a?: string;
quickExplanation: string | JSX.Element;
specialConsonant?: boolean;
shortVowel?: boolean;
longVowel?: boolean;
addAlefToStart?: boolean;
fiveYs?: boolean;
canBeIgnored?: boolean;
ipa: any;
onlyOnEnd?: boolean;
endingLetter?: ILetter;
examples: PhonemeExample[];
} & ({ possibleLetters: ILetter[] } | { diacritic: string });
export const phonemes = [
// consonants
@ -567,9 +565,9 @@ export const phonemes = [
examples: [
{
p: "وږی",
f: "wuGey",
f: "wuGay",
e: "hungry",
a: "wuggey",
a: "wuggay",
pHighlight: [[1, 1]],
fHighlight: [[2, 2]],
},
@ -824,8 +822,7 @@ export const phonemes = [
quickExplanation: "l with back of tongue higher up",
ipa: {
letter: "l",
link:
"https://en.wikipedia.org/wiki/Voiced_dental,_alveolar_and_postalveolar_lateral_approximants#Voiced_alveolar_lateral_approximant",
link: "https://en.wikipedia.org/wiki/Voiced_dental,_alveolar_and_postalveolar_lateral_approximants#Voiced_alveolar_lateral_approximant",
},
examples: [
{
@ -833,8 +830,14 @@ export const phonemes = [
f: "leekul",
e: "to write",
a: "leekul",
pHighlight: [[0, 0], [3, 3]],
fHighlight: [[0, 0], [5, 5]],
pHighlight: [
[0, 0],
[3, 3],
],
fHighlight: [
[0, 0],
[5, 5],
],
},
],
},
@ -940,8 +943,8 @@ export const phonemes = [
examples: [
{
p: "وږی",
f: "wuGey",
a: "wuggey",
f: "wuGay",
a: "wuggay",
e: "hungry",
pHighlight: [[0, 0]],
fHighlight: [[0, 0]],
@ -994,9 +997,9 @@ export const phonemes = [
examples: [
{
p: "سَړی",
f: "saRey",
f: "saRay",
e: "man",
a: "sarrey",
a: "sarray",
pHighlight: [[1, 1]],
fHighlight: [[1, 1]],
},
@ -1059,7 +1062,7 @@ export const phonemes = [
{
phoneme: "u",
shortVowel: true,
diacritic: diacritics.zwarakey,
diacritic: diacritics.zwarakay,
quickExplanation: (
<>
shwa sound similar to u in b<strong>u</strong>d
@ -1200,11 +1203,7 @@ export const phonemes = [
longVowel: true,
fiveYs: true,
addAlefToStart: true,
quickExplanation: (
<>
close to ee in b<strong>ee</strong> but more open
</>
),
quickExplanation: <>'ee' sound but with mouth slightly more open</>,
ipa: {
letter: "e",
},
@ -1220,15 +1219,15 @@ export const phonemes = [
],
},
{
phoneme: "ey",
a: "ey",
phoneme: "ay",
a: "ay",
possibleLetters: [letters.naareenaYe],
longVowel: true,
fiveYs: true,
addAlefToStart: true,
quickExplanation: (
<>
similar to ay in d<strong>ay</strong>
short 'a' sound + y. similar to ay in d<strong>ay</strong>
</>
),
ipa: {
@ -1237,32 +1236,31 @@ export const phonemes = [
examples: [
{
p: "سړی",
f: "saRey",
f: "saRay",
e: "man",
a: "saRey",
a: "saRay",
pHighlight: [[2, 2]],
fHighlight: [[3, 4]],
},
],
},
{
phoneme: "eyy",
a: "eyy",
phoneme: "ey",
a: "ey",
possibleLetters: [letters.faailiyaYe],
longVowel: true,
fiveYs: true,
onlyOnEnd: true,
quickExplanation:
"Close or the same as 'uy'. Starts with a ey sound and glides into an ee at the end",
quickExplanation: "e sound + y",
ipa: {
letter: "əi",
},
examples: [
{
p: "کښېنئ",
f: "kxeneyy",
f: "kxeney",
e: "please sit",
a: "kxeneyy",
a: "kxeney",
pHighlight: [[4, 4]],
fHighlight: [[4, 6]],
},
@ -1274,8 +1272,7 @@ export const phonemes = [
possibleLetters: [letters.xudzeenaYe],
longVowel: true,
fiveYs: true,
quickExplanation:
"Starts with a u (schwa) sound and glides into an ee at the end",
quickExplanation: "u (schwa) sound + y",
ipa: {
letter: "əi",
},

View File

@ -23,9 +23,9 @@ import e from "./audio/e.m4a";
// @ts-ignore
import ee from "./audio/ee.m4a";
// @ts-ignore
import ey from "./audio/ey.m4a";
import ay from "./audio/ey.m4a";
// @ts-ignore
import eyy from "./audio/eyy.m4a";
import ey from "./audio/eyy.m4a";
// @ts-ignore
import gh from "./audio/gh.m4a";
// @ts-ignore
@ -144,10 +144,76 @@ import naayee from "./audio/naayee.mp3";
import fojee from "./audio/fojee.mp3";
export default {
a, aa, aasmaan, d, D, DoDuy, dwa, dz, dzaay, dzungul, e, ee, ey,
eyy, gh, gharma, h, halaat, i, injuluy, islaam, joR, jz, jzwund, kadoo,
kh, khwux, kxeneyy, l, leekul, lUtfan, maalTa, maNa, meena, N, o, oo,
oox, ooy, paaNa, poza, puxto, q, qaazee, r, R, rang, saRey, t, tor, ts,
tsomra, T, u, uu, uy, w, wuGey, waadu, x, xudza, xudze, zooy, sheen, chaa,
baad, kor, gUl, maat, maruy, naayee, fojee,
a,
aa,
aasmaan,
d,
D,
DoDuy,
dwa,
dz,
dzaay,
dzungul,
e,
ee,
ay,
ey,
gh,
gharma,
h,
halaat,
i,
injuluy,
islaam,
joR,
jz,
jzwund,
kadoo,
kh,
khwux,
kxeneyy,
l,
leekul,
lUtfan,
maalTa,
maNa,
meena,
N,
o,
oo,
oox,
ooy,
paaNa,
poza,
puxto,
q,
qaazee,
r,
R,
rang,
saRey,
t,
tor,
ts,
tsomra,
T,
u,
uu,
uy,
w,
wuGey,
waadu,
x,
xudza,
xudze,
zooy,
sheen,
chaa,
baad,
kor,
gUl,
maat,
maruy,
naayee,
fojee,
};

View File

@ -34,7 +34,8 @@ const views: {
<div>
<p>
Long vowels are <strong>always written</strong> in Pashto script. When
long vowels come at the beginning of a word, they are prefixed with a letter alef (ا).
long vowels come at the beginning of a word, they are prefixed with a
letter alef (ا).
</p>
<p>for example:</p>
<ul>
@ -46,8 +47,9 @@ const views: {
</li>
</ul>
<p>
Note: When ey - ی or ee - ي are written in the middle of a word, both appear as ـیـ.
To differentiate ee - ي from ey - ی you can (optionally) add a ِ diacritic. (eg. شِین - sheen)
Note: When ay - ی or ee - ي are written in the middle of a word, both
appear as ـیـ. To differentiate ee - ي from ay - ی you can
(optionally) add a ِ diacritic. (eg. شِین - sheen)
</p>
</div>
),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,7 @@
title: Intro to Compound Verbs
---
import {
defaultTextOptions as opts,
Examples,
InlinePs,
} from "@lingdocs/ps-react";
import { KidsSection, VP, KingIcon, ServantIcon } from "../../components/terms-links";
import psmd from "../../lib/psmd";
import Link from "../../components/Link";
import Formula from "../../components/formula/Formula";
import { defaultTextOptions as opts, InlinePs } from "@lingdocs/ps-react";
import cookingPotatoes from "./cooking-potatoes.svg";
import doingWork from "./doing-work.svg";
import stativeCompoundTransitive from "./stative-compound-transitive.svg";
@ -19,73 +11,86 @@ import dynamicCopmoundTransitive from "./dynamic-compound-transitive.svg";
Compound verbs are verbs made up from a helper verb and some other word. These are extremely common in Pashto. In fact, 70% of the verbs in the [LingDocs Pashto Dictionary](https://dictionary.lingdocs.com) are compound verbs. Here are some examples:
<ul>
<li>
<InlinePs
opts={opts}
ps={{ p: "کار کول", f: "kaar kawul", e: "to work" }}
/>
</li>
<ul>
<li>
<InlinePs opts={opts} ps={{ p: "کار کول", f: "kaar kawul", e: "to work" }} />
<InlinePs opts={opts} ps={{ p: "کار", f: "kaar", e: "work" }} /> +{" "}
<InlinePs opts={opts} ps={{ p: "کول", f: "kawul", e: "to do" }} />
</li>
<ul>
<li>
<InlinePs opts={opts} ps={{ p: "کار", f: "kaar", e: "work" }} /> + <InlinePs opts={opts} ps={{ p: "کول", f: "kawul", e: "to do" }} />
</li>
</ul>
</ul>
<li>
<InlinePs
opts={opts}
ps={{ p: "منډې وهل", f: "munDe wahul", e: "to run" }}
/>
</li>
<ul>
<li>
<InlinePs opts={opts} ps={{ p: "منډې وهل", f: "munDe wahul", e: "to run" }} />
<InlinePs opts={opts} ps={{ p: "منډې", f: "munDe", e: "runs" }} /> +{" "}
<InlinePs opts={opts} ps={{ p: "وهل", f: "wahul", e: "to hit" }} />
</li>
<ul>
<li>
<InlinePs opts={opts} ps={{ p: "منډې", f: "munDe", e: "runs" }} /> + <InlinePs opts={opts} ps={{ p: "وهل", f: "wahul", e: "to hit" }} />
</li>
</ul>
</ul>
<li>
<InlinePs opts={opts} ps={{ p: "بندول", f: "bandawul", e: "to close" }} />
</li>