From 11d291a24fc732f18b5f8eec51ea46c3c5905e4c Mon Sep 17 00:00:00 2001 From: Valentyne Stigloher Date: Sat, 13 Jan 2024 17:30:33 +0100 Subject: [PATCH] (pronouns) enable translations for slash modifiers --- locale/_base/translations.suml | 4 ++ locale/de/translations.suml | 3 ++ locale/en/translations.suml | 3 ++ plugins/mainPronoun.js | 4 +- routes/profileEditor.vue | 6 +-- routes/pronoun.vue | 2 +- routes/pronouns.vue | 2 +- server/routes/banner.js | 7 +++- server/routes/pronouns.js | 10 ++++- src/buildPronoun.js | 34 +++++++++++---- src/classes.js | 9 ++-- src/missingTranslations.js | 16 +++++++ test/buildPronoun.test.js | 76 +++++++++++++++++++++------------- test/classes.test.js | 27 +++++++----- 14 files changed, 142 insertions(+), 61 deletions(-) diff --git a/locale/_base/translations.suml b/locale/_base/translations.suml index 0ac9f3e83..2024223d1 100644 --- a/locale/_base/translations.suml +++ b/locale/_base/translations.suml @@ -82,6 +82,10 @@ pronouns: comprehensive: simple: 'simple' comprehensive: 'comprehensive' + slashes: + plural: 'plural' + pluralHonorific: 'plural-honorific' + description: 'description' others: 'Other forms' othersRaw: 'other' or: 'or' diff --git a/locale/de/translations.suml b/locale/de/translations.suml index 1e35a01ab..4efc3905a 100644 --- a/locale/de/translations.suml +++ b/locale/de/translations.suml @@ -86,6 +86,9 @@ pronouns: comprehensive: simple: 'hรคufige' comprehensive: 'erweitert' + slashes: + plural: 'plural' + description: 'beschreibung' others: 'Andere Formen' othersRaw: 'andere' or: 'oder' diff --git a/locale/en/translations.suml b/locale/en/translations.suml index 68818d7b5..7a7df0d60 100644 --- a/locale/en/translations.suml +++ b/locale/en/translations.suml @@ -79,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' diff --git a/plugins/mainPronoun.js b/plugins/mainPronoun.js index 3c6acc41f..1ad93ab31 100644 --- a/plugins/mainPronoun.js +++ b/plugins/mainPronoun.js @@ -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') { diff --git a/routes/profileEditor.vue b/routes/profileEditor.vue index 6b65d1168..b088b401b 100644 --- a/routes/profileEditor.vue +++ b/routes/profileEditor.vue @@ -532,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; @@ -627,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); @@ -637,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'; }, diff --git a/routes/pronoun.vue b/routes/pronoun.vue index 624176015..9cb68d1e1 100644 --- a/routes/pronoun.vue +++ b/routes/pronoun.vue @@ -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 { diff --git a/routes/pronouns.vue b/routes/pronouns.vue index 7e8ef72b9..c4b6bd619 100644 --- a/routes/pronouns.vue +++ b/routes/pronouns.vue @@ -322,7 +322,7 @@ export default { return null; } - const slashes = this.selectedPronoun.toStringSlashes(); + const slashes = this.selectedPronoun.toStringSlashes(this.$translator); let link; if (this.usedBaseEquals) { diff --git a/server/routes/banner.js b/server/routes/banner.js index 8eaf76c06..b82cd9555 100644 --- a/server/routes/banner.js +++ b/server/routes/banner.js @@ -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)) { diff --git a/server/routes/pronouns.js b/server/routes/pronouns.js index a51099a8e..ffd56ccad 100644 --- a/server/routes/pronouns.js +++ b/server/routes/pronouns.js @@ -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' }); diff --git a/src/buildPronoun.js b/src/buildPronoun.js index acfaa89e9..b88509c00 100644 --- a/src/buildPronoun.js +++ b/src/buildPronoun.js @@ -58,7 +58,23 @@ const buildPronounFromTemplate = (key, template) => { ); }; -const buildPronounFromSlashes = (config, 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(/(? { const morphemeChunks = []; for (const chunk of chunks) { if (chunk.startsWith(':')) { - if (config.pronouns.plurals && chunk === ':plural') { + if (config.pronouns.plurals && isModifier(chunk, 'pronouns.slashes.plural', translator)) { plural = true; - } else if (config.pronouns.plurals && config.pronouns.honorifics && chunk === ':plural-honorific') { + } else if (config.pronouns.plurals && config.pronouns.honorifics && + isModifier(chunk, 'pronouns.slashes.pluralHonorific', translator)) { pluralHonorific = true; } else { - const descriptionModifierPrefix = ':description='; - if (chunk.startsWith(descriptionModifierPrefix)) { - description = unescapeControlSymbols(chunk.substring(descriptionModifierPrefix.length)); + const descriptionModifierValue = + extractModifierValue(chunk, 'pronouns.slashes.description', translator); + if (descriptionModifierValue) { + description = unescapeControlSymbols(descriptionModifierValue); } } } else { @@ -112,7 +130,7 @@ const buildPronounFromSlashes = (config, path) => { } }; -export const buildPronoun = (pronouns, path) => { +export const buildPronoun = (pronouns, path, translator) => { if (!path) { return null; } @@ -180,7 +198,7 @@ export const buildPronoun = (pronouns, path) => { } if (!pronoun && config.pronouns.slashes !== false) { - return buildPronounFromSlashes(config, path); + return buildPronounFromSlashes(config, path, translator); } return pronoun; diff --git a/src/classes.js b/src/classes.js index 5bfe8ad0f..82b22ba7a 100644 --- a/src/classes.js +++ b/src/classes.js @@ -467,7 +467,7 @@ export class Pronoun { return this.toArray().join(','); } - toStringSlashes() { + toStringSlashes(translator) { if (!config.pronouns.slashes) { return null; } @@ -490,13 +490,14 @@ export class Pronoun { }); if (this.plural[0]) { - chunks.push(':plural'); + chunks.push(`:${translator.translate('pronouns.slashes.plural')}`); } if (this.pluralHonorific[0]) { - chunks.push(':plural-honorific'); + chunks.push(`:${translator.translate('pronouns.slashes.pluralHonorific')}`); } if (this.description) { - chunks.push(`:description=${escapeControlSymbols(this.description)}`); + const escapedDescription = escapeControlSymbols(this.description); + chunks.push(`:${translator.translate('pronouns.slashes.description')}=${escapedDescription}`); } // encode a trailing space so that it does not get removed during a request diff --git a/src/missingTranslations.js b/src/missingTranslations.js index 0b96d0822..43f824c7f 100644 --- a/src/missingTranslations.js +++ b/src/missingTranslations.js @@ -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; } diff --git a/test/buildPronoun.test.js b/test/buildPronoun.test.js index 83d62f2d8..c8f2b435a 100644 --- a/test/buildPronoun.test.js +++ b/test/buildPronoun.test.js @@ -1,4 +1,16 @@ import { beforeEach, describe, expect, jest, test } from '@jest/globals'; +import { Translator } from '../src/translator.js'; + +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', () => { @@ -21,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); }); @@ -46,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( '๐Ÿ’™', @@ -82,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', @@ -103,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', () => { @@ -112,10 +124,10 @@ 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`); }); }); }); @@ -127,27 +139,31 @@ describe('when configured that slashes contain all morphemes', () => { }); 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(generatedPronouns.aer); }); test('unescapes morphemes', () => { - const actual = expect(buildPronoun(pronouns, 's`/he/hir/hir/hirs/hirself')); + 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')); + 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')); + 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โ€ `/ โ€œรฆโ€')); + const actual = expect(buildPronoun( + pronouns, + 'ae/aer/aer/aers/aerself/:description=Neopronoun โ€œaeโ€ `/ โ€œรฆโ€', + translator, + )); actual.toBeDefined(); actual.toEqual(generatedPronouns.aerWithDescription); }); @@ -155,35 +171,37 @@ describe('when configured that slashes contain all morphemes', () => { 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')); + 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/')); + 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')); + 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(); }); }); @@ -193,15 +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(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', () => { @@ -214,23 +232,23 @@ 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(); }); }); describe('building generated pronouns from commas', () => { test('succeeds with all parts present', () => { - const actual = expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself,0,')); + 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')); + 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,')); + const actual = expect(buildPronoun(pronouns, 'they,!2,aers,!2,', translator)); actual.toBeDefined(); actual.toEqual(new Pronoun( 'they/them', @@ -251,19 +269,19 @@ describe('building generated pronouns from commas', () => { )); }); test('fails when too few parts are given', () => { - expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself')).toBeUndefined(); + expect(buildPronoun(pronouns, 'ae, translator,aer,aer,aers,aerself', translator)).toBeUndefined(); }); test('fails when many few parts are given', () => { - expect(buildPronoun(pronouns, 'ae,aer,aer,aers,aerself,aersing,0,')).toBeUndefined(); + 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,!2,aers,!2,')).toBeUndefined(); + 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', @@ -284,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', diff --git a/test/classes.test.js b/test/classes.test.js index b46310a2d..89908c028 100644 --- a/test/classes.test.js +++ b/test/classes.test.js @@ -8,6 +8,11 @@ const translations = { any: { short: 'any', }, + slashes: { + plural: 'plural', + pluralHonorific: 'plural-honorific', + description: 'description', + }, }, }; const translator = new Translator(translations, translations, []); @@ -39,7 +44,7 @@ describe('formatting a pronoun with slashes', () => { }); test('yields no result', () => { - expect(generatedPronouns.aer.toStringSlashes()).toBeNull(); + expect(generatedPronouns.aer.toStringSlashes(translator)).toBeNull(); }); }); describe('when configured that slashes contain all morphemes', () => { @@ -48,39 +53,39 @@ describe('formatting a pronoun with slashes', () => { }); test('chunks contain all morphemes', () => { - expect(generatedPronouns.aer.toStringSlashes()) + expect(generatedPronouns.aer.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerself'); }); test('morphemes with control symbols gets escaped', () => { - expect(generatedPronouns.sSlashHe.toStringSlashes()) + expect(generatedPronouns.sSlashHe.toStringSlashes(translator)) .toEqual('s`/he/hir/hir/hirs/hirself'); }); test('empty morphemes receive space as placeholder', () => { - expect(generatedPronouns.aerWithEmptyPossessivePronoun.toStringSlashes()) + 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()) + expect(generatedPronouns.aerWithEmptyReflexive.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/%20'); }); test('unset morphemes receive tilde as placeholder', () => { - expect(generatedPronouns.aerWithUnsetPossessivePronoun.toStringSlashes()) + expect(generatedPronouns.aerWithUnsetPossessivePronoun.toStringSlashes(translator)) .toEqual('ae/aer/aer/~/aerself'); }); test('adds plural modifier if necessary', () => { - expect(generatedPronouns.aerPlural.toStringSlashes()) + expect(generatedPronouns.aerPlural.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural'); }); test('adds plural honorific modifier if necessary', () => { - expect(generatedPronouns.aerPluralHonorific.toStringSlashes()) + expect(generatedPronouns.aerPluralHonorific.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural-honorific'); }); test('adds escaped description if necessary', () => { - expect(generatedPronouns.aerWithDescription.toStringSlashes()) + expect(generatedPronouns.aerWithDescription.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerself/:description=Neopronoun โ€œaeโ€ `/ โ€œรฆโ€'); }); test('adds multiple modifiers if necessary', () => { - expect(generatedPronouns.aerPluralWithDescription.toStringSlashes()) + expect(generatedPronouns.aerPluralWithDescription.toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural/:description=Neopronoun โ€œaeโ€ `/ โ€œรฆโ€'); }); }); @@ -92,7 +97,7 @@ describe('formatting a pronoun with slashes', () => { }); test('chunks contain configured morphemes', () => { - expect(generatedPronouns.aer.toStringSlashes()).toEqual('ae/aer/aer/aerself'); + expect(generatedPronouns.aer.toStringSlashes(translator)).toEqual('ae/aer/aer/aerself'); }); }); });