mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-24 05:05:20 -04:00
Merge branch '138-pronoun-parsing-improvements' into 'main'
Improvements to slash pronoun parsing Closes #138 See merge request PronounsPage/PronounsPage!409
This commit is contained in:
commit
dac647d3e3
@ -194,8 +194,11 @@ import { groupBy } from '../src/helpers.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
links: groupBy([...getContactLinks(this.config), ...getSocialLinks(this.config)], (l) => l.group),
|
||||
supportLinks: [...getSupportLinks()],
|
||||
links: groupBy(
|
||||
[...getContactLinks(this.config, this.$translator), ...getSocialLinks(this.config)],
|
||||
(l) => l.group,
|
||||
),
|
||||
supportLinks: [...getSupportLinks(this.$translator)],
|
||||
versionFrontend: process.env.VERSION,
|
||||
versionBackend: undefined,
|
||||
adsVisible: false,
|
||||
|
@ -30,7 +30,7 @@ import { getSupportLinks } from '../src/contact.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
links: [...getSupportLinks()],
|
||||
links: [...getSupportLinks(this.$translator)],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@ -11,7 +11,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import translator from '../src/translator.js';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
@ -34,8 +33,8 @@ export default {
|
||||
},
|
||||
txt() {
|
||||
return this.modified
|
||||
? translator.applyParams(this.translationChanges[this.key], this.params || {})
|
||||
: translator.translate(this.key, this.params || {});
|
||||
? this.$translator.applyParams(this.translationChanges[this.key], this.params || {})
|
||||
: this.$translator.translate(this.key, this.params || {});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -47,12 +46,12 @@ export default {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const base = translator.get(this.key, false, true);
|
||||
const base = this.$translator.get(this.key, false, true);
|
||||
|
||||
const newValue = await this.$editor(
|
||||
this.modified
|
||||
? this.translationChanges[this.key]
|
||||
: translator.get(this.key),
|
||||
: this.$translator.get(this.key),
|
||||
{
|
||||
icon: 'language',
|
||||
header: this.key,
|
||||
@ -69,7 +68,7 @@ export default {
|
||||
);
|
||||
|
||||
if (newValue !== undefined) {
|
||||
this.$store.commit('translate', { key: this.key, newValue });
|
||||
this.$store.commit('translate', { translator: this.$translator, key: this.key, newValue });
|
||||
this.$cookies.set('translations', this.$store.state.translationChanges);
|
||||
}
|
||||
},
|
||||
|
@ -43,6 +43,7 @@ home:
|
||||
base: 'Based on'
|
||||
alt: 'You can also enter interchangeable forms in each field separately, eg. <code>him&her</code> = “him” or “her”.'
|
||||
pronunciation: 'You can also specify the pronunciation after a pipe character, using IPA, eg. <code>faerself|fɛɹsɛlf</code> = “faerself” pronounced as /fɛɹsɛlf/.'
|
||||
compressedAlternative: 'Use the compressed formatting alternative'
|
||||
whatisit: 'What''s the deal with pronouns?'
|
||||
mission:
|
||||
header: 'Our mission'
|
||||
@ -81,6 +82,10 @@ pronouns:
|
||||
comprehensive:
|
||||
simple: 'simple'
|
||||
comprehensive: 'comprehensive'
|
||||
slashes:
|
||||
plural: 'plural'
|
||||
pluralHonorific: 'plural-honorific'
|
||||
description: 'description'
|
||||
others: 'Other forms'
|
||||
othersRaw: 'other'
|
||||
or: 'or'
|
||||
|
@ -4,30 +4,30 @@ sie,sie/ihr Femininum TRUE sie ihrer ihr sie ihre ihrer ihrer ihre ihr ihres ihr
|
||||
es,es/sein,es/ihm Neutrum TRUE es seiner ihm es seine seiner seiner seine sein seines seinem seinen sein seines seinem sein das des dem das dieses dieses diesem dieses FALSE FALSE TRUE
|
||||
es/ihr Neutrum mit femininen Formen FALSE es ihrer ihr es ihre ihrer ihrer ihre ihr ihres ihrem ihren ihr ihres ihrem ihr das der der das dieses dieser dieser dieses FALSE FALSE TRUE Wie {/es=„es/sein“}, wobei Wörter, bei denen das {/es=Neutrum} identisch mit dem {/er=Maskulinum} ist, durch {/sie=feminine} Formen ersetzt werden
|
||||
es/denen Neutrum mit inflexiven Formen FALSE es derer denen es deren deren deren deren deren deren deren deren deren deren deren deren das des dem das dieses dieses diesem dieses FALSE FALSE TRUE Wie {/es=„es/sein“}, wobei die meisten Wörter, bei denen das {/es=Neutrum} identisch mit dem {/er=Maskulinum} ist, durch inflexive Formen ersetzt werden, wie sie auch in {/dey/denen=„dey/denen“} verwendet werden
|
||||
dey,dey/denen Neopronomen „dey“ FALSE dey derer denen dey deren deren deren deren deren deren deren deren deren deren deren deren FALSE FALSE TRUE Im Akkusativ ist ebenfalls {/dey/denen/demm=„demm”} üblich. pronoun_a
|
||||
dey/denen/demm Neopronomen „dey“ FALSE dey derer denen demm deren deren deren deren deren deren deren deren deren deren deren deren FALSE FALSE TRUE Im Akkusativ ist ebenfalls {/dey/denen=„dey”} üblich. pronoun_a
|
||||
die,die/denen Neopronomen „die“ FALSE die derer denen die deren deren deren deren deren deren deren deren deren deren deren deren FALSE FALSE TRUE
|
||||
el,el/em Neopronomen „el“ FALSE el|ɛl emser|ɛmzɐ em|ɛm en|ɛn emse|ɛmzə emser|ɛmzɐ emser|ɛmzɐ emse|ɛmzə ems|ɛmz emses|ɛmzəs emsem|ɛmzəm emsen|ɛmzən ems|ɛmz emses|ɛmzəs emsem|ɛmzəm ems|ɛmz FALSE FALSE TRUE
|
||||
em,em/em Neopronomen „em“ FALSE em|ɛm emser|ɛmzɐ em|ɛm em|ɛm ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz FALSE FALSE TRUE
|
||||
dey,dey/denen Neopronomen „dey“ FALSE dey derer denen dey deren deren deren deren deren deren deren deren deren deren deren deren ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Im Akkusativ ist ebenfalls {/dey/denen/demm=„demm”} üblich. pronoun_a
|
||||
dey/denen/demm Neopronomen „dey“ FALSE dey derer denen demm deren deren deren deren deren deren deren deren deren deren deren deren ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Im Akkusativ ist ebenfalls {/dey/denen=„dey”} üblich. pronoun_a
|
||||
die,die/denen Neopronomen „die“ FALSE die derer denen die deren deren deren deren deren deren deren deren deren deren deren deren ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
el,el/em Neopronomen „el“ FALSE el|ɛl emser|ɛmzɐ em|ɛm en|ɛn emse|ɛmzə emser|ɛmzɐ emser|ɛmzɐ emse|ɛmzə ems|ɛmz emses|ɛmzəs emsem|ɛmzəm emsen|ɛmzən ems|ɛmz emses|ɛmzəs emsem|ɛmzəm ems|ɛmz ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
em,em/em Neopronomen „em“ FALSE em|ɛm emser|ɛmzɐ em|ɛm em|ɛm ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ems|ɛmz ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
en,en/en Neopronomen „en“ FALSE en|ɛn enser|ɛnzɐ en|ɛn en|ɛn ense|ɛnzə enser|ɛnzɐ enser|ɛnzɐ ense|ɛnzə enses|ɛnzəs enses|ɛnzəs ensem|ɛnzəm ensen|ɛnzən ens|ɛnz enses|ɛnzəs ensem|ɛnzəm ens|ɛnz de|deː ders|dɛrz derm|dɛrm de|deː de|deː dersen|dɛrzən derm|dɛrm de|deː FALSE FALSE TRUE Erstellt vom österreichischen LGBTIQA+-Kongress 2018 in St. Pölten
|
||||
en/em Neopronomen „en“ FALSE en|ɛn enser|ɛnzɐ em|ɛm en|ɛn ense|ɛnzə enser|ɛnzɐ enser|ɛnzɐ ense|ɛnzə ens|ɛnz enses|ɛnzəs ensem|ɛnzəm ensen|ɛnzən ens|ɛnz enses|ɛnzəs ensem|ɛnzəm ens|ɛnz de|deː ders|dɛrz derm|dɛrm de|deː de|deː dersen|dɛrzən derm|dɛrm de|deː FALSE FALSE TRUE Erstellt vom {https://geschlechtsneutral.net/=Verein für geschlechtsneutrales Deutsch}
|
||||
ey,ey/emm Neopronomen „ey“ FALSE ey eyser emm emm eyse eyser eyser eyse eys eyses eysem eysen eys eyses eysem eys FALSE FALSE TRUE
|
||||
et,et/siem Neopronomen „et“ FALSE et sierer siem sien siere sierer sierer siere sier sieres sierem sieren sier sieres sierem sieren dat diesers diem dien dieset diesigen diesetne FALSE FALSE TRUE
|
||||
hän,hän/sim Neopronomen „hän“ FALSE hän sirer sim sin sire sirer sirer sire sir sires sirem siren sir sires sirem siren FALSE FALSE TRUE
|
||||
ey,ey/emm Neopronomen „ey“ FALSE ey eyser emm emm eyse eyser eyser eyse eys eyses eysem eysen eys eyses eysem eys ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
et,et/siem Neopronomen „et“ FALSE et sierer siem sien siere sierer sierer siere sier sieres sierem sieren sier sieres sierem sieren dat diesers diem dien dieset ~ diesigen diesetne FALSE FALSE TRUE
|
||||
hän,hän/sim Neopronomen „hän“ FALSE hän sirer sim sin sire sirer sirer sire sir sires sirem siren sir sires sirem siren ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
hen,hen/hen,hen/hem Neopronomen „hen“ FALSE hen henser hem hen hens hens hens hens hens hens hens hens hens hens hens hens dai dais dam dai diesai diesais deisam diesai FALSE FALSE TRUE Aus dem Schwedischen, siehe: {https://sv.pronouns.page/hen=hen/henom}. Auch verwendet im {https://geschlechtsneutralesdeutsch.com/=NoNa-System}.
|
||||
hie,hie/hein,hie/hiem Neopronomen „hie“ FALSE hie heiner hiem hie heine heiner heiner heine hein heines heinem heinen hein heines heinem hein FALSE FALSE TRUE
|
||||
iks,iks/iks Neopronomen „iks“ FALSE iks ikser iks iks ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses deks dieseks FALSE FALSE TRUE
|
||||
ind,ind/inde Neopronomen „ind“ FALSE ind|ɪnd indser|ɪndzɐ inde|ɪndə ind|ɪnd indse|ɪndzə indser|ɪndzɐ indser|ɪndzɐ indse|ɪndzə inds|ɪndz indsens|ɪndzəs indsem|ɪndzəm indsen|ɪndzən inds|ɪndz indsens|ɪndzəns indsem|ɪndzəm inds|ɪndz FALSE FALSE TRUE Vom Wort „Individuum“
|
||||
mensch,mensch/mensch Neopronomen „mensch” FALSE mensch menscher mensch mensch menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs FALSE FALSE TRUE Oft wird „mensch” auch als {https://de.wikipedia.org/wiki/Generalisierendes_Personalpronomen=generalisierendes Pronomen} benutzt (statt „man”).@Eher als eines von mehreren Pronomen verwendet ({https://nibi.space/pronomen#mensch=laut nibi.space}).
|
||||
hie,hie/hein,hie/hiem Neopronomen „hie“ FALSE hie heiner hiem hie heine heiner heiner heine hein heines heinem heinen hein heines heinem hein ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
iks,iks/iks Neopronomen „iks“ FALSE iks ikser iks iks ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses ikses deks ~ ~ ~ dieseks ~ ~ ~ FALSE FALSE TRUE
|
||||
ind,ind/inde Neopronomen „ind“ FALSE ind|ɪnd indser|ɪndzɐ inde|ɪndə ind|ɪnd indse|ɪndzə indser|ɪndzɐ indser|ɪndzɐ indse|ɪndzə inds|ɪndz indsens|ɪndzəs indsem|ɪndzəm indsen|ɪndzən inds|ɪndz indsens|ɪndzəns indsem|ɪndzəm inds|ɪndz ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Vom Wort „Individuum“
|
||||
mensch,mensch/mensch Neopronomen „mensch” FALSE mensch menscher mensch mensch menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs menschs ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Oft wird „mensch” auch als {https://de.wikipedia.org/wiki/Generalisierendes_Personalpronomen=generalisierendes Pronomen} benutzt (statt „man”).@Eher als eines von mehreren Pronomen verwendet ({https://nibi.space/pronomen#mensch=laut nibi.space}).
|
||||
nin,nin/nim Neopronomen „nin“ FALSE nin nimser nim nin nimse nimser nimser nimse nims nimses nimsem nimsen nims nimses nimsem nims din dins dim din diesin diesins diesim diesin FALSE FALSE TRUE Erstellt von {https://www.geschlechtsneutral.com/lit/Liminalis-2008-Sylvain-Balzer.pdf=Cabala de Sylvain und Carsten Balzer}
|
||||
oj,oj/juj,oj/ojm Neopronomen „oj“ FALSE oj jujer ojm ojn juje jujer jujer juje juj jujes jusem jusen jus juses jusem jus FALSE FALSE TRUE Erstellt von {https://www.frumble.de/blog/2021/03/26/ueberlegungen-zu-einer-genderneutralen-deutschen-grammatik=Frumble}. Siehe auch: {/substantive#Ojum=Ojum}.
|
||||
per,per/per Neopronomen „per“ FALSE per perer per per pers pers pers pers pers pers pers pers pers pers pers pers FALSE FALSE TRUE Vom Wort „Person“
|
||||
ser,ser/sem Neopronomen „ser“ FALSE ser seser sem sen ses ses ses ses ses ses ses ses ses ses ses ses FALSE FALSE TRUE
|
||||
oj,oj/juj,oj/ojm Neopronomen „oj“ FALSE oj jujer ojm ojn juje jujer jujer juje juj jujes jusem jusen jus juses jusem jus ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Erstellt von {https://www.frumble.de/blog/2021/03/26/ueberlegungen-zu-einer-genderneutralen-deutschen-grammatik=Frumble}. Siehe auch: {/substantive#Ojum=Ojum}.
|
||||
per,per/per Neopronomen „per“ FALSE per perer per per pers pers pers pers pers pers pers pers pers pers pers pers ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Vom Wort „Person“
|
||||
ser,ser/sem Neopronomen „ser“ FALSE ser seser sem sen ses ses ses ses ses ses ses ses ses ses ses ses ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
sier,sier/siem Neopronomen „sier“ FALSE sier|zi:ɐ̯ sieser|zi:sɐ siem|zi:m sien|zi:n siese|zi:zə sieser|zi:zɐ sieser|zi:zɐ siese|zi:zə sies|zi:z sieses|zi:zəs siesem|zi:zəm siesen|zi:zən sies|zi:z sieses|zi:zəs siesem|zi:zəm sies|zi:z dier|di:ɐ̯ dies|di:s diem|di:m dien|di:n dier|di:ɐ̯ dies|di:s diem|di:m dien|di:n FALSE FALSE TRUE Erstellt von {https://www.annaheger.de/pronomen21/=Illi Anna Heger}
|
||||
they,they/them Äquivalent zu englischem „they“ FALSE they them them them their their their their their their their their their their their their FALSE FALSE TRUE Aus dem Englischen, sieh: {https://en.pronouns.page/they=they/them}
|
||||
they,they/them Äquivalent zu englischem „they“ FALSE they them them them their their their their their their their their their their their their ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE Aus dem Englischen, sieh: {https://en.pronouns.page/they=they/them}
|
||||
xier,xier/xiem Neopronomen „xier“ FALSE xier|ksi:ɐ̯ xieser|ksi:sɐ xiem|ksi:m xien|ksi:n xiese|ksi:zə xieser|ksi:ɐ̯sɐ xieser|ksi:ɐ̯sɐ xiese|ksi:zə xies|ksi:z xieses|ksi:zəs xiesem|ksi:zəm xiesen|ksi:zən xies|ksi:z xieses|ksi:zəs xiesem|ksi:zəm xies|ksi:z dier|di:ɐ̯ dies|di:s diem|di:m dien|di:n dier|di:ɐ̯ dies|di:s diem|di:m dien|di:n FALSE FALSE TRUE Erstellt von {https://www.annaheger.de/pronomen33/=Illi Anna Heger}.
|
||||
zet,zet/zerm Neopronomen „zet“ FALSE zet zerner zerm zern zets zets zets zets zets zets zets zets zets zets zets zets FALSE FALSE TRUE
|
||||
zet,zet/zerm Neopronomen „zet“ FALSE zet zerner zerm zern zets zets zets zets zets zets zets zets zets zets zets zets ~ ~ ~ ~ ~ ~ ~ ~ FALSE FALSE TRUE
|
||||
er_sie,er_sie/ihm_ihr Neopronomen „er_sie“ FALSE er_sie seiner_ihrer ihm_ihr ihn_sie seine_ihre seiner_ihrer seiner_ihrer seine_ihre sein_ihr seines_ihres seinem_ihrem seinen_ihren sein_ihr seines_ihres seinem_ihrem sein_ihr der_die des_der dem_der den_die diese_r dieses_dieser diesem_dieser diese_n FALSE FALSE FALSE
|
||||
er*sie,er*sie/ihm*ihr Neopronomen „er*sie“ FALSE er*sie seiner*ihrer ihm*ihr ihn*sie seine*ihre seiner*ihrer seiner*ihrer seine*ihre sein*ihr seines*ihres seinem*ihrem seinen*ihren sein*ihr seines*ihres seinem*ihrem sein*ihr der*die des*der dem*der den*die diese*r dieses*dieser diesem*dieser diese*n FALSE FALSE FALSE
|
||||
er:sie,er:sie/ihm:ihr Neopronomen „er:sie“ FALSE er:sie seiner:ihrer ihm:ihr ihn:sie seine:ihre seiner:ihrer seiner:ihrer seine:ihre sein:ihr seines:ihres seinem:ihrem seinen:ihren sein:ihr seines:ihres seinem:ihrem sein:ihr der:die des:der dem:der den:die diese:r dieses:dieser diesem:dieser diese:n FALSE FALSE FALSE
|
||||
ersie,ersie/ihmihr Neopronomen „ersie“ FALSE ersie seinerihrer ihmihr ihnsie seineihre seinerihrer seinerihrer seineihre seinihr seinesihres seinemihrem seinenihren seinihr seinesihres seinemihrem seinihr derdie desder demder dendie dieserdiese diesesdieser diesemdieser diesediesen FALSE FALSE FALSE
|
||||
ersie,ersie/ihmihr Neopronomen „ersie“ FALSE ersie seinerihrer ihmihr ihnsie seineihre seinerihrer seinerihrer seineihre seinihr seinesihres seinemihrem seinenihren seinihr seinesihres seinemihrem seinihr derdie desder demder dendie dieserdiese diesesdieser diesemdieser diesediesen FALSE FALSE FALSE
|
||||
|
|
@ -41,6 +41,7 @@ home:
|
||||
Du kannst auch die Aussprache spezifizieren, die indem du nach einem senkrechten Strich „|“ (mit der Tastenkombination <kbd>Alt Gr+<</kbd>) hinzufügst.
|
||||
Benutze dafür die {https://de.wikipedia.org/wiki/Liste_der_IPA-Zeichen=Lautschrift IPA (Internationales Phonetisches Alphabet)}.
|
||||
Ein Beispiel wäre: <code>sier|zi:ɐ̯</code> = „sier“, ausgesprochen wie /zi:ɐ̯/.
|
||||
compressedAlternative: 'komprimierte Formattierung verwenden'
|
||||
whatisit: 'Was ist das Problem mit Pronomen?'
|
||||
mission:
|
||||
header: 'Unsere Mission'
|
||||
@ -85,6 +86,9 @@ pronouns:
|
||||
comprehensive:
|
||||
simple: 'häufige'
|
||||
comprehensive: 'erweitert'
|
||||
slashes:
|
||||
plural: 'plural'
|
||||
description: 'beschreibung'
|
||||
others: 'Andere Formen'
|
||||
othersRaw: 'andere'
|
||||
or: 'oder'
|
||||
|
@ -43,6 +43,7 @@ home:
|
||||
base: 'Based on'
|
||||
alt: 'You can also enter interchangeable forms in each field separately, eg. <code>him&her</code> = “him” or “her”.'
|
||||
pronunciation: 'You can also specify the pronunciation after a pipe character, using IPA, eg. <code>faerself|fɛɹsɛlf</code> = “faerself” pronounced as /fɛɹsɛlf/.'
|
||||
compressedAlternative: 'Use the compressed formatting alternative'
|
||||
whatisit: 'What''s the deal with pronouns?'
|
||||
mission:
|
||||
header: 'Our mission'
|
||||
@ -78,6 +79,9 @@ pronouns:
|
||||
Even though for many people it's incredibly important that people use specific pronouns to talk about them,
|
||||
others don't mind being addressed in any way – as long as the context is clear as to who one talks about.
|
||||
options: 'check out the options [share]{/pronouns=here}.'
|
||||
slashes:
|
||||
plural: 'plural'
|
||||
description: 'description'
|
||||
others: 'Other forms'
|
||||
othersRaw: 'other'
|
||||
or: 'or'
|
||||
|
@ -1,15 +1,19 @@
|
||||
import Vue from 'vue';
|
||||
import translator from '../src/translator.js';
|
||||
import { Translator } from '../src/translator.js';
|
||||
import config from '../data/config.suml';
|
||||
import { buildDict } from '../src/helpers.js';
|
||||
import { DateTime, Settings } from 'luxon';
|
||||
import { decodeTime } from 'ulid';
|
||||
|
||||
import translations from '../data/translations.suml';
|
||||
import baseTranslations from '../locale/_base/translations.suml';
|
||||
|
||||
export default ({ app, store }) => {
|
||||
Vue.prototype.$eventHub = new Vue();
|
||||
|
||||
Vue.prototype.$base = process.env.BASE_URL;
|
||||
|
||||
const translator = new Translator(translations, baseTranslations, config);
|
||||
Vue.prototype.$translator = translator;
|
||||
Vue.prototype.$t = (key, params = {}, warn = false) => translator.translate(key, params, warn);
|
||||
Vue.prototype.$te = (key, fallback = false) => {
|
||||
|
@ -60,7 +60,7 @@ export default {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pronounEntity = buildPronoun(pronouns, link);
|
||||
const pronounEntity = buildPronoun(pronouns, link, this.$translator);
|
||||
|
||||
if (pronounEntity) {
|
||||
pronounOpinions.push({
|
||||
@ -76,7 +76,7 @@ export default {
|
||||
if (!this.config.profile?.flags?.defaultPronoun) {
|
||||
return null;
|
||||
}
|
||||
let mainPronoun = buildPronoun(pronouns, this.config.profile.flags.defaultPronoun);
|
||||
let mainPronoun = buildPronoun(pronouns, this.config.profile.flags.defaultPronoun, this.$translator);
|
||||
let mainOpinion = -1;
|
||||
for (const { pronoun, opinion } of this.pronounOpinions) {
|
||||
if (typeof pronoun === 'string') {
|
||||
|
@ -28,7 +28,7 @@
|
||||
<tbody>
|
||||
<tr v-for="tp in translationProposals">
|
||||
<td>{{ tp.tKey }}</td>
|
||||
<td>{{ translator.get(tp.tKey, false, true) }}</td>
|
||||
<td>{{ $translator.get(tp.tKey, false, true) }}</td>
|
||||
<td>{{ $te(tp.tKey) ? $t(tp.tKey) : '' }}</td>
|
||||
<td>{{ tp.tValue }}</td>
|
||||
<td>
|
||||
@ -132,7 +132,6 @@
|
||||
|
||||
<script>
|
||||
import { deepSet, head } from '../src/helpers.js';
|
||||
import translator from '../src/translator.js';
|
||||
import Suml from 'suml';
|
||||
|
||||
export default {
|
||||
@ -142,11 +141,6 @@ export default {
|
||||
contributors: await app.$axios.$get('/translations/contributors'),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
translator,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return head({
|
||||
title: `${this.$t('admin.header')} • Translation proposals`,
|
||||
|
@ -30,7 +30,7 @@
|
||||
<tbody>
|
||||
<tr v-for="mt in missingTranslations">
|
||||
<td>{{ mt }}</td>
|
||||
<td>{{ translator.get(mt, false, true) }}</td>
|
||||
<td>{{ $translator.get(mt, false, true) }}</td>
|
||||
<td><T>{{ mt }}</T></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -43,13 +43,11 @@
|
||||
|
||||
<script>
|
||||
import { head } from '../src/helpers.js';
|
||||
import translator from '../src/translator.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
translator,
|
||||
missingTranslations: translator.listMissingTranslations(),
|
||||
missingTranslations: this.$translator.listMissingTranslations(),
|
||||
};
|
||||
},
|
||||
head() {
|
||||
|
@ -34,7 +34,8 @@ import Columnist from 'avris-columnist';
|
||||
|
||||
export default {
|
||||
async asyncData({ route }) {
|
||||
return parseMarkdown((await import(`../data/blog/${route.params.slug || route.meta[0].slug}.md`)).default);
|
||||
const content = (await import(`../data/blog/${route.params.slug || route.meta[0].slug}.md`)).default;
|
||||
return parseMarkdown(content, this.$translator);
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -97,7 +97,10 @@ import { getContactLinks, getSocialLinks } from '../src/contact.js';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
links: groupBy([...getContactLinks(this.config), ...getSocialLinks(this.config)], (l) => l.group),
|
||||
links: groupBy(
|
||||
[...getContactLinks(this.config, this.$translator), ...getSocialLinks(this.config)],
|
||||
(l) => l.group,
|
||||
),
|
||||
};
|
||||
},
|
||||
head() {
|
||||
|
@ -19,7 +19,7 @@ import parseMarkdown from '../src/parseMarkdown.js';
|
||||
|
||||
export default {
|
||||
async asyncData() {
|
||||
return parseMarkdown((await import('../LICENSE.md')).default);
|
||||
return parseMarkdown((await import('../LICENSE.md')).default, this.$translator);
|
||||
},
|
||||
head() {
|
||||
return head({
|
||||
|
@ -359,7 +359,6 @@ import config from '../data/config.suml';
|
||||
import link from '../plugins/link.js';
|
||||
import { minBirthdate, maxBirthdate, formatDate, parseDate } from '../src/birthdate.js';
|
||||
import opinions from '../src/opinions.js';
|
||||
import t from '../src/translator.js';
|
||||
import { calendar } from '../src/calendar/calendar.js';
|
||||
|
||||
const defaultWords = config.profile.defaultWords.map(({ header, values }) => {
|
||||
@ -385,19 +384,19 @@ function fixArrayObject(arrayObject) {
|
||||
return Array.isArray(arrayObject) ? arrayObject : Object.values(arrayObject);
|
||||
}
|
||||
|
||||
const opinionsToForm = (opinions) => buildList(function*() {
|
||||
const opinionsToForm = (opinions, translator) => buildList(function*() {
|
||||
for (const [key, options] of Object.entries(opinions)) {
|
||||
yield {
|
||||
key,
|
||||
icon: options.icon,
|
||||
description: options.description || t.get(`profile.opinion.${key}`),
|
||||
description: options.description || translator.get(`profile.opinion.${key}`),
|
||||
colour: options.colour || '',
|
||||
style: options.style || '',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const buildProfile = (profiles, currentLocale) => {
|
||||
const buildProfile = (profiles, currentLocale, translator) => {
|
||||
// card in this locale exists
|
||||
for (const locale in profiles) {
|
||||
if (!profiles.hasOwnProperty(locale)) {
|
||||
@ -421,7 +420,7 @@ const buildProfile = (profiles, currentLocale) => {
|
||||
credentials: profile.credentials,
|
||||
credentialsLevel: profile.credentialsLevel,
|
||||
credentialsName: profile.credentialsName,
|
||||
opinions: opinionsToForm(profile.opinions || {}),
|
||||
opinions: opinionsToForm(profile.opinions || {}, translator),
|
||||
circle: profile.circle,
|
||||
sensitive: profile.sensitive,
|
||||
markdown: profile.markdown,
|
||||
@ -453,7 +452,7 @@ const buildProfile = (profiles, currentLocale) => {
|
||||
credentials: [],
|
||||
credentialsLevel: null,
|
||||
credentialsName: null,
|
||||
opinions: opinionsToForm(profile.opinions || {}),
|
||||
opinions: opinionsToForm(profile.opinions || {}, translator),
|
||||
circle: profile.circle,
|
||||
sensitive: [],
|
||||
markdown: profile.markdown,
|
||||
@ -501,7 +500,7 @@ export default {
|
||||
authorization: `Bearer ${store.state.token}`,
|
||||
} })).profiles;
|
||||
|
||||
const profile = buildProfile(profiles, currentLocale);
|
||||
const profile = buildProfile(profiles, currentLocale, app.$translator);
|
||||
|
||||
return {
|
||||
...profile,
|
||||
@ -518,7 +517,7 @@ export default {
|
||||
},
|
||||
propagate: [],
|
||||
flagsAsterisk: process.env.FLAGS_ASTERISK,
|
||||
defaultOpinions: opinionsToForm(opinions),
|
||||
defaultOpinions: opinionsToForm(opinions, this.$translator),
|
||||
isValidLink,
|
||||
year: calendar.getCurrentYear(),
|
||||
};
|
||||
@ -533,7 +532,7 @@ export default {
|
||||
if (!this.config.profile?.flags?.defaultPronoun) {
|
||||
return null;
|
||||
}
|
||||
let mainPronoun = buildPronoun(pronouns, this.config.profile.flags?.defaultPronoun);
|
||||
let mainPronoun = buildPronoun(pronouns, this.config.profile.flags?.defaultPronoun, this.$translator);
|
||||
let mainOpinion = -1;
|
||||
for (const { value: pronoun, opinion } of this.pronouns) {
|
||||
const opinionValue = opinions[opinion]?.value || 0;
|
||||
@ -628,7 +627,7 @@ export default {
|
||||
}
|
||||
},
|
||||
normaliseAndBuildPronoun(pronoun) {
|
||||
return buildPronoun(pronouns, this.normalisePronoun(pronoun));
|
||||
return buildPronoun(pronouns, this.normalisePronoun(pronoun), this.$translator);
|
||||
},
|
||||
validatePronoun(pronoun) {
|
||||
pronoun = this.normalisePronoun(pronoun);
|
||||
@ -638,7 +637,7 @@ export default {
|
||||
return this.validateAnyPronoun(pronoun) ||
|
||||
this.config.pronouns.null && this.config.pronouns.null.routes && this.config.pronouns.null.routes.includes(pronoun) ||
|
||||
this.config.pronouns.mirror && this.config.pronouns.mirror.route === pronoun ||
|
||||
buildPronoun(pronouns, pronoun)
|
||||
buildPronoun(pronouns, pronoun, this.$translator)
|
||||
? null
|
||||
: 'profile.pronounsNotFound';
|
||||
},
|
||||
|
@ -130,7 +130,7 @@ export default {
|
||||
const key = this.getPronounKeyFromUrl();
|
||||
|
||||
const selectedPronoun = this.config.pronouns.enabled && key
|
||||
? buildPronoun(pronouns, key)
|
||||
? buildPronoun(pronouns, key, this.$translator)
|
||||
: null;
|
||||
|
||||
return {
|
||||
|
@ -75,7 +75,7 @@
|
||||
v-model="selectedPronoun.description"
|
||||
class="form-control form-input p-0 form-control-sm"
|
||||
:size="selectedPronoun.description.length ? selectedPronoun.description.length + 3 : 16"
|
||||
maxlength="64"
|
||||
:maxlength="DESCRIPTION_MAXLENGTH"
|
||||
:placeholder="$t('profile.description')"
|
||||
>
|
||||
</template>
|
||||
@ -128,6 +128,12 @@
|
||||
</div>
|
||||
<div v-if="link" class="card-footer">
|
||||
<LinkInput :link="link" />
|
||||
<div v-if="config.pronouns.slashes" class="form-check form-switch">
|
||||
<label>
|
||||
<input v-model="linkCompressedAlternative" class="form-check-input" type="checkbox">
|
||||
<T>home.generator.compressedAlternative</T>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@ -247,7 +253,7 @@
|
||||
|
||||
<script>
|
||||
import { examples, pronouns, pronounLibrary } from '../src/data.js';
|
||||
import { ExamplePart } from '../src/classes.js';
|
||||
import { ExamplePart, Pronoun } from '../src/classes.js';
|
||||
import Compressor from '../src/compressor.js';
|
||||
import MORPHEMES from '../data/pronouns/morphemes.js';
|
||||
import { mapState } from 'vuex';
|
||||
@ -265,6 +271,7 @@ export default {
|
||||
// for its' default to prevent a crash
|
||||
selectedPronoun: pronouns[this.config.pronouns.default].clone(true) ?? 'he',
|
||||
selectedMorpheme: '',
|
||||
linkCompressedAlternative: false,
|
||||
|
||||
customiseMultiple: false,
|
||||
multiple: this.config.pronouns.multiple ? this.config.pronouns.multiple.examples[0].split('&') : [],
|
||||
@ -272,6 +279,8 @@ export default {
|
||||
customise: this.config.pronouns.autoOpenGenerator || false,
|
||||
|
||||
glue: ` ${this.$t('pronouns.or')} `,
|
||||
|
||||
DESCRIPTION_MAXLENGTH: Pronoun.DESCRIPTION_MAXLENGTH,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -313,12 +322,16 @@ export default {
|
||||
return null;
|
||||
}
|
||||
|
||||
const slashes = this.selectedPronoun.toStringSlashes();
|
||||
const commas = this.usedBaseEquals ? this.usedBase : this.longLink;
|
||||
const slashes = this.selectedPronoun.toStringSlashes(this.$translator);
|
||||
|
||||
const link = slashes && slashes.length < commas.length
|
||||
? slashes
|
||||
: commas;
|
||||
let link;
|
||||
if (this.usedBaseEquals) {
|
||||
link = this.usedBase;
|
||||
} else if (slashes && !this.linkCompressedAlternative) {
|
||||
link = slashes;
|
||||
} else {
|
||||
link = this.longLink;
|
||||
}
|
||||
|
||||
return this.addSlash(`${this.$base + (this.config.pronouns.prefix || '')}/${link}`);
|
||||
},
|
||||
|
@ -1,15 +1,19 @@
|
||||
import { Router } from 'express';
|
||||
import SQL from 'sql-template-strings';
|
||||
import { createCanvas, loadImage } from 'canvas';
|
||||
import { loadSuml } from '../loader.js';
|
||||
import { loadSuml, loadSumlFromBase } from '../loader.js';
|
||||
import avatar from '../avatar.js';
|
||||
import { buildPronoun, parsePronouns } from '../../src/buildPronoun.js';
|
||||
import { loadTsv } from '../../src/tsv.js';
|
||||
import { handleErrorAsync } from '../../src/helpers.js';
|
||||
import { Translator } from '../../src/translator.js';
|
||||
import { CacheObject } from '../../src/cache.js';
|
||||
import { registerLocaleFont } from '../localeFont.js';
|
||||
|
||||
const translations = loadSuml('translations');
|
||||
const baseTranslations = loadSumlFromBase('locale/_base/translations');
|
||||
|
||||
const translator = new Translator(translations, baseTranslations, global.config);
|
||||
|
||||
const drawCircle = (context, image, x, y, size) => {
|
||||
context.save();
|
||||
@ -84,6 +88,7 @@ router.get('/banner/:pronounName*.png', handleErrorAsync(async (req, res) => {
|
||||
const pronoun = buildPronoun(
|
||||
parsePronouns(loadTsv(`${__dirname}/../../data/pronouns/pronouns.tsv`)),
|
||||
pronounName,
|
||||
translator,
|
||||
);
|
||||
|
||||
if (pronounName === 'zaimki' || !pronoun && pronounName !== global.config.pronouns.any && (!global.config.pronouns || pronounName !== global.config.pronouns.mirror)) {
|
||||
|
@ -1,13 +1,19 @@
|
||||
import { Router } from 'express';
|
||||
import { loadTsv } from '../loader.js';
|
||||
import { loadSuml, loadSumlFromBase, loadTsv } from '../loader.js';
|
||||
import { buildPronoun, parsePronouns } from '../../src/buildPronoun.js';
|
||||
import { buildList, handleErrorAsync } from '../../src/helpers.js';
|
||||
import { Example } from '../../src/classes.js';
|
||||
import { caches } from '../../src/cache.js';
|
||||
import { Translator } from '../../src/translator.js';
|
||||
import md5 from 'js-md5';
|
||||
import assert from 'assert';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const translations = loadSuml('translations');
|
||||
const baseTranslations = loadSumlFromBase('locale/_base/translations');
|
||||
|
||||
const translator = new Translator(translations, baseTranslations, global.config);
|
||||
|
||||
const buildExample = (e) => new Example(
|
||||
Example.parse(e.singular),
|
||||
Example.parse(e.plural || e.singular),
|
||||
@ -52,6 +58,7 @@ router.get('/pronouns/:pronoun*', handleErrorAsync(async (req, res) => {
|
||||
const pronoun = buildPronoun(
|
||||
parsePronouns(loadTsv('pronouns/pronouns')),
|
||||
req.params.pronoun + req.params[0],
|
||||
translator,
|
||||
);
|
||||
if (pronoun) {
|
||||
pronoun.examples = addExamples(pronoun, requestExamples(req.query.examples));
|
||||
@ -64,6 +71,7 @@ router.get('/pronouns-name/:pronoun*', handleErrorAsync(async (req, res) => {
|
||||
const pronoun = buildPronoun(
|
||||
parsePronouns(loadTsv('pronouns/pronouns')),
|
||||
(req.params.pronoun + req.params[0]).toLowerCase(),
|
||||
translator,
|
||||
);
|
||||
if (!pronoun) {
|
||||
return res.status(404).json({ error: 'Not found' });
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pronoun } from './classes.js';
|
||||
import Compressor from './compressor.js';
|
||||
import { buildDict, isEmoji } from './helpers.js';
|
||||
import { buildDict, isEmoji, unescapeControlSymbols } from './helpers.js';
|
||||
import MORPHEMES from '../data/pronouns/morphemes.js';
|
||||
|
||||
export const addAliasesToPronouns = (pronouns) => {
|
||||
@ -58,7 +58,79 @@ const buildPronounFromTemplate = (key, template) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const buildPronoun = (pronouns, path) => {
|
||||
const isModifier = (chunk, key, translator) => {
|
||||
// use both locale and base translations to ensure backwards compatibility if key gets translated
|
||||
return chunk === `:${translator.translate(key)}` || chunk === `:${translator.get(key, false, true)}`;
|
||||
};
|
||||
|
||||
const extractModifierValue = (chunk, key, translator) => {
|
||||
// use both locale and base translations to ensure backwards compatibility if key gets translated
|
||||
const prefixes = [`:${translator.translate(key)}=`, `:${translator.get(key, false, true)}=`];
|
||||
for (const prefix of prefixes) {
|
||||
if (chunk.startsWith(prefix)) {
|
||||
return chunk.substring(prefix.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const buildPronounFromSlashes = (config, path, translator) => {
|
||||
const chunks = path.split(/(?<!`)\//);
|
||||
let plural = false;
|
||||
let pluralHonorific = false;
|
||||
let description = '';
|
||||
const morphemeChunks = [];
|
||||
for (const chunk of chunks) {
|
||||
if (chunk.startsWith(':')) {
|
||||
if (config.pronouns.plurals && isModifier(chunk, 'pronouns.slashes.plural', translator)) {
|
||||
plural = true;
|
||||
} else if (config.pronouns.plurals && config.pronouns.honorifics &&
|
||||
isModifier(chunk, 'pronouns.slashes.pluralHonorific', translator)) {
|
||||
pluralHonorific = true;
|
||||
} else {
|
||||
const descriptionModifierValue =
|
||||
extractModifierValue(chunk, 'pronouns.slashes.description', translator);
|
||||
if (descriptionModifierValue) {
|
||||
description = unescapeControlSymbols(descriptionModifierValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chunk === '~') {
|
||||
morphemeChunks.push(null);
|
||||
} else if (chunk === ' ') {
|
||||
morphemeChunks.push('');
|
||||
} else {
|
||||
morphemeChunks.push(unescapeControlSymbols(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (description.length > Pronoun.DESCRIPTION_MAXLENGTH) {
|
||||
return null;
|
||||
}
|
||||
const slashMorphemes = config.pronouns.slashes === true
|
||||
? MORPHEMES
|
||||
: config.pronouns.slashes;
|
||||
if (slashMorphemes && morphemeChunks.length === slashMorphemes.length) {
|
||||
return new Pronoun(
|
||||
`${morphemeChunks[0]}/${morphemeChunks[1]}`,
|
||||
description,
|
||||
false,
|
||||
buildDict(function*() {
|
||||
for (const m of MORPHEMES) {
|
||||
const index = slashMorphemes.indexOf(m);
|
||||
yield [m, index === -1 ? null : morphemeChunks[index]];
|
||||
}
|
||||
}),
|
||||
[plural],
|
||||
[pluralHonorific],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const buildPronoun = (pronouns, path, translator) => {
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
@ -125,29 +197,8 @@ export const buildPronoun = (pronouns, path) => {
|
||||
pronoun = buildPronounFromTemplate(path.substring(1), config.pronouns.null);
|
||||
}
|
||||
|
||||
const p = path.split('/').filter((s) => !!s);
|
||||
if (!pronoun && config.pronouns.slashes !== false) {
|
||||
const slashMorphemes = config.pronouns.slashes === true
|
||||
? MORPHEMES
|
||||
: config.pronouns.slashes;
|
||||
if (slashMorphemes && p.length === slashMorphemes.length) {
|
||||
pronoun = new Pronoun(
|
||||
`${p[0]}/${p[1]}`,
|
||||
'',
|
||||
false,
|
||||
buildDict(function*() {
|
||||
for (const m of MORPHEMES) {
|
||||
const index = slashMorphemes.indexOf(m);
|
||||
yield [m, index === -1 ? '' : p[index]];
|
||||
}
|
||||
}),
|
||||
[p[p.length - 1].endsWith('selves')], // TODO English specific, extract somewhere
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
}
|
||||
return buildPronounFromSlashes(config, path, translator);
|
||||
}
|
||||
|
||||
return pronoun;
|
||||
@ -166,7 +217,19 @@ export const parsePronouns = (pronounsRaw) => {
|
||||
t.normative,
|
||||
buildDict(function* () {
|
||||
for (const morpheme of MORPHEMES) {
|
||||
yield [morpheme, t[morpheme]];
|
||||
let value;
|
||||
if (t[morpheme] === null) {
|
||||
// empty cells are parsed as null in dynamic parse mode,
|
||||
// but most of the time an empty string is intended
|
||||
value = '';
|
||||
} else if (t[morpheme] === '~') {
|
||||
// to really describe that a pronoun does not support a morpheme,
|
||||
// tilde is used to describe null as in yaml.
|
||||
value = null;
|
||||
} else {
|
||||
value = t[morpheme];
|
||||
}
|
||||
yield [morpheme, value];
|
||||
}
|
||||
}),
|
||||
[t.plural],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { buildDict, buildList, capitalise, escapePronunciationString } from './helpers.js';
|
||||
import { buildDict, buildList, capitalise, escapeControlSymbols, escapePronunciationString } from './helpers.js';
|
||||
import MORPHEMES from '../data/pronouns/morphemes.js';
|
||||
|
||||
const config = process.env.CONFIG || global.config;
|
||||
@ -295,6 +295,8 @@ const escape = (s) => {
|
||||
};
|
||||
|
||||
export class Pronoun {
|
||||
static DESCRIPTION_MAXLENGTH = 64;
|
||||
|
||||
constructor(canonicalName, description, normative, morphemes, plural, pluralHonorific, aliases = [], history = '', pronounceable = true, thirdForm = null, smallForm = null, sourcesInfo = null) {
|
||||
this.canonicalName = canonicalName;
|
||||
this.description = description || '';
|
||||
@ -305,7 +307,7 @@ export class Pronoun {
|
||||
if (!morphemes.hasOwnProperty(m)) {
|
||||
continue;
|
||||
}
|
||||
const [morpheme, pronunciation] = morphemes[m] ? morphemes[m].split('|') : [null, null];
|
||||
const [morpheme, pronunciation] = typeof morphemes[m] === 'string' ? morphemes[m].split('|') : [null, null];
|
||||
this.morphemes[m] = morpheme;
|
||||
this.pronunciations[m] = pronunciation;
|
||||
}
|
||||
@ -403,17 +405,13 @@ export class Pronoun {
|
||||
morpheme = morpheme.substring(1);
|
||||
}
|
||||
|
||||
if (!this.morphemes[morpheme]) {
|
||||
if (this.morphemes[morpheme] === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = this.morphemes[morpheme].split('&');
|
||||
|
||||
const result = options[counter % options.length];
|
||||
if (result === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return capital ? capitalise(result) : result;
|
||||
}
|
||||
|
||||
@ -469,16 +467,41 @@ export class Pronoun {
|
||||
return this.toArray().join(',');
|
||||
}
|
||||
|
||||
toStringSlashes() {
|
||||
if (!config.pronouns.slashes || this.description) {
|
||||
toStringSlashes(translator) {
|
||||
if (!config.pronouns.slashes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let chunks;
|
||||
if (Array.isArray(config.pronouns.slashes)) {
|
||||
return config.pronouns.slashes.map((m) => this.morphemes[m]).join('/');
|
||||
chunks = config.pronouns.slashes.map((m) => this.morphemes[m]);
|
||||
} else {
|
||||
chunks = Object.values(this.morphemes);
|
||||
}
|
||||
chunks = chunks.map((chunk) => {
|
||||
if (chunk === null) {
|
||||
return '~';
|
||||
} else if (chunk === '') {
|
||||
// use an extra space because double slashes get replaced by a single one during a request
|
||||
return ' ';
|
||||
} else {
|
||||
return escapeControlSymbols(chunk);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.plural[0]) {
|
||||
chunks.push(`:${translator.translate('pronouns.slashes.plural')}`);
|
||||
}
|
||||
if (this.pluralHonorific[0]) {
|
||||
chunks.push(`:${translator.translate('pronouns.slashes.pluralHonorific')}`);
|
||||
}
|
||||
if (this.description) {
|
||||
const escapedDescription = escapeControlSymbols(this.description);
|
||||
chunks.push(`:${translator.translate('pronouns.slashes.description')}=${escapedDescription}`);
|
||||
}
|
||||
|
||||
return Object.values(this.morphemes).join('/');
|
||||
// encode a trailing space so that it does not get removed during a request
|
||||
return chunks.join('/').replace(/ $/, encodeURI(' '));
|
||||
}
|
||||
|
||||
static from(data) {
|
||||
@ -557,7 +580,7 @@ export class Pronoun {
|
||||
|
||||
if (data.length !== MORPHEMES.length + extraFields ||
|
||||
data[0].length === 0 ||
|
||||
data[data.length - 1].length > 64 ||
|
||||
data[data.length - 1].length > Pronoun.DESCRIPTION_MAXLENGTH ||
|
||||
data.slice(1, data.length - extraFields).filter((s) => s.length > 24).length
|
||||
) {
|
||||
return null;
|
||||
@ -569,7 +592,7 @@ export class Pronoun {
|
||||
}
|
||||
|
||||
return new Pronoun(
|
||||
m[MORPHEMES[0]],
|
||||
`${m[MORPHEMES[0]]}/${m[MORPHEMES[1]]}`,
|
||||
data[data.length - 1],
|
||||
false,
|
||||
m,
|
||||
|
@ -1,5 +1,3 @@
|
||||
import translator from './translator.js';
|
||||
|
||||
export const contact = {
|
||||
all: {
|
||||
email: {
|
||||
@ -110,26 +108,28 @@ const AVATARS = {
|
||||
calendar: 'calendarqueer.png',
|
||||
};
|
||||
|
||||
const supportLinks = {
|
||||
all: {
|
||||
bank: {
|
||||
icon: 'money-check-alt',
|
||||
url: 'https://bunq.me/PronounsPage',
|
||||
headline: translator.translate('support.bankAccount'),
|
||||
// tooltip: translator.translate('support.bankAccountOwner'),
|
||||
const supportLinks = (translator) => {
|
||||
return {
|
||||
all: {
|
||||
bank: {
|
||||
icon: 'money-check-alt',
|
||||
url: 'https://bunq.me/PronounsPage',
|
||||
headline: translator.translate('support.bankAccount'),
|
||||
// tooltip: translator.translate('support.bankAccountOwner'),
|
||||
},
|
||||
kofi: {
|
||||
icon: 'coffee',
|
||||
url: 'https://ko-fi.com/radajezykaneutralnego',
|
||||
headline: 'Ko-Fi',
|
||||
},
|
||||
paypal: {
|
||||
icon: 'paypal',
|
||||
iconSet: 'b',
|
||||
url: 'https://paypal.me/RJNeutralnego',
|
||||
headline: 'PayPal',
|
||||
},
|
||||
},
|
||||
kofi: {
|
||||
icon: 'coffee',
|
||||
url: 'https://ko-fi.com/radajezykaneutralnego',
|
||||
headline: 'Ko-Fi',
|
||||
},
|
||||
paypal: {
|
||||
icon: 'paypal',
|
||||
iconSet: 'b',
|
||||
url: 'https://paypal.me/RJNeutralnego',
|
||||
headline: 'PayPal',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function* getLink(links, group) {
|
||||
@ -151,7 +151,7 @@ function* getLink(links, group) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* getContactLinks(config) {
|
||||
export function* getContactLinks(config, translator) {
|
||||
if (config.faq.enabled) {
|
||||
yield {
|
||||
url: `/${config.faq.route}`,
|
||||
@ -178,6 +178,6 @@ export function* getSocialLinks(config) {
|
||||
yield* getLink(socialLinks, 'shop');
|
||||
}
|
||||
|
||||
export function* getSupportLinks() {
|
||||
yield* getLink(supportLinks, 'all');
|
||||
export function* getSupportLinks(translator) {
|
||||
yield* getLink(supportLinks(translator), 'all');
|
||||
}
|
||||
|
@ -268,6 +268,22 @@ const escapeChars = {
|
||||
|
||||
export const escapeHtml = (text) => text.replace(/[&<>"]/g, (tag) => escapeChars[tag] || tag);
|
||||
|
||||
export const escapeControlSymbols = (text) => {
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
// a backtick is used because browsers replace backslashes
|
||||
// with forward slashes if they are not url-encoded
|
||||
return text.replaceAll(/[`&/|,]/g, '`$&');
|
||||
};
|
||||
|
||||
export const unescapeControlSymbols = (text) => {
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
return text.replaceAll(/`(.)/g, '$1');
|
||||
};
|
||||
|
||||
export const escapePronunciationString = (text) => {
|
||||
return text.replaceAll('\\', '\\\\')
|
||||
.replaceAll('/', '\\/');
|
||||
|
@ -63,6 +63,22 @@ export function listMissingTranslations(translations, baseTranslations, config)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.pronouns.slashes && keyMatches('pronouns.slashes.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.pronouns.plurals && keyMatches(
|
||||
'pronouns.plural',
|
||||
'pronouns.slashes.plural',
|
||||
'pronouns.slashes.pluralHonorific',
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.pronouns.honorifics && keyMatches('pronouns.slashes.pluralHonorific')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.pronouns.comprehensive && keyMatches('pronouns.comprehensive.')) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import translator from './translator.js';
|
||||
|
||||
// TODO make generic
|
||||
const census_groups = {
|
||||
location_poland: 'Osoby mieszkające w Polsce',
|
||||
@ -63,7 +61,7 @@ const fetchJson = (_, filename, key) => {
|
||||
return c;
|
||||
};
|
||||
|
||||
const generateToC = (content) => (_) => {
|
||||
const generateToC = (content, translator) => (_) => {
|
||||
const tags = [];
|
||||
let curentLevel = 2;
|
||||
let needsClosing = false;
|
||||
@ -99,7 +97,7 @@ const generateToC = (content) => (_) => {
|
||||
`;
|
||||
};
|
||||
|
||||
export default async function parseMarkdown(markdown) {
|
||||
export default async function parseMarkdown(markdown, translator) {
|
||||
try {
|
||||
let content = `<div>${
|
||||
markdown
|
||||
@ -127,7 +125,7 @@ export default async function parseMarkdown(markdown) {
|
||||
.replace(/{\/wide_table}/g, '</div>')
|
||||
}</div>`
|
||||
;
|
||||
content = content.replace(/{table_of_contents}/g, generateToC(content));
|
||||
content = content.replace(/{table_of_contents}/g, generateToC(content, translator));
|
||||
|
||||
const titleMatch = content.match('<h1[^>]*>(.+?)</h1>');
|
||||
const title = titleMatch ? titleMatch[1] : null;
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { deepGet } from './helpers.js';
|
||||
import config from '../data/config.suml';
|
||||
import translations from '../data/translations.suml';
|
||||
import baseTranslations from '../locale/_base/translations.suml';
|
||||
import { listMissingTranslations } from './missingTranslations.js';
|
||||
|
||||
class Translator {
|
||||
export class Translator {
|
||||
constructor(translations, baseTranslations, config) {
|
||||
this.translations = translations;
|
||||
this.baseTranslations = baseTranslations;
|
||||
@ -58,5 +55,3 @@ class Translator {
|
||||
return listMissingTranslations(this.translations, this.baseTranslations, this.config);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Translator(translations, baseTranslations, config);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import translator from '../src/translator.js';
|
||||
import { buildDict, parseUserJwt } from '../src/helpers.js';
|
||||
|
||||
export const state = () => ({
|
||||
@ -69,7 +68,7 @@ export const mutations = {
|
||||
translationPause(state) {
|
||||
state.translationMode = false;
|
||||
},
|
||||
translate(state, { key, newValue }) {
|
||||
translate(state, { translator, key, newValue }) {
|
||||
if (newValue !== translator.get(key)) {
|
||||
const translationChanges = { ...state.translationChanges };
|
||||
translationChanges[key] = newValue;
|
||||
|
@ -1,7 +1,16 @@
|
||||
import { beforeEach, describe, expect, jest, test } from '@jest/globals';
|
||||
import { mockTranslations } from './fixtures/translations.js';
|
||||
import { Translator } from '../src/translator.js';
|
||||
|
||||
mockTranslations({});
|
||||
const translations = {
|
||||
pronouns: {
|
||||
slashes: {
|
||||
plural: 'plural',
|
||||
pluralHonorific: 'plural-honorific',
|
||||
description: 'description',
|
||||
},
|
||||
},
|
||||
};
|
||||
const translator = new Translator(translations, translations, []);
|
||||
|
||||
// workaround to be independent of the current selected locale
|
||||
jest.unstable_mockModule('../data/pronouns/morphemes.js', () => {
|
||||
@ -12,7 +21,7 @@ jest.unstable_mockModule('../data/pronouns/morphemes.js', () => {
|
||||
|
||||
const { Pronoun } = await import('../src/classes.js');
|
||||
const { buildPronoun } = await import('../src/buildPronoun.js');
|
||||
const { default: pronouns } = await import('./fixtures/pronouns.js');
|
||||
const { default: pronouns, generated: generatedPronouns } = await import('./fixtures/pronouns.js');
|
||||
|
||||
beforeEach(() => {
|
||||
global.config.pronouns = {
|
||||
@ -24,13 +33,13 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
test('finds pronouns by canonical name', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'they'));
|
||||
const actual = expect(buildPronoun(pronouns, 'they', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toBe(pronouns.they);
|
||||
});
|
||||
|
||||
test('finds pronouns by alias', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'they/them'));
|
||||
const actual = expect(buildPronoun(pronouns, 'they/them', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toBe(pronouns.they);
|
||||
});
|
||||
@ -49,7 +58,7 @@ describe('when configured that emojiself pronouns are available', () => {
|
||||
});
|
||||
|
||||
test('builds pronouns from emoji', () => {
|
||||
const actual = expect(buildPronoun(pronouns, '💙'));
|
||||
const actual = expect(buildPronoun(pronouns, '💙', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'💙',
|
||||
@ -85,7 +94,7 @@ describe('when configured that null pronouns are available', () => {
|
||||
});
|
||||
|
||||
test('builds pronouns from name', () => {
|
||||
const actual = expect(buildPronoun(pronouns, ':S'));
|
||||
const actual = expect(buildPronoun(pronouns, ':S', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'S',
|
||||
@ -106,7 +115,7 @@ describe('when configured that null pronouns are available', () => {
|
||||
));
|
||||
});
|
||||
test('builds nothing if name too long', () => {
|
||||
expect(buildPronoun(pronouns, ':Abcdefghijklmnopqrstuvwxyz')).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, ':Abcdefghijklmnopqrstuvwxyz', translator)).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('with conditional placeholders', () => {
|
||||
@ -115,45 +124,85 @@ describe('when configured that null pronouns are available', () => {
|
||||
});
|
||||
|
||||
test.each(['S', 'Ringelnatz', 'Max', 'X'])('builds morpheme conditionally if name matches', (name) => {
|
||||
expect(buildPronoun(pronouns, `:${name}`).morphemes.possessive_pronoun).toBe(`${name}’`);
|
||||
expect(buildPronoun(pronouns, `:${name}`, translator).morphemes.possessive_pronoun).toBe(`${name}’`);
|
||||
});
|
||||
test.each(['Sofi', 'Xavier'])('builds morpheme by default if name does not match', (name) => {
|
||||
expect(buildPronoun(pronouns, `:${name}`).morphemes.possessive_pronoun).toBe(`${name}s`);
|
||||
expect(buildPronoun(pronouns, `:${name}`, translator).morphemes.possessive_pronoun).toBe(`${name}s`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configured that slashes contain all morphemes', () => {
|
||||
beforeEach(() => {
|
||||
global.config.pronouns.honorifics = true;
|
||||
global.config.pronouns.slashes = true;
|
||||
});
|
||||
|
||||
test('builds generated pronoun from all morphemes', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself'));
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
actual.toEqual(generatedPronouns.aer);
|
||||
});
|
||||
test('unescapes morphemes', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 's`/he/hir/hir/hirs/hirself', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.sSlashHe);
|
||||
});
|
||||
test('builds generated pronoun from all required morphemes and plural modifier', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerselves/:plural', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerPlural);
|
||||
});
|
||||
test('builds generated pronoun from all required morphemes and plural honorific modifier', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerselves/:plural-honorific', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerPluralHonorific);
|
||||
});
|
||||
test('builds generated pronoun from all required morphemes and description', () => {
|
||||
const actual = expect(buildPronoun(
|
||||
pronouns,
|
||||
'ae/aer/aer/aers/aerself/:description=Neopronoun “ae” `/ “æ”',
|
||||
translator,
|
||||
));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerWithDescription);
|
||||
});
|
||||
test('builds generated pronoun from all required morphemes and modifiers', () => {
|
||||
const actual = expect(buildPronoun(
|
||||
pronouns,
|
||||
'ae/aer/:description=Neopronoun “ae” `/ “æ”/:plural/aer/aers/aerselves',
|
||||
translator,
|
||||
));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerPluralWithDescription);
|
||||
});
|
||||
test('builds generated pronoun with some morphemes empty', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/ /aerself', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerWithEmptyPossessivePronoun);
|
||||
});
|
||||
test('builds generated pronoun with morpheme at end empty', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aers/', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerWithEmptyReflexive);
|
||||
});
|
||||
test('builds generated pronoun with some morphemes unset', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/~/aerself', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aerWithUnsetPossessivePronoun);
|
||||
});
|
||||
test('builds nothing if morphemes are missing', () => {
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aerself')).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aerself', translator)).toBeUndefined();
|
||||
});
|
||||
test('builds nothing if too many morphemes are given', () => {
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself/aer')).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself/aer', translator)).toBeUndefined();
|
||||
});
|
||||
test('builds nothing if description too long', () => {
|
||||
expect(buildPronoun(
|
||||
pronouns,
|
||||
'ae/aer/aer/aers/aerself/:description=Neopronoun “ae” `/ “æ” which is my favorite so you should use it too',
|
||||
translator,
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
describe('when configured that slashes contain some morphemes', () => {
|
||||
@ -162,31 +211,15 @@ describe('when configured that slashes contain some morphemes', () => {
|
||||
});
|
||||
|
||||
test('builds generated pronoun from all required morphemes', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aerself'));
|
||||
const actual = expect(buildPronoun(pronouns, 'ae/aer/aer/aerself', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: null,
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
));
|
||||
actual.toEqual(generatedPronouns.aerWithUnsetPossessivePronoun);
|
||||
});
|
||||
test('builds nothing if morphemes are missing', () => {
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer')).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer', translator)).toBeUndefined();
|
||||
});
|
||||
test('builds nothing if too many morphemes are given', () => {
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself')).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, 'ae/aer/aer/aers/aerself', translator)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
describe('when configured that slashes cannot contain morphemes', () => {
|
||||
@ -199,56 +232,56 @@ describe('when configured that slashes cannot contain morphemes', () => {
|
||||
test.each([3, 4, 5, 6])('builds nothing if %d morphemes are given', (count) => {
|
||||
const path = pathBase.split('/').slice(0, count)
|
||||
.join('/');
|
||||
expect(buildPronoun(pronouns, path.slice(0, count))).toBeUndefined();
|
||||
expect(buildPronoun(pronouns, path.slice(0, count), translator)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
test('builds generated pronouns from commas', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself,0,'));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'ae',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
));
|
||||
});
|
||||
test('builds generated pronouns from commas with compressed parts', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'they,!2,aers,!2,'));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'they',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'they',
|
||||
pronoun_object: 'them',
|
||||
possessive_determiner: 'their',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'themselves',
|
||||
},
|
||||
[true],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
));
|
||||
describe('building generated pronouns from commas', () => {
|
||||
test('succeeds with all parts present', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself,0,', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aer);
|
||||
});
|
||||
test('succeeds with description missing present', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself,0', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(generatedPronouns.aer);
|
||||
});
|
||||
test('succeeds with base pronoun and some custom morphemes', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'they,!2,aers,!2,', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'they/them',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'they',
|
||||
pronoun_object: 'them',
|
||||
possessive_determiner: 'their',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'themselves',
|
||||
},
|
||||
[true],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
));
|
||||
});
|
||||
test('fails when too few parts are given', () => {
|
||||
expect(buildPronoun(pronouns, 'ae, translator,aer,aer,aers,aerself', translator)).toBeUndefined();
|
||||
});
|
||||
test('fails when many few parts are given', () => {
|
||||
expect(buildPronoun(pronouns, 'ae, translator,aer,aer,aers,aerself,aersing,0,', translator)).toBeUndefined();
|
||||
});
|
||||
test('fails when base pronoun is unknown', () => {
|
||||
expect(buildPronoun(pronouns, 's/he, translator,!2,aers,!2,', translator)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('builds interchangable pronouns from ampersand', () => {
|
||||
test('two interchangable pronouns', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'he&she'));
|
||||
const actual = expect(buildPronoun(pronouns, 'he&she', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'he&she',
|
||||
@ -269,7 +302,7 @@ describe('builds interchangable pronouns from ampersand', () => {
|
||||
));
|
||||
});
|
||||
test('three interchangable pronouns', () => {
|
||||
const actual = expect(buildPronoun(pronouns, 'he&she&they'));
|
||||
const actual = expect(buildPronoun(pronouns, 'he&she&they', translator));
|
||||
actual.toBeDefined();
|
||||
actual.toEqual(new Pronoun(
|
||||
'he&she&they',
|
||||
|
@ -1,19 +1,106 @@
|
||||
import { describe, expect, test } from '@jest/globals';
|
||||
import { beforeEach, describe, expect, test } from '@jest/globals';
|
||||
|
||||
import { MergedPronounGroup, PronounGroup, PronounLibrary } from '../src/classes.js';
|
||||
import { mockTranslations } from './fixtures/translations.js';
|
||||
import { Example, ExamplePart, MergedPronounGroup, PronounGroup, PronounLibrary } from '../src/classes.js';
|
||||
import { Translator } from '../src/translator.js';
|
||||
|
||||
const translations = {
|
||||
pronouns: {
|
||||
any: {
|
||||
short: 'any',
|
||||
},
|
||||
slashes: {
|
||||
plural: 'plural',
|
||||
pluralHonorific: 'plural-honorific',
|
||||
description: 'description',
|
||||
},
|
||||
},
|
||||
};
|
||||
mockTranslations(translations);
|
||||
const translator = new Translator(translations, translations, []);
|
||||
|
||||
const { default: pronouns } = await import('./fixtures/pronouns.js');
|
||||
const { default: translator } = await import('../src/translator.js');
|
||||
const { default: pronouns, generated: generatedPronouns } = await import('./fixtures/pronouns.js');
|
||||
|
||||
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);
|
||||
|
||||
test('are present if all morphemes are present', () => {
|
||||
expect(example.requiredMorphemesPresent(pronouns.they)).toBe(true);
|
||||
});
|
||||
test('are present even if one morpheme is empty', () => {
|
||||
expect(example.requiredMorphemesPresent(generatedPronouns.aerWithEmptyPossessivePronoun)).toBe(true);
|
||||
});
|
||||
test('are missing if one morpheme is null', () => {
|
||||
expect(example.requiredMorphemesPresent(generatedPronouns.aerWithUnsetPossessivePronoun)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatting a pronoun with slashes', () => {
|
||||
describe('when slashes are not configured', () => {
|
||||
beforeEach(() => {
|
||||
global.config.pronouns = {};
|
||||
});
|
||||
|
||||
test('yields no result', () => {
|
||||
expect(generatedPronouns.aer.toStringSlashes(translator)).toBeNull();
|
||||
});
|
||||
});
|
||||
describe('when configured that slashes contain all morphemes', () => {
|
||||
beforeEach(() => {
|
||||
global.config.pronouns = { slashes: true };
|
||||
});
|
||||
|
||||
test('chunks contain all morphemes', () => {
|
||||
expect(generatedPronouns.aer.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/aerself');
|
||||
});
|
||||
test('morphemes with control symbols gets escaped', () => {
|
||||
expect(generatedPronouns.sSlashHe.toStringSlashes(translator))
|
||||
.toEqual('s`/he/hir/hir/hirs/hirself');
|
||||
});
|
||||
test('empty morphemes receive space as placeholder', () => {
|
||||
expect(generatedPronouns.aerWithEmptyPossessivePronoun.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/ /aerself');
|
||||
});
|
||||
test('empty morphemes at end receive url-encoded space as placeholder', () => {
|
||||
expect(generatedPronouns.aerWithEmptyReflexive.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/%20');
|
||||
});
|
||||
test('unset morphemes receive tilde as placeholder', () => {
|
||||
expect(generatedPronouns.aerWithUnsetPossessivePronoun.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/~/aerself');
|
||||
});
|
||||
test('adds plural modifier if necessary', () => {
|
||||
expect(generatedPronouns.aerPlural.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/aerselves/:plural');
|
||||
});
|
||||
test('adds plural honorific modifier if necessary', () => {
|
||||
expect(generatedPronouns.aerPluralHonorific.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/aerselves/:plural-honorific');
|
||||
});
|
||||
test('adds escaped description if necessary', () => {
|
||||
expect(generatedPronouns.aerWithDescription.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/aerself/:description=Neopronoun “ae” `/ “æ”');
|
||||
});
|
||||
test('adds multiple modifiers if necessary', () => {
|
||||
expect(generatedPronouns.aerPluralWithDescription.toStringSlashes(translator))
|
||||
.toEqual('ae/aer/aer/aers/aerselves/:plural/:description=Neopronoun “ae” `/ “æ”');
|
||||
});
|
||||
});
|
||||
describe('when configured that slashes contain some morphemes', () => {
|
||||
beforeEach(() => {
|
||||
global.config.pronouns = {
|
||||
slashes: ['pronoun_subject', 'pronoun_object', 'possessive_determiner', 'reflexive'],
|
||||
};
|
||||
});
|
||||
|
||||
test('chunks contain configured morphemes', () => {
|
||||
expect(generatedPronouns.aer.toStringSlashes(translator)).toEqual('ae/aer/aer/aerself');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when merging pronoun groups by key', () => {
|
||||
test('groups without keys are not assigned a merged group', () => {
|
||||
|
184
test/fixtures/pronouns.js
vendored
184
test/fixtures/pronouns.js
vendored
@ -14,11 +14,8 @@ const he = new Pronoun(
|
||||
[false],
|
||||
[false],
|
||||
['he/him'],
|
||||
'',
|
||||
null,
|
||||
true,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
);
|
||||
|
||||
const she = new Pronoun(
|
||||
@ -35,11 +32,8 @@ const she = new Pronoun(
|
||||
[false],
|
||||
[false],
|
||||
['she/her'],
|
||||
'',
|
||||
null,
|
||||
true,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
);
|
||||
|
||||
const they = new Pronoun(
|
||||
@ -56,11 +50,10 @@ const they = new Pronoun(
|
||||
[true],
|
||||
[true],
|
||||
['they/them'],
|
||||
'',
|
||||
null,
|
||||
true,
|
||||
'',
|
||||
'themselves',
|
||||
'',
|
||||
null,
|
||||
'reflexive',
|
||||
);
|
||||
|
||||
export default {
|
||||
@ -68,3 +61,170 @@ export default {
|
||||
she,
|
||||
they,
|
||||
};
|
||||
|
||||
const aer = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerPlural = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerselves',
|
||||
},
|
||||
[true],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerPluralHonorific = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerselves',
|
||||
},
|
||||
[false],
|
||||
[true],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerWithDescription = new Pronoun(
|
||||
'ae/aer',
|
||||
'Neopronoun “ae” / “æ”',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerPluralWithDescription = new Pronoun(
|
||||
'ae/aer',
|
||||
'Neopronoun “ae” / “æ”',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: 'aerselves',
|
||||
},
|
||||
[true],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerWithEmptyPossessivePronoun = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: '',
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerWithEmptyReflexive = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: 'aers',
|
||||
reflexive: '',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
const aerWithUnsetPossessivePronoun = new Pronoun(
|
||||
'ae/aer',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 'ae',
|
||||
pronoun_object: 'aer',
|
||||
possessive_determiner: 'aer',
|
||||
possessive_pronoun: null,
|
||||
reflexive: 'aerself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
|
||||
const sSlashHe = new Pronoun(
|
||||
's/he/hir',
|
||||
'',
|
||||
false,
|
||||
{
|
||||
pronoun_subject: 's/he',
|
||||
pronoun_object: 'hir',
|
||||
possessive_determiner: 'hir',
|
||||
possessive_pronoun: 'hirs',
|
||||
reflexive: 'hirself',
|
||||
},
|
||||
[false],
|
||||
[false],
|
||||
[],
|
||||
'__generator__',
|
||||
false,
|
||||
);
|
||||
|
||||
export const generated = {
|
||||
aer,
|
||||
aerPlural,
|
||||
aerPluralHonorific,
|
||||
aerWithDescription,
|
||||
aerPluralWithDescription,
|
||||
aerWithEmptyPossessivePronoun,
|
||||
aerWithEmptyReflexive,
|
||||
aerWithUnsetPossessivePronoun,
|
||||
sSlashHe,
|
||||
};
|
||||
|
15
test/fixtures/translations.js
vendored
15
test/fixtures/translations.js
vendored
@ -1,15 +0,0 @@
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
|
||||
export const mockTranslations = (translations) => {
|
||||
jest.unstable_mockModule(`${__dirname}/../../data/translations.suml`, () => {
|
||||
return { default: translations };
|
||||
});
|
||||
jest.unstable_mockModule(`${__dirname}/../../locale/_base/translations.suml`, () => {
|
||||
return { default: {} };
|
||||
});
|
||||
jest.unstable_mockModule(`${__dirname}/../../data/config.suml`, () => {
|
||||
return { default: {} };
|
||||
});
|
||||
};
|
@ -1,6 +1,57 @@
|
||||
import { describe, expect, test } from '@jest/globals';
|
||||
|
||||
import { convertPronunciationStringToSsml, escapePronunciationString } from '../src/helpers.js';
|
||||
import {
|
||||
escapeControlSymbols,
|
||||
unescapeControlSymbols,
|
||||
convertPronunciationStringToSsml,
|
||||
escapePronunciationString,
|
||||
} from '../src/helpers.js';
|
||||
|
||||
const controlSymbols = [
|
||||
{
|
||||
description: 'slashes',
|
||||
unescaped: 'w/o n/A',
|
||||
escaped: 'w`/o n`/A',
|
||||
},
|
||||
{
|
||||
description: 'ampersands',
|
||||
unescaped: 'me & you',
|
||||
escaped: 'me `& you',
|
||||
},
|
||||
{
|
||||
description: 'backticks',
|
||||
unescaped: '`code` in markdown',
|
||||
escaped: '``code`` in markdown',
|
||||
},
|
||||
{
|
||||
description: 'commas',
|
||||
unescaped: 'me, you',
|
||||
escaped: 'me`, you',
|
||||
},
|
||||
{
|
||||
description: 'pipes',
|
||||
unescaped: '0 | 1',
|
||||
escaped: '0 `| 1',
|
||||
},
|
||||
];
|
||||
|
||||
describe('when escaping control symbols', () => {
|
||||
test('safely handles null', () => {
|
||||
expect(escapeControlSymbols(null)).toBeNull();
|
||||
});
|
||||
test.each(controlSymbols)('$description get escaped with `', ({ unescaped, escaped }) => {
|
||||
expect(escapeControlSymbols(unescaped)).toBe(escaped);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when unescaping control symbols', () => {
|
||||
test('safely handles null', () => {
|
||||
expect(unescapeControlSymbols(null)).toBeNull();
|
||||
});
|
||||
test.each(controlSymbols)('$description get unescaped', ({ unescaped, escaped }) => {
|
||||
expect(unescapeControlSymbols(escaped)).toBe(unescaped);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when escaping pronunciation', () => {
|
||||
test.each([
|
||||
|
Loading…
x
Reference in New Issue
Block a user