Merge branch '385-multiple-example-sentences-per-morpheme-category' into 'main'

multiple example sentences per morpheme category

Closes #385

See merge request PronounsPage/PronounsPage!448
This commit is contained in:
Valentyne Stigloher 2024-05-13 10:17:41 +00:00
commit 87e4001a20
53 changed files with 912 additions and 373 deletions

View File

@ -21,7 +21,8 @@ body[data-theme="dark"] {
/* BUTTONS */
.btn-link { color: #fff; }
.btn-link { color: $primary-dark; }
.btn-link:hover { color: lighten($primary-dark, 10%); }
.btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
.btn-dark { color: #000; background-color: #f8f9fa; border-color: #f8f9fa; }
.btn-dark:hover { color: #000; background-color: #f9fafb; border-color: #f9fafb; }

View File

@ -14,14 +14,17 @@
</span>
</template>
<script>
export default {
<script lang="ts">
import Vue from 'vue';
import { Example, Pronoun } from '../src/classes.ts';
export default Vue.extend({
props: {
example: { required: true },
pronoun: { required: true },
counter: { default: 0 },
example: { required: true, type: Example },
pronoun: { required: true, type: Pronoun },
counter: { default: 0, type: Number },
link: { type: Boolean },
pronunciation: { type: Boolean },
},
};
});
</script>

View File

@ -0,0 +1,74 @@
<template>
<li v-if="pronoun">
<Example :example="example" :pronoun="pronoun" :counter="counter" :link="link" :pronunciation="pronunciation" />
<Tooltip :text="tooltipText">
<button
v-if="hasDifferentExample"
type="button"
class="btn btn-sm btn-link p-1"
@click="selectDifferentExample()"
>
<Icon v="random" />
</button>
</Tooltip>
</li>
</template>
<script lang="ts">
import Vue from 'vue';
import type { PropType } from 'vue';
import { ExampleCategory } from '../src/classes.ts';
import type { Example, Pronoun } from '../src/classes.ts';
import { randomItem } from '../src/helpers.ts';
export default Vue.extend({
props: {
exampleCategory: { required: true, type: ExampleCategory },
pronounsChoice: { required: true, type: Array as PropType<Pronoun[]> },
counter: { default: 0, type: Number },
link: { type: Boolean },
pronunciation: { type: Boolean },
},
data() {
return {
pronoun: null as Pronoun | null,
example: this.exampleCategory.examples[0],
};
},
computed: {
suitableExamples(): Example[] {
return this.exampleCategory.examples.filter((example) => {
return this.pronounsChoice.some((pronoun) => example.requiredMorphemesPresent(pronoun, this.counter));
});
},
hasDifferentExample(): boolean {
return this.pronounsChoice.length > 1 || this.suitableExamples.length > 1;
},
tooltipText(): string {
if (this.exampleCategory.name) {
return this.$t('pronouns.examples.shuffleNamed', { category: this.exampleCategory.name });
}
return this.$t('pronouns.examples.shuffle');
},
},
created() {
this.selectPronounForExample(false);
},
methods: {
selectDifferentExample(): void {
const changeExample = this.suitableExamples.length > 1;
if (changeExample) {
const differentExamples = this.suitableExamples.filter((example) => example !== this.example);
this.example = randomItem(differentExamples);
}
this.selectPronounForExample(!changeExample);
},
selectPronounForExample(forceDifferent: boolean): void {
const suitablePronouns = this.pronounsChoice.filter((pronoun) => {
return this.example.requiredMorphemesPresent(pronoun) && (!forceDifferent || pronoun !== this.pronoun);
});
this.pronoun = randomItem(suitablePronouns);
},
},
});
</script>

View File

@ -1,42 +1,46 @@
<template>
<span
:class="['morpheme', 'rounded', highlightedMorpheme == baseMorpheme ? 'bg-primary text-white' : '']"
@mouseenter="highlightMorpheme(baseMorpheme)"
@mouseleave="highlightMorpheme(null)"
@touchstart="highlightMorpheme(highlightedMorpheme == baseMorpheme ? null : baseMorpheme)"
:class="['morpheme', 'rounded', isHighlighted ? 'text-bg-primary' : '']"
@mouseenter="highlightMorphemes(highlightsMorphemes)"
@mouseleave="highlightMorphemes(new Set())"
><Spelling escape :text="prepend + pronoun.getMorpheme(morpheme, counter) + append" /></span>
</template>
<script>
import { mapState } from 'vuex';
<script lang="ts">
import Vue from 'vue';
import type { PropType } from 'vue';
import { Pronoun } from '../src/classes.ts';
import { intersection } from '../src/sets.ts';
export default {
export default Vue.extend({
props: {
pronoun: { required: true },
morpheme: { required: true },
counter: { default: 0 },
pronoun: { required: true, type: Pronoun },
morpheme: { required: true, type: String },
counter: { default: 0, type: Number },
prepend: { default: '' },
append: { default: '' },
prepend: { default: '', type: String },
append: { default: '', type: String },
highlightsMorphemes: {
default() {
if (this.morpheme.startsWith('\'')) {
return new Set([this.morpheme.substring(1)]);
}
return new Set([this.morpheme]);
},
type: Set as PropType<Set<string>>,
},
},
computed: {
baseMorpheme: {
get() {
if (this.morpheme[0] == '\'') {
return this.morpheme.substring(1);
} else {
return this.morpheme;
}
},
isHighlighted(): boolean {
return intersection(this.highlightsMorphemes, this.$store.state.highlightedMorphemes).size > 0;
},
...mapState(['highlightedMorpheme']),
},
methods: {
highlightMorpheme(morpheme) {
this.$store.commit('highlightMorpheme', morpheme);
highlightMorphemes(morphemes: Set<string>): void {
this.$store.commit('highlightMorphemes', morphemes);
},
},
};
});
</script>
<style>

View File

@ -1,25 +1,69 @@
<template>
<span v-if="pronoun.getMorpheme(morpheme, counter)">
<Morpheme :pronoun="pronoun" :morpheme="morpheme" :counter="counter" :prepend="prepend" :append="append" />
<Pronunciation
v-if="pronoun.pronounceable && pronoun.getPronunciation(morpheme, counter) && !pronoun.getPronunciation(morpheme, counter).startsWith('=')"
:pronunciation="`/${prependPr}${pronoun.getPronunciation(morpheme, counter)}${appendPr}/`"
text
<Popover v-if="pronoun.getMorpheme(morpheme, counter)" resize>
<Morpheme
:pronoun="pronoun"
:morpheme="morpheme"
:counter="counter"
:prepend="prepend"
:append="append"
:highlights-morphemes="highlightsMorphemes"
/>
</span>
<Pronunciation v-if="pronunciation" :pronunciation="pronunciation" text />
<template v-if="examples.length > 0" #content>
<ul class="my-0 ps-3">
<li v-for="example in examples" class="my-1">
<Example
:example="example"
:pronoun="pronoun"
:counter="counter"
/>
</li>
</ul>
</template>
</Popover>
</template>
<script>
export default {
props: {
pronoun: { required: true },
morpheme: { required: true },
counter: { default: 0 },
<script lang="ts">
import Vue from 'vue';
import type { PropType } from 'vue';
import { Pronoun } from '../src/classes.ts';
import { examples } from '../src/data.js';
import type { Example } from '../src/classes.ts';
prepend: { default: '' },
prependPr: { default: '' },
append: { default: '' },
appendPr: { default: '' },
export default Vue.extend({
props: {
pronoun: { required: true, type: Pronoun },
morpheme: { required: true, type: String },
counter: { default: 0, type: Number },
prepend: { default: '', type: String },
prependPr: { default: '', type: String },
append: { default: '', type: String },
appendPr: { default: '', type: String },
highlightsMorphemes: {
default() {
if (this.morpheme.startsWith('\'')) {
return new Set([this.morpheme.substring(1)]);
}
return new Set([this.morpheme]);
},
type: Set as PropType<Set<string>>,
},
},
};
computed: {
examples(): Example[] {
return examples.filter((example) => {
return example.requiredMorphemesPresent(this.pronoun, this.counter) &&
[...this.highlightsMorphemes.values()].some((morpheme) => example.hasMorpheme(morpheme));
});
},
pronunciation(): string | null {
const pronunciation = this.pronoun.getPronunciation(this.morpheme, this.counter);
if (!this.pronoun.pronounceable || !pronunciation || pronunciation.startsWith('=')) {
return null;
}
return `/${this.prependPr}${this.pronoun.getPronunciation(this.morpheme, this.counter)}${this.appendPr}/`;
},
},
});
</script>

178
components/Popover.vue Normal file
View File

@ -0,0 +1,178 @@
<template>
<span
ref="reference"
@mouseenter="show"
@mouseleave="hide"
><slot></slot><span
v-if="visible && hasContent"
ref="floating"
class="popover"
:style="floatingStyles"
><span
ref="arrow"
class="popover-arrow bg-dark text-white"
:style="arrowStyles"
></span><div
ref="content"
class="overflow-auto bg-dark text-white px-2 py-1 rounded"
><slot name="content"></slot></div></span></span>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { arrow, autoUpdate, computePosition, flip, offset, shift, size } from '@floating-ui/dom';
import Vue from 'vue';
import type { CSSProperties } from 'vue/types/jsx';
import type { ComputePositionReturn, Placement } from '@floating-ui/dom';
const getDPR = (element: Element): number => {
if (typeof window === 'undefined') {
return 1;
}
const win = element.ownerDocument.defaultView || window;
return win.devicePixelRatio || 1;
};
const roundByDPR = (element: Element, value: number): number => {
const dpr = getDPR(element);
return Math.round(value * dpr) / dpr;
};
const remToPx = (valueInRem: number): number => {
return valueInRem * parseFloat(getComputedStyle(document.documentElement).fontSize);
};
interface Data {
visible: boolean;
position: ComputePositionReturn | null;
cleanup: (() => void) | null;
}
interface Refs {
reference: HTMLSpanElement | undefined;
floating: HTMLSpanElement | undefined;
content: HTMLSpanElement | undefined;
arrow: HTMLSpanElement | undefined;
}
export default Vue.extend({
props: {
placement: { default: 'bottom', type: String as PropType<Placement> },
resize: { type: Boolean },
},
data(): Data {
return {
visible: false,
position: null,
cleanup: null,
};
},
computed: {
$tRefs(): Refs {
return this.$refs as unknown as Refs;
},
hasContent(): boolean {
return !!this.$slots.content?.[0];
},
floatingStyles(): CSSProperties | undefined {
if (!this.position || !this.$tRefs.floating) {
return;
}
const xVal = roundByDPR(this.$tRefs.floating, this.position.x);
const yVal = roundByDPR(this.$tRefs.floating, this.position.y);
return {
transform: `translate(${xVal}px, ${yVal}px)`,
...getDPR(this.$tRefs.floating) >= 1.5 && {
willChange: 'transform',
},
};
},
arrowStyles(): CSSProperties | undefined {
if (!this.position) {
return;
}
const arrowData = this.position.middlewareData.arrow;
const staticSite = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[this.position.placement.split('-')[0]]!;
return {
left: typeof arrowData?.x === 'number' ? `${arrowData.x}px` : '',
top: typeof arrowData?.y === 'number' ? `${arrowData.y}px` : '',
[staticSite]: '-0.25rem',
};
},
},
beforeDestroy() {
this.hide();
},
methods: {
async show() {
this.visible = true;
// floating element will be rendered on next tick
await this.$nextTick();
if (!this.$tRefs.reference || !this.$tRefs.floating) {
return;
}
// remove title from reference element to prevent a double tooltip
this.$tRefs.reference.removeAttribute('title');
this.cleanup = autoUpdate(this.$tRefs.reference, this.$tRefs.floating, () => this.update());
},
hide() {
this.visible = false;
if (this.cleanup) {
this.cleanup();
this.cleanup = null;
}
},
async update() {
if (!this.$tRefs.reference || !this.$tRefs.floating || !this.$tRefs.arrow) {
return;
}
const middleware = [offset(remToPx(0.25)), flip(), shift()];
if (this.resize) {
middleware.push(size({
apply: ({ availableWidth, availableHeight }) => {
if (!this.$tRefs.content) {
return;
}
Object.assign(this.$tRefs.content.style, {
maxWidth: `${availableWidth}px`,
maxHeight: `${availableHeight}px`,
});
},
}));
}
middleware.push(arrow({ element: this.$tRefs.arrow }));
this.position = await computePosition(this.$tRefs.reference, this.$tRefs.floating, {
middleware,
placement: this.placement as Placement,
});
},
},
});
</script>
<style lang="scss">
.popover {
position: absolute;
top: 0;
left: 0;
font-weight: normal;
font-style: normal;
font-size: .85rem;
z-index: 999;
}
.popover-arrow {
position: absolute;
width: 0.5rem;
height: 0.5rem;
transform: rotate(45deg);
}
</style>

View File

@ -1,13 +1,15 @@
<template>
<a
:class="['mr-2', !pronunciation ? 'disabled' : '']"
dir="ltr"
:href="pronunciationLink"
target="_blank"
@click.prevent="pronounce"
>
<Icon :v="icon" /><sub v-if="name">{{ name }}</sub>
</a>
<Tooltip :text="$t('pronunciation.play')">
<a
:class="['btn', 'btn-sm', 'btn-link', 'p-1', !pronunciation ? 'disabled' : '']"
dir="ltr"
:href="pronunciationLink"
target="_blank"
@click.prevent="pronounce"
>
<Icon :v="icon" /><sub v-if="name">{{ name }}</sub>
</a>
</Tooltip>
</template>

View File

@ -1,152 +1,22 @@
<template>
<span
ref="reference"
<Popover
:title="text"
:aria-label="text"
@mouseenter="show"
@mouseleave="hide"
@touchstart="visible ? hide() : show()"
><slot></slot><span
v-if="visible"
ref="floating"
class="tooltip-content bg-dark text-white px-2 py-1 rounded"
:style="floatingStyles"
>{{ text }}<span ref="arrow" class="tooltip-arrow bg-dark text-white" :style="arrowStyles"></span></span></span>
placement="top"
>
<slot></slot>
<template #content>
{{ text }}
</template>
</Popover>
</template>
<script lang="ts">
import Vue from 'vue';
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
import type { ComputePositionReturn } from '@floating-ui/dom';
import type { CSSProperties } from 'vue/types/jsx';
const getDPR = (element: Element): number => {
if (typeof window === 'undefined') {
return 1;
}
const win = element.ownerDocument.defaultView || window;
return win.devicePixelRatio || 1;
};
const roundByDPR = (element: Element, value: number): number => {
const dpr = getDPR(element);
return Math.round(value * dpr) / dpr;
};
const remToPx = (valueInRem: number): number => {
return valueInRem * parseFloat(getComputedStyle(document.documentElement).fontSize);
};
interface TooltipData {
visible: boolean;
position: ComputePositionReturn | null;
cleanup: (() => void) | null;
}
interface TooltipRefs {
reference: HTMLSpanElement | undefined;
floating: HTMLSpanElement | undefined;
arrow: HTMLSpanElement | undefined;
}
export default Vue.extend({
props: {
text: { required: true, type: String },
},
data(): TooltipData {
return {
visible: false,
position: null,
cleanup: null,
};
},
computed: {
$tRefs(): TooltipRefs {
return this.$refs as unknown as TooltipRefs;
},
floatingStyles(): CSSProperties | undefined {
if (!this.position || !this.$tRefs.floating) {
return;
}
const xVal = roundByDPR(this.$tRefs.floating, this.position.x);
const yVal = roundByDPR(this.$tRefs.floating, this.position.y);
return {
transform: `translate(${xVal}px, ${yVal}px)`,
...getDPR(this.$tRefs.floating) >= 1.5 && {
willChange: 'transform',
},
};
},
arrowStyles(): CSSProperties | undefined {
if (!this.position) {
return;
}
const arrowData = this.position.middlewareData.arrow;
const staticSite = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[this.position.placement.split('-')[0]]!;
return {
left: typeof arrowData?.x === 'number' ? `${arrowData.x}px` : '',
top: typeof arrowData?.y === 'number' ? `${arrowData.y}px` : '',
[staticSite]: '-0.25rem',
};
},
},
beforeDestroy() {
this.hide();
},
methods: {
async show() {
this.visible = true;
// floating element will be rendered on next tick
await this.$nextTick();
if (!this.$tRefs.reference || !this.$tRefs.floating) {
return;
}
// remove title from reference element to prevent a double tooltip
this.$tRefs.reference.removeAttribute('title');
this.cleanup = autoUpdate(this.$tRefs.reference, this.$tRefs.floating, () => this.update());
},
hide() {
this.visible = false;
if (this.cleanup) {
this.cleanup();
this.cleanup = null;
}
},
async update() {
if (!this.$tRefs.reference || !this.$tRefs.floating || !this.$tRefs.arrow) {
return;
}
this.position = await computePosition(this.$tRefs.reference, this.$tRefs.floating, {
middleware: [offset(remToPx(0.25)), flip(), shift(), arrow({ element: this.$tRefs.arrow })],
placement: 'top',
});
},
},
});
</script>
<style lang="scss">
.tooltip-content {
position: absolute;
top: 0;
left: 0;
font-weight: normal;
font-style: normal;
font-size: .85rem;
}
.tooltip-arrow {
position: absolute;
width: 0.5rem;
height: 0.5rem;
transform: rotate(45deg);
}
</style>

View File

@ -63,7 +63,10 @@ home:
pronouns:
header: 'Pronouns'
headerLong: 'Pronouns'
examples: 'Example usage in sentences'
examples:
header: 'Example usage in sentences'
shuffle: 'Show different example'
shuffleNamed: 'Show different example for %category%'
plural: 'Plural'
intro: 'My pronouns are'
normative: 'Normative'
@ -91,6 +94,9 @@ pronouns:
or: 'or'
grammarTable: 'Table'
pronunciation:
play: 'Play audio sample'
sources:
header: 'Sources'
headerLong: 'Examples from cultural texts'

View File

@ -58,7 +58,8 @@ home:
pronouns:
header: 'الضمائر'
headerLong: 'الضمائر'
examples: 'تُستعمل في الجمل كالآتي'
examples:
header: 'تُستعمل في الجمل كالآتي'
plural: 'صيغة الجمع'
intro: 'ضمائري هي'
normative: 'المعياريه '

View File

@ -90,6 +90,10 @@ export interface PronounsConfig {
* whether pronouns exists which are considered honorifics and thus alter example sentences
*/
honorifics: boolean;
/**
* how to group example sentences. if not present, all example sentences are shown
*/
exampleCategories?: ExampleCategory[];
multiple: MultiplePronounsConfig;
/**
* whether null pronouns are explained or can be generated.
@ -143,6 +147,22 @@ export interface PronounsConfig {
disableDisclaimer?: boolean;
}
interface ExampleCategory {
/**
* short description of the category (translated)
*/
name: string;
/**
* which morphemes belong to this category. visible example sentences will contain at least one listed morpheme
*/
morphemes: string[];
/**
* whether this category is only shown in comprehensive view
* @default false
*/
comprehensive?: boolean;
}
interface MultiplePronounsConfig {
/**
* short description in the header (translated)

View File

@ -10,6 +10,79 @@ pronouns:
comprehensive: 'erweitert'
plurals: true
honorifics: false
exampleCategories:
-
name: 'Personalpronomen im Nominativ'
morphemes: ['pronoun_n']
-
name: 'Possessiv'
morphemes:
- 'possessive_determiner_f_n'
- 'possessive_determiner_f_g'
- 'possessive_determiner_f_a'
- 'possessive_determiner_f_d'
- 'possessive_determiner_m_n'
- 'possessive_determiner_m_g'
- 'possessive_determiner_m_a'
- 'possessive_determiner_m_d'
- 'possessive_determiner_n_n'
- 'possessive_determiner_n_g'
- 'possessive_determiner_n_a'
- 'possessive_determiner_n_d'
- 'possessive_determiner_x_n'
- 'possessive_determiner_x_g'
- 'possessive_determiner_x_a'
- 'possessive_determiner_x_d'
- 'possessive_determiner_pl_n'
- 'possessive_determiner_pl_g'
- 'possessive_determiner_pl_a'
- 'possessive_determiner_pl_d'
- 'possessive_pronoun_f_n'
- 'possessive_pronoun_f_g'
- 'possessive_pronoun_f_a'
- 'possessive_pronoun_f_d'
- 'possessive_pronoun_m_n'
- 'possessive_pronoun_m_g'
- 'possessive_pronoun_m_a'
- 'possessive_pronoun_m_d'
- 'possessive_pronoun_n_n'
- 'possessive_pronoun_n_g'
- 'possessive_pronoun_n_a'
- 'possessive_pronoun_n_d'
- 'possessive_pronoun_x_n'
- 'possessive_pronoun_x_g'
- 'possessive_pronoun_x_a'
- 'possessive_pronoun_x_d'
- 'possessive_pronoun_pl_n'
- 'possessive_pronoun_pl_g'
- 'possessive_pronoun_pl_a'
- 'possessive_pronoun_pl_d'
-
name: 'Personalpronomen im Dativ'
morphemes: ['pronoun_d']
-
name: 'Personalpronomen im Akkusativ'
morphemes: ['pronoun_a']
-
name: 'Personalpronomen im Genitiv'
morphemes: ['pronoun_g']
comprehensive: true
-
name: 'Relativpronomen'
morphemes: ['relative_n', 'relative_g', 'relative_d', 'relative_a']
comprehensive: true
-
name: 'Demonstrativpronomen'
morphemes: ['demonstrative_n', 'demonstrative_g', 'demonstrative_d', 'demonstrative_a']
comprehensive: true
-
name: 'weitere Pronomen und Adjektive'
morphemes: ['pronoun_equal', 'possessive_pronoun_substantivized', 'adjective_back_then']
comprehensive: true
-
name: 'Adverbien'
morphemes: ['adverb_because', 'adverb_back_then', 'adverb_by']
comprehensive: true
multiple:
name: 'Austauschbare Pronomen'
description: 'Einige nichtbinäre Menschen nutzen mehr als eine Form von Pronomen und sind damit einverstanden, mit irgendwelchen dieser Pronomen angesprochen zu werden.'

View File

@ -49,10 +49,11 @@
</th>
</template>
</template>
<td v-for="morpheme in variant.morphemes" :key="morpheme">
<td v-for="morphemeCell in variant.morphemeCells" :key="morphemeCell.morpheme">
<MorphemeWithPronunciation
:pronoun="selectedPronoun"
:morpheme="morpheme"
:morpheme="morphemeCell.morpheme"
:highlights-morphemes="morphemeCell.highlightsMorphemes"
:counter="counter"
/>
</td>
@ -121,7 +122,22 @@ interface Header {
interface SectionDefinition {
header?: Header;
variants: PronounVariant[] | { base: string, type: string };
variants: PronounVariantDefinition[] | PronounVariantsFromDeclensionDefinition;
}
interface PronounVariantDefinition {
name?: string;
morphemeCells: (string | MorphemeCellDefinition)[];
}
interface PronounVariantsFromDeclensionDefinition {
base: string;
type: string;
}
interface MorphemeCellDefinition {
morpheme: string;
highlightsMorphemes: string[];
}
const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableDefinition[]> = {
@ -149,7 +165,21 @@ const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableD
{
variants: [
{
morphemes: ['pronoun_n', 'possessive_determiner_m_n', 'pronoun_d', 'pronoun_a'],
morphemeCells: [
'pronoun_n',
{
morpheme: 'possessive_determiner_m_n',
highlightsMorphemes: ['determiner', 'pronoun'].flatMap((possessiveKind) => {
return ['f', 'm', 'n', 'x', 'pl'].flatMap((genus) => {
return cases.flatMap((casus) => {
return `possessive_${possessiveKind}_${genus}_${casus}`;
});
});
}),
},
'pronoun_d',
'pronoun_a',
],
},
],
},
@ -245,11 +275,11 @@ const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableD
variants: [
{
name: 'gleicher Art',
morphemes: ['pronoun_equal'],
morphemeCells: ['pronoun_equal'],
},
{
name: 'zugehörig',
morphemes: ['possessive_pronoun_substantivized'],
morphemeCells: ['possessive_pronoun_substantivized'],
},
],
},
@ -261,15 +291,15 @@ const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableD
variants: [
{
name: 'wegen',
morphemes: ['adverb_because'],
morphemeCells: ['adverb_because'],
},
{
name: 'damals',
morphemes: ['adverb_back_then'],
morphemeCells: ['adverb_back_then'],
},
{
name: 'von',
morphemes: ['adverb_by'],
morphemeCells: ['adverb_by'],
},
],
},
@ -280,7 +310,7 @@ const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableD
},
variants: [
{
morphemes: ['adjective_back_then'],
morphemeCells: ['adjective_back_then'],
},
],
},
@ -289,8 +319,8 @@ const grammarTablesDefinitions: Record<'simple' | 'comprehensive', GrammarTableD
],
};
const morphemesByCase = (morphemeBase: string): string[] => {
return cases.map((caseAbbreviation) => `${morphemeBase}_${caseAbbreviation}`);
const morphemesByCase = (morphemeBase: string): MorphemeCell[] => {
return cases.map((caseAbbreviation) => ({ morpheme: `${morphemeBase}_${caseAbbreviation}` }));
};
interface GrammarTable {
@ -308,23 +338,36 @@ interface PronounVariant {
name?: string;
numerus?: 'singular' | 'plural';
icon?: string;
morphemes: string[];
morphemeCells: MorphemeCell[];
}
interface MorphemeCell {
morpheme: string;
highlightsMorphemes?: Set<string>;
}
const expandVariantsForSection = (sectionVariants: SectionDefinition['variants']): PronounVariant[] => {
if (Array.isArray(sectionVariants)) {
return sectionVariants;
return sectionVariants.map((sectionVariant) => {
const morphemeCells = sectionVariant.morphemeCells.map((morphemeCell) => {
if (typeof morphemeCell === 'string') {
return { morpheme: morphemeCell };
}
return { ...morphemeCell, highlightsMorphemes: new Set(morphemeCell.highlightsMorphemes) };
});
return { ...sectionVariant, morphemeCells };
});
} else {
const morphemeBase = sectionVariants.base;
switch (sectionVariants.type) {
case 'case':
return [{ morphemes: morphemesByCase(morphemeBase) }];
return [{ morphemeCells: morphemesByCase(morphemeBase) }];
case 'declension-with-case':
return declensions.map((declension) => ({
name: declension.name,
numerus: declension.numerus,
icon: declension.icon,
morphemes: morphemesByCase(`${morphemeBase}_${declension.abbreviation}`),
morphemeCells: morphemesByCase(`${morphemeBase}_${declension.abbreviation}`),
}));
default:
throw new Error(`variant type ${sectionVariants.type} is unknown`);
@ -338,7 +381,9 @@ const buildVariantsForSection = (
counter: number,
): PronounVariant[] => {
return expandVariantsForSection(sectionVariants).filter((variant) => {
return variant.morphemes.some((morpheme) => pronoun.getMorpheme(morpheme, counter) !== null);
return variant.morphemeCells.some((morphemeCell) => {
return pronoun.getMorpheme(morphemeCell.morpheme, counter) !== null;
});
});
};

View File

@ -1,17 +1,63 @@
singular plural isHonorific comprehensive
{'pronoun_n} ist so süß. {'pronoun_n} sind so süß. FALSE FALSE
Ist das {possessive_determiner_m_n} Hund? FALSE FALSE
Heute hat {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. Heute haben {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. FALSE TRUE
{'possessive_determiner_n_n} Kaninchen spielt mit {possessive_determiner_m_d} Hund. FALSE TRUE
Meine Lieblingsfarbe ist violett, {possessive_pronoun_f_n} ist gelb. FALSE TRUE
Dieses Haus ist recht alt, während {possessive_pronoun_n_n} gerade renoviert wurde. FALSE TRUE
Wir freuen uns {pronoun_g}. FALSE TRUE
Ich bin {pronoun_d} erst kürzlich begegnet. FALSE FALSE
Ich verstehe {pronoun_a} so gut. FALSE FALSE
{'relative_n} Einzige, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. {'relative_n} Einzigen, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. FALSE TRUE
{'demonstrative_n} Studierende kennt alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. {'demonstrative_n} Studierende kennen alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. FALSE TRUE
Mit {pronoun_equal} komme ich gut zurecht. FALSE TRUE
Mein Fahrrad ist schneller als das {possessive_pronoun_substantivized}. FALSE TRUE
{'pronoun_n} möchte nicht, dass wir nur {adverb_because} einen Umweg fahren. {'pronoun_n} möchten nicht, dass wir nur {adverb_because} einen Umweg fahren. FALSE TRUE
{'adverb_back_then} wurden {possessive_determiner_pl_n} Ideen kontrovers diskutiert. FALSE TRUE
Mit {possessive_determiner_f_d} großer Geduld reagiert {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. Mit {possessive_determiner_f_d} großer Geduld reagieren {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. FALSE TRUE
singular plural
{'pronoun_n} ist so süß. {'pronoun_n} sind so süß.
Ist das {possessive_determiner_m_n} Hund?
Ich bin {pronoun_d} erst kürzlich begegnet.
Ich verstehe {pronoun_a} so gut.
Darum kümmert sich {pronoun_n}. Darum kümmern sich {pronoun_n}.
{'pronoun_n} hat das Telefon abgehoben. {'pronoun_n} haben das Telefon abgehoben.
{'pronoun_n} spricht während {pronoun_n} schläft. {'pronoun_n} sprechen während {pronoun_n} schläft.
Wir nehmen uns {pronoun_g} an.
Wir erfreuen uns {pronoun_g}.
Hast du Lust, mit {pronoun_d} ins Kino zu gehen?
Ich habe gestern mit {pronoun_d} gesprochen.
Ich bin mit {pronoun_d} zur Schule gegangen.
Ich habe {pronoun_a} gestern getroffen.
Wir haben das letzte Mal über {pronoun_a} gesprochen.
{'pronoun_a} kenne ich schon lange.
Kannst du {pronoun_d} Bescheid geben, wenn {possessive_determiner_f_n} Katze aufwacht?
{'possessive_determiner_f_n} Abschlussfeier beginnt bald.
{'pronoun_n} kümmert sich sehr gut um {possessive_determiner_f_a} Katze. {'pronoun_n} kümmern sich sehr gut um {possessive_determiner_f_a} Katze.
Ich habe {pronoun_a} gefragt, ob ich {possessive_determiner_m_a} Bleistift ausleihen kann.
Heute hat {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. Heute haben {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei.
{'possessive_determiner_n_n} Kaninchen spielt mit {possessive_determiner_m_d} Hund.
Das ist das Ladekabel {possessive_determiner_m_g} Computers.
Der Stall {possessive_determiner_n_g} Pferdes wird von {pronoun_d} wöchentlich besucht.
Mit {possessive_determiner_n_d} Fahrrad kommt {pronoun_n} sicher ans Ziel. Mit {possessive_determiner_n_d} Fahrrad kommen {pronoun_n} sicher ans Ziel.
Ich vermisse {possessive_determiner_n_a} Lachen.
{'possessive_determiner_x_n} Freund*in besucht {pronoun_a} morgen.
Kennst du die Telefonnummer {possessive_determiner_x_g} Ärzt*in?
{'pronoun_n} bringt {possessive_determiner_x_d} Partner*in ein Geschenk.
Hast du {possessive_determiner_x_a} Freund*in gesehen?
{'possessive_determiner_pl_n} Mitarbeitenden sind hilfsbereit.
Den Umfang {possessive_determiner_pl_g} Aufgaben kann {pronoun_n} leicht bewältigen.
Bringe bitte {possessive_determiner_pl_d} Haustiere frisches Futter mit.
Wir schätzen {possessive_determiner_pl_a} Ideen.
Meine Lieblingsfarbe ist violett, {possessive_pronoun_f_n} ist gelb.
Ich hätte gerne eine Schildkröte, die so aussieht wie {possessive_pronoun_f_n}.
Es bedarf meiner Hilfe, nicht {possessive_pronoun_f_g}.
Dieses Jahr sind wir in meiner Stadt, nächstes in {possessive_pronoun_f_d}.
Ich mag meine Arbeit, während {possessive_pronoun_f_a} mich überfordern würde.
{'possessive_determiner_m_a} Geburtstag kann {pronoun_n} kaum erwarten, {possessive_pronoun_m_n} ist aber erst in drei Monaten. {possessive_determiner_m_a} Geburtstag können {pronoun_n} kaum erwarten, {possessive_pronoun_m_n} ist aber erst in drei Monaten.
Mein Kuchen ist sehr lecker, schmeckt {pronoun_d} auch {possessive_pronoun_m_n}?
Wir sind uns unseres Fehlers bewusst, {pronoun_n} sich auch {possessive_pronoun_m_g}?
Ich spiele gerne mit meinem Ball, aber gelegentlich auch mit {possessive_pronoun_m_d}.
Bei der Suche nach interessanten Artikeln bin ich auf {possessive_pronoun_m_a} gestoßen.
{'pronoun_n} hat mir gesagt, dieses Haus ist {possessive_pronoun_n_n}.
Versprechen sind einzuhalten, und ich erinnere mich {possessive_pronoun_n_g}.
Du trinkst von deinem Glas und {pronoun_n} von {possessive_pronoun_n_d}.
Du kaufst dein neues Outfit und {pronoun_n} {possessive_pronoun_n_a}.
Meine Eltern unterstützen {pronoun_a}. Und {possessive_pronoun_pl_n}?
Wir brauchen noch Geschirr für das Picknick, also bedienen wir uns {possessive_pronoun_pl_g}.
Es ist wichtig Kontakt mit Bekannten zu halten, also schreibt {pronoun_n} häufig {possessive_pronoun_pl_d}. Es ist wichtig Kontakt mit Bekannten zu halten, also schreiben {pronoun_n} häufig {possessive_pronoun_pl_d}.
Heute sind die Bücher angekommen, bitte gebe {pronoun_d} {possessive_pronoun_pl_a}.
{'relative_n} Einzige, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. {'relative_n} Einzigen, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden.
{'demonstrative_n} Studierende kennt alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. {'demonstrative_n} Studierende kennen alle Lösungen {possessive_determiner_f_g} Übungsaufgabe.
{demonstrative_n} drüben ist hübsch. {demonstrative_n} drüben sind hübsch.
Gib das einfach {demonstrative_d} da.
Mit {pronoun_equal} komme ich gut zurecht.
Mein Fahrrad ist schneller als das {possessive_pronoun_substantivized}.
{'pronoun_n} möchte nicht, dass wir nur {adverb_because} einen Umweg fahren. {'pronoun_n} möchten nicht, dass wir nur {adverb_because} einen Umweg fahren.
{'adverb_because} sollen wir nicht auf {pronoun_a} warten.
{'adverb_back_then} wurden {possessive_determiner_pl_n} Ideen kontrovers diskutiert.
Mit {possessive_determiner_f_d} großer Geduld reagiert {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. Mit {possessive_determiner_f_d} großer Geduld reagieren {pronoun_n} {adverb_by} gelassen auf die unruhige Situation.
Das {adjective_back_then}e Motto lautete: „Wir schaffen das!“

1 singular plural isHonorific comprehensive
2 {'pronoun_n} ist so süß. {'pronoun_n} sind so süß. FALSE FALSE
3 Ist das {possessive_determiner_m_n} Hund? FALSE FALSE
4 Heute hat {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. Ich bin {pronoun_d} erst kürzlich begegnet. Heute haben {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. FALSE TRUE
5 {'possessive_determiner_n_n} Kaninchen spielt mit {possessive_determiner_m_d} Hund. Ich verstehe {pronoun_a} so gut. FALSE TRUE
6 Meine Lieblingsfarbe ist violett, {possessive_pronoun_f_n} ist gelb. Darum kümmert sich {pronoun_n}. Darum kümmern sich {pronoun_n}. FALSE TRUE
7 Dieses Haus ist recht alt, während {possessive_pronoun_n_n} gerade renoviert wurde. {'pronoun_n} hat das Telefon abgehoben. {'pronoun_n} haben das Telefon abgehoben. FALSE TRUE
8 Wir freuen uns {pronoun_g}. {'pronoun_n} spricht während {pronoun_n} schläft. {'pronoun_n} sprechen während {pronoun_n} schläft. FALSE TRUE
9 Ich bin {pronoun_d} erst kürzlich begegnet. Wir nehmen uns {pronoun_g} an. FALSE FALSE
10 Ich verstehe {pronoun_a} so gut. Wir erfreuen uns {pronoun_g}. FALSE FALSE
11 {'relative_n} Einzige, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. Hast du Lust, mit {pronoun_d} ins Kino zu gehen? {'relative_n} Einzigen, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. FALSE TRUE
12 {'demonstrative_n} Studierende kennt alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. Ich habe gestern mit {pronoun_d} gesprochen. {'demonstrative_n} Studierende kennen alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. FALSE TRUE
13 Mit {pronoun_equal} komme ich gut zurecht. Ich bin mit {pronoun_d} zur Schule gegangen. FALSE TRUE
14 Mein Fahrrad ist schneller als das {possessive_pronoun_substantivized}. Ich habe {pronoun_a} gestern getroffen. FALSE TRUE
15 {'pronoun_n} möchte nicht, dass wir nur {adverb_because} einen Umweg fahren. Wir haben das letzte Mal über {pronoun_a} gesprochen. {'pronoun_n} möchten nicht, dass wir nur {adverb_because} einen Umweg fahren. FALSE TRUE
16 {'adverb_back_then} wurden {possessive_determiner_pl_n} Ideen kontrovers diskutiert. {'pronoun_a} kenne ich schon lange. FALSE TRUE
17 Mit {possessive_determiner_f_d} großer Geduld reagiert {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. Kannst du {pronoun_d} Bescheid geben, wenn {possessive_determiner_f_n} Katze aufwacht? Mit {possessive_determiner_f_d} großer Geduld reagieren {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. FALSE TRUE
18 {'possessive_determiner_f_n} Abschlussfeier beginnt bald.
19 {'pronoun_n} kümmert sich sehr gut um {possessive_determiner_f_a} Katze. {'pronoun_n} kümmern sich sehr gut um {possessive_determiner_f_a} Katze.
20 Ich habe {pronoun_a} gefragt, ob ich {possessive_determiner_m_a} Bleistift ausleihen kann.
21 Heute hat {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei. Heute haben {pronoun_n} {possessive_determiner_m_a} Apfel, {possessive_determiner_f_a} Birne und {possessive_determiner_n_a} Törtchen dabei.
22 {'possessive_determiner_n_n} Kaninchen spielt mit {possessive_determiner_m_d} Hund.
23 Das ist das Ladekabel {possessive_determiner_m_g} Computers.
24 Der Stall {possessive_determiner_n_g} Pferdes wird von {pronoun_d} wöchentlich besucht.
25 Mit {possessive_determiner_n_d} Fahrrad kommt {pronoun_n} sicher ans Ziel. Mit {possessive_determiner_n_d} Fahrrad kommen {pronoun_n} sicher ans Ziel.
26 Ich vermisse {possessive_determiner_n_a} Lachen.
27 {'possessive_determiner_x_n} Freund*in besucht {pronoun_a} morgen.
28 Kennst du die Telefonnummer {possessive_determiner_x_g} Ärzt*in?
29 {'pronoun_n} bringt {possessive_determiner_x_d} Partner*in ein Geschenk.
30 Hast du {possessive_determiner_x_a} Freund*in gesehen?
31 {'possessive_determiner_pl_n} Mitarbeitenden sind hilfsbereit.
32 Den Umfang {possessive_determiner_pl_g} Aufgaben kann {pronoun_n} leicht bewältigen.
33 Bringe bitte {possessive_determiner_pl_d} Haustiere frisches Futter mit.
34 Wir schätzen {possessive_determiner_pl_a} Ideen.
35 Meine Lieblingsfarbe ist violett, {possessive_pronoun_f_n} ist gelb.
36 Ich hätte gerne eine Schildkröte, die so aussieht wie {possessive_pronoun_f_n}.
37 Es bedarf meiner Hilfe, nicht {possessive_pronoun_f_g}.
38 Dieses Jahr sind wir in meiner Stadt, nächstes in {possessive_pronoun_f_d}.
39 Ich mag meine Arbeit, während {possessive_pronoun_f_a} mich überfordern würde.
40 {'possessive_determiner_m_a} Geburtstag kann {pronoun_n} kaum erwarten, {possessive_pronoun_m_n} ist aber erst in drei Monaten. {‘possessive_determiner_m_a} Geburtstag können {pronoun_n} kaum erwarten, {possessive_pronoun_m_n} ist aber erst in drei Monaten.
41 Mein Kuchen ist sehr lecker, schmeckt {pronoun_d} auch {possessive_pronoun_m_n}?
42 Wir sind uns unseres Fehlers bewusst, {pronoun_n} sich auch {possessive_pronoun_m_g}?
43 Ich spiele gerne mit meinem Ball, aber gelegentlich auch mit {possessive_pronoun_m_d}.
44 Bei der Suche nach interessanten Artikeln bin ich auf {possessive_pronoun_m_a} gestoßen.
45 {'pronoun_n} hat mir gesagt, dieses Haus ist {possessive_pronoun_n_n}.
46 Versprechen sind einzuhalten, und ich erinnere mich {possessive_pronoun_n_g}.
47 Du trinkst von deinem Glas und {pronoun_n} von {possessive_pronoun_n_d}.
48 Du kaufst dein neues Outfit und {pronoun_n} {possessive_pronoun_n_a}.
49 Meine Eltern unterstützen {pronoun_a}. Und {possessive_pronoun_pl_n}?
50 Wir brauchen noch Geschirr für das Picknick, also bedienen wir uns {possessive_pronoun_pl_g}.
51 Es ist wichtig Kontakt mit Bekannten zu halten, also schreibt {pronoun_n} häufig {possessive_pronoun_pl_d}. Es ist wichtig Kontakt mit Bekannten zu halten, also schreiben {pronoun_n} häufig {possessive_pronoun_pl_d}.
52 Heute sind die Bücher angekommen, bitte gebe {pronoun_d} {possessive_pronoun_pl_a}.
53 {'relative_n} Einzige, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden. {'relative_n} Einzigen, {relative_a} ich kenne, {relative_d} wir noch einen Gefallen schulden.
54 {'demonstrative_n} Studierende kennt alle Lösungen {possessive_determiner_f_g} Übungsaufgabe. {'demonstrative_n} Studierende kennen alle Lösungen {possessive_determiner_f_g} Übungsaufgabe.
55 {‘demonstrative_n} drüben ist hübsch. {‘demonstrative_n} drüben sind hübsch.
56 Gib das einfach {demonstrative_d} da.
57 Mit {pronoun_equal} komme ich gut zurecht.
58 Mein Fahrrad ist schneller als das {possessive_pronoun_substantivized}.
59 {'pronoun_n} möchte nicht, dass wir nur {adverb_because} einen Umweg fahren. {'pronoun_n} möchten nicht, dass wir nur {adverb_because} einen Umweg fahren.
60 {'adverb_because} sollen wir nicht auf {pronoun_a} warten.
61 {'adverb_back_then} wurden {possessive_determiner_pl_n} Ideen kontrovers diskutiert.
62 Mit {possessive_determiner_f_d} großer Geduld reagiert {pronoun_n} {adverb_by} gelassen auf die unruhige Situation. Mit {possessive_determiner_f_d} großer Geduld reagieren {pronoun_n} {adverb_by} gelassen auf die unruhige Situation.
63 Das {adjective_back_then}e Motto lautete: „Wir schaffen das!“

View File

@ -61,7 +61,10 @@ home:
pronouns:
header: 'Pronomen'
headerLong: 'Pronomen'
examples: 'Beispielsätze'
examples:
header: 'Beispielsätze'
shuffle: 'Anderen Beispielsatz anzeigen'
shuffleNamed: 'Anderen Beispielsatz für %category% anzeigen'
plural: 'Plural'
intro: 'Meine Pronomen sind'
normative: 'Normativ'
@ -94,6 +97,9 @@ pronouns:
or: 'oder'
grammarTable: 'Tabelle'
pronunciation:
play: 'Hörbeispiel abspielen'
sources:
header: 'Quellen'
headerLong: 'Beispiele aus Kulturtexten'

View File

@ -11,6 +11,22 @@ pronouns:
any: 'any'
plurals: true
honorifics: false
exampleCategories:
-
name: 'subject pronoun'
morphemes: ['pronoun_subject']
-
name: 'object pronoun'
morphemes: ['pronoun_object']
-
name: 'possessive determiner'
morphemes: ['possessive_determiner']
-
name: 'possessive pronoun'
morphemes: ['possessive_pronoun']
-
name: 'reflexive pronoun'
morphemes: ['reflexive']
multiple:
name: 'Interchangeable forms'
description: >

View File

@ -1,5 +1,20 @@
singular plural isHonorific
I think {pronoun_subject} is very nice. I think {pronoun_subject} are very nice. FALSE
I asked {pronoun_object} if I can borrow {possessive_determiner} pencil. FALSE
{'pronoun_subject} told me that the house is {possessive_pronoun}. FALSE
{'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}. FALSE
singular plural
I think {pronoun_subject} is very nice. I think {pronoun_subject} are very nice.
I met {pronoun_object} recently.
Is this {possessive_determiner} dog?
{'pronoun_subject} told me that the house is {possessive_pronoun}.
{'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}.
{'pronoun_subject} is really beautiful. {'pronoun_subject} are really beautiful.
{'pronoun_subject} answered the phone.
{'pronoun_subject} takes good care of {possessive_determiner} cat. {'pronoun_subject} take good care of {possessive_determiner} cat.
{'pronoun_subject} did it all by {reflexive}.
{'pronoun_subject} talks in {possessive_determiner} sleep. {'pronoun_subject} talk in {possessive_determiner} sleep.
{'pronoun_subject} landed the plane safely.
{'pronoun_subject} argues that… {'pronoun_subject} argue that…
Did you buy {pronoun_object} {possessive_determiner} gift?
I asked {pronoun_object} if I can borrow {possessive_determiner} pencil.
I talked to {pronoun_object} yesterday.
Would you like to go to the movies with {pronoun_object}?
Can you call {pronoun_object} when {possessive_determiner} cat awakes?
{'possessive_determiner} graduation starts soon.
My favorite color is purple, {possessive_pronoun} is yellow.

1 singular plural isHonorific
2 I think {pronoun_subject} is very nice. I think {pronoun_subject} are very nice. FALSE
3 I asked {pronoun_object} if I can borrow {possessive_determiner} pencil. I met {pronoun_object} recently. FALSE
4 {'pronoun_subject} told me that the house is {possessive_pronoun}. Is this {possessive_determiner} dog? FALSE
5 {'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}. {'pronoun_subject} told me that the house is {possessive_pronoun}. FALSE
6 {'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}.
7 {'pronoun_subject} is really beautiful. {'pronoun_subject} are really beautiful.
8 {'pronoun_subject} answered the phone.
9 {'pronoun_subject} takes good care of {possessive_determiner} cat. {'pronoun_subject} take good care of {possessive_determiner} cat.
10 {'pronoun_subject} did it all by {reflexive}.
11 {'pronoun_subject} talks in {possessive_determiner} sleep. {'pronoun_subject} talk in {possessive_determiner} sleep.
12 {'pronoun_subject} landed the plane safely.
13 {'pronoun_subject} argues that… {'pronoun_subject} argue that…
14 Did you buy {pronoun_object} {possessive_determiner} gift?
15 I asked {pronoun_object} if I can borrow {possessive_determiner} pencil.
16 I talked to {pronoun_object} yesterday.
17 Would you like to go to the movies with {pronoun_object}?
18 Can you call {pronoun_object} when {possessive_determiner} cat awakes?
19 {'possessive_determiner} graduation starts soon.
20 My favorite color is purple, {possessive_pronoun} is yellow.

View File

@ -63,7 +63,10 @@ home:
pronouns:
header: 'Pronouns'
headerLong: 'Pronouns'
examples: 'Example usage in sentences'
examples:
header: 'Example usage in sentences'
shuffle: 'Show different example'
shuffleNamed: 'Show different example for %category%'
plural: 'Plural'
intro: 'My pronouns are'
normative: 'Normative'
@ -89,6 +92,9 @@ pronouns:
sentence: >
You can modify the default links so that the URL reads like a sentence; for example:
pronunciation:
play: 'Play audio sample'
sources:
header: 'Sources'
headerLong: 'Examples from cultural texts'

View File

@ -50,7 +50,8 @@ home:
pronouns:
header: 'Pronomoj'
headerLong: 'Pronomoj'
examples: 'Ekzempla uzado en frazoj'
examples:
header: 'Ekzempla uzado en frazoj'
plural: 'Pluralo'
intro: 'Miaj pronomoj estas'
normative: 'Norma'

View File

@ -61,7 +61,8 @@ home:
pronouns:
header: 'Pronombres'
headerLong: 'Pronombres'
examples: 'Ejemplos de uso en oraciones'
examples:
header: 'Ejemplos de uso en oraciones'
plural: 'Plural'
intro: 'Mis pronombres son'
normative: 'Normativo'

View File

@ -56,7 +56,8 @@ home:
pronouns:
header: 'Asesõnad'
headerLong: 'Asesõnad'
examples: 'Kasutuse näide lausetes'
examples:
header: 'Kasutuse näide lausetes'
plural: 'Mitmus'
intro: 'Minu asesõnad on'
normative: 'Normatiivne'

View File

@ -58,7 +58,8 @@ home:
pronouns:
header: 'Pronoms'
headerLong: 'Pronoms'
examples: 'Exemple dusage dans des phrases'
examples:
header: 'Exemple dusage dans des phrases'
plural: 'Pluriel'
intro: 'Mes pronoms sont'
normative: 'Normatif'

View File

@ -60,7 +60,8 @@ home:
pronouns:
header: 'Pronomes'
headerLong: 'Pronomes'
examples: 'Exemplos de uso em frases'
examples:
header: 'Exemplos de uso em frases'
plural: 'Plural'
intro: 'Meus pronomes são'
normative: 'Normativo'

View File

@ -62,7 +62,8 @@ home:
pronouns:
header: 'Pronouns'
headerLong: 'Pronouns'
examples: 'Example usage in sentences'
examples:
header: 'Example usage in sentences'
plural: 'Plural'
intro: 'My pronouns are'
normative: 'Normative'

View File

@ -60,7 +60,8 @@ home:
pronouns:
header: 'Pronomi'
headerLong: 'Pronomi'
examples: 'Esempi pratici di utilizzo'
examples:
header: 'Esempi pratici di utilizzo'
plural: 'Plurale'
intro: 'I miei pronomi sono'
normative: 'Normative'

View File

@ -44,7 +44,8 @@ home:
pronouns:
header: '三人称代名詞'
headerLong: '三人称代名詞'
examples: '文章での使い方の例'
examples:
header: '文章での使い方の例'
plural: '複数形'
intro: '私の三人称代名詞は'
normative: '規範的'

View File

@ -61,7 +61,8 @@ home:
pronouns:
header: '대명사'
headerLong: '대명사'
examples: '문장안에 예'
examples:
header: '문장안에 예'
plural: '복유의'
intro: '나의 대명사는'
normative: '규범적'

View File

@ -61,7 +61,8 @@ home:
pronouns:
header: 'Pronombres'
headerLong: 'Pronombres'
examples: 'Enshemplos de uzo en frazas'
examples:
header: 'Enshemplos de uzo en frazas'
plural: 'Plural'
intro: 'Mis pronombres son'
normative: 'Normativo'

View File

@ -59,7 +59,8 @@ home:
pronouns:
header: 'Voornaamwoorden'
headerLong: 'Voornaamwoorden'
examples: 'Gebruiksvoorbeelden in zinnen'
examples:
header: 'Gebruiksvoorbeelden in zinnen'
plural: 'Meervoud'
intro: 'Mijn voornaamwoorden zijn'
normative: 'Normatief'

View File

@ -53,7 +53,8 @@ home:
pronouns:
header: 'Pronomen'
headerLong: 'Pronomen'
examples: 'Eksempler i setninger'
examples:
header: 'Eksempler i setninger'
plural: 'Flertall'
intro: 'Mine pronomen er'
normative: 'Normativ'

View File

@ -74,7 +74,8 @@ home:
pronouns:
header: 'Zaimki'
headerLong: 'Niebinarne zaimki<br/> i inne formy'
examples: 'Przykłady użycia w zdaniu'
examples:
header: 'Przykłady użycia w zdaniu'
plural: 'Liczba mnoga'
intro: 'Moje zaimki to'
normative: 'Normatywne'
@ -282,7 +283,8 @@ nouns:
Słownik Neutratywów podaje odmianę pasującą do {/ono=„ono/jego”},
lecz możliwa jest też odmiana zgodna z pozostałymi opcjami:
examples: 'Przykłady'
examples:
header: 'Przykłady'
dictionary: 'Słownik Neutratywów'
approved: 'wpisów zatwierdzonych'

View File

@ -65,7 +65,8 @@ home:
pronouns:
header: 'Pronomes'
headerLong: 'Pronomes'
examples: 'Exemplos de uso em frases'
examples:
header: 'Exemplos de uso em frases'
plural: 'Plural'
intro: 'Meu conjunto é'
normative: 'Normativo'

View File

@ -61,7 +61,8 @@ home:
pronouns:
header: 'Pronume'
headerLong: 'Listă cu pronume'
examples: 'Utilizarea pronumelor în propoziții'
examples:
header: 'Utilizarea pronumelor în propoziții'
plural: 'Plural'
intro: 'Pronumele mele sunt'
normative: 'Normativ'

View File

@ -54,7 +54,8 @@ home:
pronouns:
header: 'Местоимения'
headerLong: 'Местоимения и другие формы'
examples: 'Примеры употребления в предложениях'
examples:
header: 'Примеры употребления в предложениях'
plural: 'Множественное число'
intro: 'Мои местоимения'
normative: 'Нормативное'

View File

@ -63,7 +63,8 @@ home:
pronouns:
header: 'Pronomen'
headerLong: 'Pronomen'
examples: 'Exempel på användningar i meningar'
examples:
header: 'Exempel på användningar i meningar'
plural: 'Plural'
intro: 'Mina pronomen är'
normative: 'Normativ'

View File

@ -1,5 +1 @@
singular plural isHonorific
I think {pronoun_subject} is very nice. I think {pronoun_subject} are very nice. FALSE
I asked {pronoun_object} if I can borrow {possessive_determiner} pencil. FALSE
{'pronoun_subject} told me that the house is {possessive_pronoun}. FALSE
{'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}. FALSE

1 singular plural isHonorific
I think {pronoun_subject} is very nice. I think {pronoun_subject} are very nice. FALSE
I asked {pronoun_object} if I can borrow {possessive_determiner} pencil. FALSE
{'pronoun_subject} told me that the house is {possessive_pronoun}. FALSE
{'pronoun_subject} said {pronoun_subject} would rather do it {reflexive}. FALSE

View File

@ -50,7 +50,8 @@ home:
pronouns:
header: 'Zamirler'
headerLong: 'Zamirler'
examples: 'Cumlede nasil gözükeceğini anlamak icin örnek kullanım'
examples:
header: 'Cumlede nasil gözükeceğini anlamak icin örnek kullanım'
plural: 'Birden Çok'
intro: 'Benim zamirlerim'
normative: 'Genel Zamirler'

View File

@ -54,7 +54,8 @@ home:
pronouns:
header: 'Займенники'
headerLong: 'Займенники та інші форми'
examples: 'Приклади використання у реченнях'
examples:
header: 'Приклади використання у реченнях'
plural: 'Множина'
intro: 'Мої займенники'
normative: 'Нормативне'

View File

@ -63,7 +63,8 @@ home:
pronouns:
header: 'Các danh xưng'
headerLong: 'Các danh xưng'
examples: 'Ví dụ trong câu'
examples:
header: 'Ví dụ trong câu'
plural: 'Số nhiều'
intro: 'Tôi dùng các danh xưng'
normative: 'Chuẩn mực'

View File

@ -60,7 +60,8 @@ home:
pronouns:
header: 'פּראָנאָמען'
headerLong: 'פּראָנאָמען'
examples: 'Example usage in sentences'
examples:
header: 'Example usage in sentences'
plural: 'מערצאָל'
intro: 'מײַנע פּראָנאָמען זענען'
normative: 'נאָרמאַטיװ'

View File

@ -62,7 +62,8 @@ home:
pronouns:
header: '代詞'
headerLong: '代詞'
examples: '例句用法'
examples:
header: '例句用法'
plural: '複數'
intro: '我的代詞是'
normative: '規範'

View File

@ -20,10 +20,11 @@
</Page>
</template>
<script>
<script lang="ts">
import Vue from 'vue';
import { head } from '../src/helpers.ts';
export default {
export default Vue.extend({
async asyncData({ app }) {
return {
chart: await app.$axios.$get(`/admin/stats/users-chart/${process.env.LOCALE}`),
@ -34,5 +35,5 @@ export default {
title: `${this.$t('admin.header')} • Profiles`,
});
},
};
});
</script>

View File

@ -7,7 +7,7 @@
<Icon v="tag" />
<T>pronouns.intro</T><T>quotation.colon</T>
</span>
<ComprehensiveSwitch @update:comprehensive="updated => comprehensive = updated" />
<ComprehensiveSwitch v-model="comprehensive" />
</h2>
<section>
@ -29,14 +29,17 @@
<section>
<h2 class="h4">
<Icon v="file-signature" />
<T>pronouns.examples</T><T>quotation.colon</T>
<T>pronouns.examples.header</T><T>quotation.colon</T>
</h2>
<ul>
<template v-for="example in examples">
<li v-if="!example.comprehensive || comprehensive" class="my-1">
<Example :example="example" :pronoun="randomPronounForExample(example)" link />
</li>
<template v-for="exampleCategory in exampleCategories">
<ExampleCategory
v-if="!exampleCategory.comprehensive || comprehensive"
:example-category="exampleCategory"
:pronouns-choice="pronounsChoice"
link
/>
</template>
</ul>
</section>
@ -67,6 +70,7 @@
<script>
import { examples, pronouns, pronounLibrary } from '../src/data.js';
import { head } from '../src/helpers.ts';
import { ExampleCategory } from '../src/classes.ts';
export default {
data() {
@ -82,11 +86,9 @@ export default {
}
return {
examples,
exampleCategories: ExampleCategory.from(examples, this.$config),
short,
pronounGroups,
comprehensive: false,
};
},
head() {
@ -96,23 +98,34 @@ export default {
});
},
computed: {
comprehensive: {
get() {
return Object.hasOwn(this.$route.query, this.$config.pronouns.comprehensive);
},
set(value) {
if (value === this.comprehensive) {
// prevent warning that $router.replace has no effect
return;
}
const query = structuredClone(this.$route.query);
if (value) {
query[this.$config.pronouns.comprehensive] = null;
} else {
delete query[this.$config.pronouns.comprehensive];
}
this.$router.replace({ query });
},
},
pronounsChoice() {
if (!this.pronounGroups.length) {
return pronouns;
return Object.values(pronouns);
}
let choice = {};
for (const pronounGroup of this.pronounGroups) {
choice = { ...choice, ...pronounGroup.groupPronouns };
}
return choice;
},
},
methods: {
randomPronounForExample(example) {
const suitablePronouns = Object.values(this.pronounsChoice)
.filter((pronoun) => example.requiredMorphemesPresent(pronoun));
return suitablePronouns[suitablePronouns.length * Math.random() << 0];
return Object.values(choice);
},
},
};

View File

@ -55,17 +55,18 @@
<section>
<h2 class="h4">
<Icon v="file-signature" />
<T>pronouns.examples</T><T>quotation.colon</T>
<T>pronouns.examples.header</T><T>quotation.colon</T>
</h2>
<ul>
<template v-for="example in examples">
<li
v-if="example.requiredMorphemesPresent(selectedPronoun, counter) && (!example.comprehensive || comprehensive)"
class="my-1"
>
<Example :example="example" :pronoun="selectedPronoun" :counter="counter" pronunciation />
</li>
<template v-for="exampleCategory in exampleCategories">
<ExampleCategory
v-if="!exampleCategory.comprehensive || comprehensive"
:example-category="exampleCategory"
:pronouns-choice="[selectedPronoun]"
:counter="counter"
pronunciation
/>
</template>
</ul>
</section>
@ -126,7 +127,7 @@ import { buildPronoun } from '../src/buildPronoun.ts';
import { head } from '../src/helpers.ts';
import GrammarTables from '../data/pronouns/GrammarTables.vue';
import LinkedText from '../components/LinkedText.vue';
import { SourceLibrary } from '../src/classes.ts';
import { ExampleCategory, SourceLibrary } from '../src/classes.ts';
export default {
components: { LinkedText, GrammarTables },
@ -143,7 +144,7 @@ export default {
: null;
return {
examples,
exampleCategories: ExampleCategory.from(examples, this.$config),
pronouns,
glue: ` ${this.$t('pronouns.or')} `,
@ -163,7 +164,7 @@ export default {
? head({
title: `${this.$t('pronouns.intro')}: ${this.selectedPronoun.name(this.glue)}`,
description: [
this.$t('pronouns.examples', {}, false),
this.$t('pronouns.examples.header', {}, false),
this.$t('pronouns.grammarTable', {}, false),
this.$t('sources.headerLong', {}, false),
].filter((x) => !!x).join(', '),

View File

@ -85,55 +85,51 @@
</p>
</div>
<p>
<T>pronouns.examples</T><T>quotation.colon</T>
<T>pronouns.examples.header</T><T>quotation.colon</T>
</p>
<template v-for="isHonorific in [false, true]">
<template v-if="examples.filter(e => e.isHonorific === isHonorific).length">
<ul>
<template v-for="example in examples">
<li v-if="example.isHonorific === isHonorific && !example.comprehensive">
<span v-for="part in clearExampleParts(example.parts(selectedPronoun))">
<input
v-if="part.variable"
v-model="selectedPronoun.morphemes[part.str]"
:class="['form-control form-input p-0', { active: selectedMorpheme === part.str }]"
:size="selectedPronoun.morphemes[part.str]?.length ?? 0"
maxlength="24"
@focus="selectedMorpheme = part.str"
@blur="selectedMorpheme = ''"
>
<span v-else><Spelling :text="part.str" /></span>
</span>
</li>
<template v-for="{ examples, isHonorific } in examplesByHonorific">
<ul>
<li v-for="example in examples">
<template v-for="part in clearExampleParts(example.parts(selectedPronoun))">
<input
v-if="part.variable"
v-model="selectedPronoun.morphemes[part.str]"
:class="['form-control form-input p-0', { active: selectedMorpheme === part.str }]"
:size="selectedPronoun.morphemes[part.str]?.length ?? 0"
maxlength="24"
@focus="selectedMorpheme = part.str"
@blur="selectedMorpheme = ''"
>
<span v-else><Spelling :text="part.str" /></span>
</template>
</ul>
<div v-if="$config.pronouns.plurals" class="my-3">
<div v-if="isHonorific" class="custom-control custom-switch">
<input
id="pluralHonorific"
v-model="selectedPronoun.pluralHonorific[0]"
type="checkbox"
class="custom-control-input"
>
<label class="custom-control-label" for="pluralHonorific">
<T>pronouns.plural</T>
<Icon v="level-up" />
</label>
</div>
<div v-else class="custom-control custom-switch">
<input
id="plural"
v-model="selectedPronoun.plural[0]"
type="checkbox"
class="custom-control-input"
>
<label class="custom-control-label" for="plural">
<T>pronouns.plural</T>
<Icon v="level-up" />
</label>
</div>
</li>
</ul>
<div v-if="$config.pronouns.plurals" class="my-3">
<div v-if="isHonorific" class="custom-control custom-switch">
<input
id="pluralHonorific"
v-model="selectedPronoun.pluralHonorific[0]"
type="checkbox"
class="custom-control-input"
>
<label class="custom-control-label" for="pluralHonorific">
<T>pronouns.plural</T>
<Icon v="level-up" />
</label>
</div>
</template>
<div v-else class="custom-control custom-switch">
<input
id="plural"
v-model="selectedPronoun.plural[0]"
type="checkbox"
class="custom-control-input"
>
<label class="custom-control-label" for="plural">
<T>pronouns.plural</T>
<Icon v="level-up" />
</label>
</div>
</div>
</template>
<p class="small">
<T icon="info-circle">home.generator.alt</T>
@ -271,15 +267,15 @@
<script lang="ts">
import Vue from 'vue';
import { examples, pronouns, pronounLibrary } from '../src/data.js';
import { ExamplePart, Pronoun } from '../src/classes.ts';
import { ExampleCategory, ExamplePart, Pronoun } from '../src/classes.ts';
import Compressor from '../src/compressor.js';
import MORPHEMES from '../data/pronouns/morphemes.js';
import { mapState } from 'vuex';
import Suggested from '../data/pronouns/Suggested.vue';
import type { Example, PronounLibrary } from '../src/classes.ts';
import type { PronounLibrary, Example } from '../src/classes.ts';
interface PronounsData {
examples: Example[];
examplesByHonorific: { examples: Example[], isHonorific: boolean }[];
pronouns: Record<string, Pronoun>;
pronounLibrary: PronounLibrary;
selectedPronoun: Pronoun;
@ -297,8 +293,18 @@ export default Vue.extend({
if (!this.$config.pronouns.enabled) {
throw null;
}
const exampleCategories = ExampleCategory.from(examples, this.$config);
const examplesByHonorific = [false, true].map((isHonorific) => {
const examples = exampleCategories
.filter((exampleCategory) => !exampleCategory.comprehensive)
.map((exampleCategory) => exampleCategory.examples[0])
.filter((example) => example.isHonorific === isHonorific);
return { examples, isHonorific };
}).filter(({ examples }) => examples.length > 0);
return {
examples,
examplesByHonorific,
pronouns,
pronounLibrary,

View File

@ -4,7 +4,7 @@ import sha1 from 'sha1';
import { ulid } from 'ulid';
import Papa from 'papaparse';
import { groupBy, handleErrorAsync } from '../../src/helpers.ts';
import { intersection, difference } from '../../src/sets.js';
import { intersection, difference } from '../../src/sets.ts';
import { buildChart } from '../../src/stats.ts';
import auditLog from '../audit.ts';

View File

@ -19,13 +19,11 @@ export class Example {
singularParts: ExamplePart[];
pluralParts: ExamplePart[];
isHonorific: boolean;
comprehensive: boolean;
constructor(singularParts: ExamplePart[], pluralParts: ExamplePart[], isHonorific = false, comprehensive = false) {
constructor(singularParts: ExamplePart[], pluralParts: ExamplePart[], isHonorific = false) {
this.singularParts = singularParts;
this.pluralParts = pluralParts;
this.isHonorific = isHonorific;
this.comprehensive = comprehensive;
}
static parse(str: string): ExamplePart[] {
@ -57,6 +55,12 @@ export class Example {
return this[plural ? 'pluralParts' : 'singularParts'];
}
hasMorpheme(morpheme: string): boolean {
return this.singularParts.filter((part) => part.variable).some((part) => {
return part.str.replace(/^'/, '') === morpheme;
});
}
requiredMorphemesPresent(pronoun: Pronoun, counter = 0): boolean {
return this.parts(pronoun, counter).filter((part) => part.variable)
.every((part) => pronoun.getMorpheme(part.str, counter) !== null);
@ -116,6 +120,30 @@ export class Example {
}
}
export class ExampleCategory {
name: string | undefined;
examples: Example[];
comprehensive: boolean;
constructor(name: string | undefined, examples: Example[], comprehensive: boolean = false) {
this.name = name;
this.examples = examples;
this.comprehensive = comprehensive;
}
static from(examples: Example[], config: Config): ExampleCategory[] {
if (!config.pronouns.exampleCategories) {
return examples.map((example) => new ExampleCategory(undefined, [example]));
}
return config.pronouns.exampleCategories.map((exampleCategory) => {
const matchingExamples = examples.filter((example) => {
return exampleCategory.morphemes.some((morpheme) => example.hasMorpheme(morpheme));
});
return new ExampleCategory(exampleCategory.name, matchingExamples, exampleCategory.comprehensive);
});
}
}
function clone<T extends object>(mainObject: T): T {
const objectCopy = {} as T;
for (const [key, value] of Object.entries(mainObject)) {

View File

@ -12,7 +12,6 @@ export const examples = buildList(function* () {
Example.parse(e.singular),
Example.parse(e.plural || e.singular),
e.isHonorific,
e.comprehensive || false,
);
}
});

View File

@ -82,6 +82,10 @@ export function listMissingTranslations(
return false;
}
if (!config.pronouns.exampleCategories && keyMatches('pronouns.examples.shuffleNamed')) {
return false;
}
if (!config.pronouns.slashes && keyMatches('pronouns.slashes.')) {
return false;
}

View File

@ -1,6 +1,6 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
export const isSuperset = (set, subset) => {
export const isSuperset = <T>(set: Set<T>, subset: Set<T>): boolean => {
for (const elem of subset) {
if (!set.has(elem)) {
return false;
@ -9,7 +9,7 @@ export const isSuperset = (set, subset) => {
return true;
};
export const union = (setA, setB) => {
export const union = <T>(setA: Set<T>, setB: Set<T>): Set<T> => {
const _union = new Set(setA);
for (const elem of setB) {
_union.add(elem);
@ -17,8 +17,8 @@ export const union = (setA, setB) => {
return _union;
};
export const intersection = (setA, setB) => {
const _intersection = new Set();
export const intersection = <T>(setA: Set<T>, setB: Set<T>): Set<T> => {
const _intersection: Set<T> = new Set();
for (const elem of setB) {
if (setA.has(elem)) {
_intersection.add(elem);
@ -27,7 +27,7 @@ export const intersection = (setA, setB) => {
return _intersection;
};
export const symmetricDifference = (setA, setB) => {
export const symmetricDifference = <T>(setA: Set<T>, setB: Set<T>): Set<T> => {
const _difference = new Set(setA);
for (const elem of setB) {
if (_difference.has(elem)) {
@ -39,7 +39,7 @@ export const symmetricDifference = (setA, setB) => {
return _difference;
};
export const difference = (setA, setB) => {
export const difference = <T>(setA: Set<T>, setB: Set<T>): Set<T> => {
const _difference = new Set(setA);
for (const elem of setB) {
_difference.delete(elem);

View File

@ -7,7 +7,7 @@ export interface RootState {
user: User | null;
preToken: string | null;
spelling: string | null;
highlightedMorpheme: string | null;
highlightedMorphemes: Set<string>;
darkMode: boolean;
translationModeVisible: boolean;
translationMode: boolean;
@ -22,7 +22,7 @@ export const state: () => RootState = () => ({
user: null,
preToken: null,
spelling: null,
highlightedMorpheme: null,
highlightedMorphemes: new Set(),
darkMode: false,
translationModeVisible: false,
translationMode: false,
@ -63,8 +63,8 @@ export const mutations: MutationTree<RootState> = {
setSpelling(state, spelling) {
state.spelling = spelling;
},
highlightMorpheme(state, morpheme) {
state.highlightedMorpheme = morpheme;
highlightMorphemes(state, morphemes: Set<string>) {
state.highlightedMorphemes = morphemes;
},
setDarkMode(state, isDark) {
state.darkMode = isDark;

View File

@ -21,21 +21,41 @@ const translator = new Translator(translations, translations, []);
const { default: pronouns, generated: generatedPronouns } = await import('./fixtures/pronouns.ts');
describe('required morphemes for an example', () => {
const exampleParts = [
new ExamplePart(false, 'This house is '),
new ExamplePart(true, 'possessive_pronoun'),
];
const example = new Example(exampleParts, exampleParts);
const inlineMorphemeExampleParts = [
new ExamplePart(false, 'This house is '),
new ExamplePart(true, 'possessive_pronoun'),
];
const inlineMorphemeExample = new Example(inlineMorphemeExampleParts, inlineMorphemeExampleParts);
const capitalizedMorphemeExampleParts = [
new ExamplePart(true, '\'pronoun_subject'),
new ExamplePart(false, ' is very nice.'),
];
const capitalizedMorphemeExample = new Example(capitalizedMorphemeExampleParts, capitalizedMorphemeExampleParts);
describe('morphemes of examples', () => {
test('are contained when present as variable', () => {
expect(inlineMorphemeExample.hasMorpheme('possessive_pronoun')).toBe(true);
});
test('are contained when present as capitalized variable', () => {
expect(capitalizedMorphemeExample.hasMorpheme('pronoun_subject')).toBe(true);
});
test('are not contained when missing as variable', () => {
expect(inlineMorphemeExample.hasMorpheme('pronoun_subject')).toBe(false);
});
});
describe('required morphemes for an example', () => {
test('are present if all morphemes are present', () => {
expect(example.requiredMorphemesPresent(pronouns.they)).toBe(true);
expect(inlineMorphemeExample.requiredMorphemesPresent(pronouns.they)).toBe(true);
});
test('are present even if one morpheme is empty', () => {
expect(example.requiredMorphemesPresent(generatedPronouns.aerWithEmptyPossessivePronoun)).toBe(true);
expect(inlineMorphemeExample.requiredMorphemesPresent(generatedPronouns.aerWithEmptyPossessivePronoun))
.toBe(true);
});
test('are missing if one morpheme is null', () => {
expect(example.requiredMorphemesPresent(generatedPronouns.aerWithUnsetPossessivePronoun)).toBe(false);
expect(inlineMorphemeExample.requiredMorphemesPresent(generatedPronouns.aerWithUnsetPossessivePronoun))
.toBe(false);
});
});

View File

@ -2,11 +2,39 @@ import { describe, expect, test } from '@jest/globals';
import allLocales from '../../locale/locales.ts';
import { loadTsv } from '../../src/tsv.js';
import { Example } from '../../src/classes.ts';
import type { ExpectationResult } from 'expect';
const __dirname = new URL('.', import.meta.url).pathname;
function toHaveValidMorphemes(actual: string, morphemes: string[]): ExpectationResult {
const containedMorphemes = Example.parse(actual).filter((part) => part.variable)
.map((part) => part.str.replace(/^'/, ''));
const unknownMorphemes = containedMorphemes.filter((morpheme) => !morphemes.includes(morpheme));
if (unknownMorphemes.length > 0) {
return {
message: () => `expected example '${actual}' to have valid morphemes,` +
` but these are unknown:\n${unknownMorphemes.join(', ')}`,
pass: false,
};
} else {
return {
message: () => 'expected example to have invalid morphemes',
pass: true,
};
}
}
declare module 'expect' {
interface Matchers<R> {
toHaveValidMorphemes(morphemes: string[]): R;
}
}
expect.extend({ toHaveValidMorphemes });
describe.each(allLocales)('data files of $code', ({ code }) => {
test('pronouns.tsv match schema', async () => {
test('pronouns/pronouns.tsv match schema', async () => {
const { default: MORPHEMES } = await import(`../../locale/${code}/pronouns/morphemes.js`);
const pronouns = loadTsv(`${__dirname}/../../locale/${code}/pronouns/pronouns.tsv`);
if (pronouns.length === 0) {
@ -26,4 +54,14 @@ describe.each(allLocales)('data files of $code', ({ code }) => {
expect(actual).toEqual(expect.arrayContaining(required));
expect([...required, ...optional]).toEqual(expect.arrayContaining(actual));
});
test('pronouns/examples.tsv contain valid morphemes', async () => {
const { default: MORPHEMES } = await import(`../../locale/${code}/pronouns/morphemes.js`);
const examples = loadTsv(`${__dirname}/../../locale/${code}/pronouns/examples.tsv`);
for (const example of examples) {
expect(example.singular).toHaveValidMorphemes(MORPHEMES);
if (example.plural) {
expect(example.plural).toHaveValidMorphemes(MORPHEMES);
}
}
});
});