PronounsPage/locale/config.ts

1055 lines
26 KiB
TypeScript

import type { Category } from '#shared/classes.ts';
import type { GrammarTablesDefinition } from '#shared/language/grammarTables.ts';
import type { VoiceKey } from '#shared/pronunciation/voices.ts';
import type { LocaleCode } from '~~/locale/locales.ts';
export type Toggable<T> = ({ enabled: true } & T) | { enabled: false } & Partial<T>;
export interface Config {
/**
* language code
*/
locale: LocaleCode;
/**
* language code used for formatting
* @default locale
*/
intlLocale?: string;
style: StyleConfig;
format?: FormatConfig;
/**
* age limit for users
* @default 13
*/
ageLimit?: number;
header: boolean;
/**
* disables translation mode
* @default false
*/
disableTranslationProposals?: boolean;
pronouns: Toggable<PronounsConfig>;
pronunciation?: Toggable<PronunciationConfig>;
sources: Toggable<SourcesConfig>;
nouns: Toggable<NounsConfig>;
community?: CommunityConfig;
inclusive: Toggable<InclusiveConfig>;
terminology: Toggable<TerminologyConfig>;
names: Toggable<NamesConfig>;
people: Toggable<PeopleConfig>;
english: Toggable<EnglishConfig>;
faq: Toggable<FaqConfig>;
links: Toggable<LinksConfig>;
contact: Toggable<ContactConfig>;
workshops?: Toggable<WorkshopsConfig>;
support: Toggable<SupportConfig>;
user: Toggable<UserConfig>;
profile: Toggable<ProfileConfig>;
calendar?: Toggable<CalendarConfig>;
census: Toggable<CensusConfig>;
blog?: BlogConfig;
ads?: Toggable<AdsConfig>;
redirects: RedirectConfig[];
api?: Toggable<ApiConfig>;
/**
* Configuration to be set if this locale represents a macrolanguage. A
* macrolanguage is a language that has multiple individual languages that
* are considered to be variations of the macrolanguage.
*
* For example, written Norwegian is a macrolanguage that includes Bokmål
* and Nynorsk.
*/
macrolanguage?: MacrolanguageConfig;
}
/**
* Configuration for macrolanguages. A macrolanguage is a language that
* has multiple individual languages that are considered to be variations of the
* macrolanguage.
*
* For example, written Norwegian is a macrolanguage that includes Bokmål and
* Nynorsk.
*/
export interface MacrolanguageConfig {
/**
* Whether the locale is a macrolanguage.
*/
enabled?: boolean;
/**
* Which individual languages are part of the macrolanguage. These will be
* used to generate the list of individual languages on the language
* switcher.
*/
languages?: IndividualLanguageConfig[];
}
/**
* Configuration for individual languages that are part of a macrolanguage.
*/
export interface IndividualLanguageConfig {
/**
* The language code of the individual language.
*/
code: LocaleCode;
/**
* The language codes that should be used for the individual language. This
* is used to redirect the user to the correct language when they visit the
* macrolanguage's route, based on their browser's Accept-Language header.
* If omitted, the user will not be automatically redirected, and will have
* to manually select the individual language.
*
* @example
* ```yaml
* languages:
* -
* code: 'nb'
* matches: ['nb', 'nob', 'nb-NO']
* -
* code: 'nn'
* matches: ['nn', 'nno', 'nn-NO']
* ```
*/
matches?: string[];
}
export type ConfigModule = 'pronouns' | 'pronunciation' | 'sources' | 'nouns' | 'inclusive' | 'terminology' | 'names'
| 'people' | 'english' | 'faq' | 'links' | 'contact' | 'workshops' | 'support' | 'user' | 'profile' | 'calendar'
| 'census' | 'ads';
export type ConfigWithEnabled<M extends ConfigModule> = Config & {
[K in M]: { enabled: true };
};
export const isEnabled = <M extends ConfigModule>(config: Config, module: M): config is ConfigWithEnabled<M> => {
return !!config[module]?.enabled;
};
interface FormatConfig {
timezone: string;
}
interface StyleConfig {
/**
* text direction
* @default "ltr"
*/
dir?: 'ltr' | 'rtl';
/**
* fonts used in headings, sorted by priority
*/
fontHeadings: string[];
/**
* fonts used in texts, sorted by priority
*/
fontText: string[];
}
export interface PronounsConfig {
/**
* route path for the pronouns list page (translated)
*/
route: string;
/**
* prefix to the route path of an individual pronoun (translated)
*/
prefix?: string;
/**
* available morpheme identifiers for a pronoun,
* typically named after their grammatical function in english snake_case
*/
morphemes: string[];
/**
* grammar table definitions to show morphemes in a compact way
*/
grammarTables?: GrammarTablesDefinition;
/**
* route path for any pronoun usage (translated)
* `false` means that it is disabled
*/
any: string | false;
/**
* when present, enables a switch between a simple view for a pronoun (reduced count of morphemes)
* and a comprehensive one (nearly all morphemes).
* value describes the query parameter added to the pronoun route path (translated)
*/
comprehensive?: string;
/**
* whether pronouns exists which are considered plural and thus alter example sentences
*/
plurals: boolean;
/**
* whether pronouns exists which are considered honorifics and thus alter example sentences
*/
honorifics: boolean;
/**
* how to group example sentences. if not present, all example sentences are shown
*/
exampleCategories?: ExampleCategory[];
/**
* configuration for generated pronouns
*/
generator: Toggable<PronounsGeneratorConfig>;
/**
* configuration for multiple pronouns
*/
multiple: MultiplePronounsConfig;
/**
* whether null pronouns are explained or can be generated.
* `false` means that explanation and generation are disabled
*/
null: NullPronounsConfig | false;
/**
* whether and how emoji pronouns can be generated.
* `false` means that generation is disabled
*/
emoji: EmojiPronounsConfig | false;
/**
* whether and how mirror pronouns are explained.
* `false` or absent means that explanation is disabled
*/
mirror?: MirrorPronounsConfig | false;
/**
* whether asking for pronouns are explained.
* `false` or absent means that explanation is disabled
*/
ask?: AskPronounsConfig | false;
/**
* group name for pronouns which have no assigned group (translated)
*/
others?: string;
/**
* overwrites number of morphemes displayed in the pronoun short
* @default 2
*/
shortMorphemes?: number;
/**
* configure subdomains and paths so that the link reads like a sentence
*/
sentence?: PronounsSentenceConfig;
}
interface ExampleCategory {
/**
* short description of the category (translated)
*/
name: string;
/**
* which morphemes belong to this category. visible example sentences will contain at least one listed morpheme.
* if not present, example categories must be assigned to examples using the comma-separated "categories" column in examples.tsv
*/
morphemes?: string[];
/**
* whether this category is only shown in comprehensive view
* @default false
*/
comprehensive?: boolean;
}
interface PronounsGeneratorConfig {
/**
* pronoun which is selected at the beginning.
*/
startPronoun: string;
/**
* whether pronouns can be generated via passing morphemes seperated by slashes.
* `false` means that generation is disabled.
* `true` means that all morphemes of this locale are used in their declared order.
* `string[]` means that only the configured morphemes can be used, enabling to reorder and exclude some morphemes.
*/
slashes: boolean | string[];
/**
* whether the generator is opened by default on the pronouns list page
* @default false
*/
autoOpen?: boolean;
/**
* whether descriptions for generated pronouns are displayed
* @default true
*/
description?: boolean;
/**
* whether the disclaimer for generated pronouns is displayed
* @default true
*/
disclaimer?: boolean;
}
interface MultiplePronounsConfig {
/**
* short description in the header (translated)
*/
name: string;
/**
* longer description as text (translated)
*/
description: string;
/**
* examples for multiple pronouns, typically multiple base pronouns joined with `&`
*/
examples: string[];
}
export type NullPronounsConfig = {
/**
* route paths for null pronoun usage (translated).
* when empty, disables page
* @default []
*/
routes: string[];
/**
* different strategies of avoiding pronouns
*/
ideas?: NullPronounsIdea[];
} & ({
/**
* when present, defines template for generating a pronoun from a name. use `#` as template character.
* when absent, generation is disabled
*/
morphemes?: Record<string, string>;
/**
* examples for null pronouns, typically names or initials prefixed with `:`
*/
examples: string[];
} | {
morphemes?: never;
examples?: never;
});
interface NullPronounsIdea {
/**
* short description of a strategy to avoid pronouns (translated)
*/
header: string;
/**
* long description as text (translated)
*/
description?: string;
/**
* whether this pronoun avoiding strategy is considered to be of normative use
* @default false
*/
normative?: boolean;
/**
* examples for a pronoun avoiding strategy, the first is a sentence using pronouns,
* the second one is the same sentences, but with the pronoun avoiding strategy applied
*/
examples: [string, string][];
}
interface EmojiPronounsConfig {
/**
* short description in the header (translated)
*/
description: string;
/**
* longer description as text (translated)
*/
history: string;
/**
* template for generating a pronoun from a name. use `#` as template character
*/
morphemes: Record<string, string>;
/**
* examples for emoji pronouns, each has to be a single emoji character
*/
examples: string[];
}
interface MirrorPronounsConfig {
/**
* route path for mirror pronoun usage (translated)
*/
route: string;
/**
* short description in the header (translated)
*/
name: string;
/**
* longer description as text (translated)
*/
description: string;
/**
* example for mirror pronoun usage, typically a short story with several persons
*/
example: string[];
}
interface AskPronounsConfig {
/**
* route paths for ask pronouns usage (translated)
*/
routes: string[];
}
interface PronounsSentenceConfig {
/**
* subdomains added before the locale domain to begin a sentence, typically a possessive (translated)
*/
subdomains: string[];
/**
* words between the locale domain and a pronoun, typically a verb (translated)
*/
prefixes: string[];
/**
* examples for linking pronouns in a sentence
*/
examples: string[];
}
interface PronunciationConfig {
/**
* whether to treat letter graphemes as phonemes. useful when the configured locale has no matching voice
*/
ipa?: boolean;
voices: VoiceKey[];
}
interface SourcesConfig {
/**
* route path for sources (translated)
*/
route: string;
/**
* whether it is possible that users submit source entries
*/
submit: boolean;
/**
* merges pronouns to display the same source for both of them
* (key is the alternative pronoun, value is the general pronoun added in the source)
*/
mergePronouns: Record<string, string>;
/**
* additional pronoun descriptors for sources. by default only the canonical names of known pronouns are supported
*/
extraTypes?: string[];
}
interface NounsConfig {
/**
* route path for nouns (translated)
*/
route: string;
routeMain?: string;
/**
* route paths for single pages describing an approach in detail (translated)
* @default []
*/
subroutes?: string[];
hashNamespace?: string;
/**
* whether the dictionary should be collapsed by default
*/
collapsable: boolean;
/**
* whether to have a nonbinary column
*/
nonbinary?: boolean;
/**
* whether nouns have plural forms
*/
plurals: boolean;
/**
* whether plurals are required when users submit nouns entries
*/
pluralsRequired: boolean;
/**
* categories for nouns
*/
categories: Category[];
/**
* whether nouns have different declension depending on case
*/
declension?: Toggable<NounsDeclensionConfig>;
/**
* whether it is possible that users submit nouns entries
*/
submit: boolean;
/**
* whether the configured locale uses noun templates in `nouns/nounTemplates.tsv`
*/
templates: Toggable<NounTemplatesConfig>;
/**
* whether the configured locale uses noun conventions in `nouns/nounConventions.suml`
*/
conventions?: { enabled: boolean };
}
interface NounsDeclensionConfig {
/**
* whether to automatically detect used declension
* @default false
*/
detect?: boolean;
}
interface NounTemplatesConfig {
/**
* route path for noun templates (translated)
*/
route?: string;
/**
* whether a filter is shown for templates, useful when the language has several templates
* @default false
*/
filter?: boolean;
}
interface CommunityConfig {
/**
* route path for community (translated)
*/
route: string;
}
interface InclusiveConfig {
/**
* route path for inclusive terms (translated)
*/
route: string;
hashNamespace?: string;
/**
* categories for inclusive terms (translated)
*/
categories: Category[];
}
interface TerminologyConfig {
/**
* route path for terminology (translated)
*/
route: string;
hashNamespace?: string;
/**
* whether terminology are published for all users. when `false`, only users with the `terms` role can access
*/
published: boolean;
/**
* categories for terminology (translated)
*/
categories: Category[];
}
interface NamesConfig {
/**
* whether names are published for all users. when `false`, only users with the `names` role can access
*/
published: boolean;
/**
* route path for names (translated)
*/
route: string;
/**
* whether the legally option is available for a submitted name
*/
legally: boolean;
/**
* whether name counts are shown
*/
count: boolean;
/**
* how to display name counts (key is an abbreviation, value is a icon name)
*/
countSex: Record<string, string>;
/**
* display description of different name parts (key is a number, value is the description)
*/
countOrdinal: Record<string, string>;
/**
* whether namedays can be defined for a submitted name
*/
namedays: boolean;
}
interface PeopleConfig {
/**
* route path for people page (translated)
*/
route: string;
}
interface EnglishConfig {
/**
* route path for english explanation page (typically in english)
*/
route: string;
/**
* links to other pages which address the inclusive language of the configured locale in english.
* each item is a paragraph
* @default []
*/
links?: Link[];
/**
* description of some inclusive pronoun stragies
*/
pronounGroups: EnglishPronounGroup[];
}
interface EnglishPronounGroup {
/**
* short description in the header (in english)
*/
name: string;
/**
* longer description as text (in english)
*/
description: string[];
/**
* data for the EnglishTable component
*/
table?: unknown;
}
interface FaqConfig {
/**
* route path for faq (translated)
*/
route: string;
}
interface LinksConfig {
/**
* route path for faq (translated)
*/
route: string;
/**
* route path for blog (translated)
*/
blogRoute: string;
/**
* route path for academic sources (translated)
*/
academicRoute?: string;
/**
* route path for media sources (translated)
*/
mediaRoute?: string;
/**
* route path for translinguistics (translated)
*/
translinguisticsRoute?: string;
/**
* whether blog is enabled
*/
blog: boolean;
/**
* @default false
*/
split?: boolean;
splitBlog?: boolean;
links: Link[];
academic: Record<string, LinkCategory>;
mediaGuests: Link[];
mediaMentions: Link[];
recommended: Link[];
names?: Link[];
endorsements?: Record<string, Endorsement>;
zine?: Toggable<ZineConfig>;
translinguistics?: AcademicReference[];
}
interface LinkCategory {
/**
* short description of the link category (translated)
*/
name: string | null;
/**
* links of this category
*/
entries: Link[];
}
export interface Link {
/**
* locale codes of the linked resource, e.g. when the source talks in a different locale about the configured locale
*/
lang?: string[];
/**
* icon name
* @see https://fontawesome.com/v5/search
*/
icon: string;
/**
* icon style (light, solid or brand)
* @default "l"
*/
iconSet?: 'l' | 's' | 'b';
/**
* references external resource
* @format uri
*/
url?: string;
/**
* short description of the resource displayed as header (translated)
*/
headline?: string;
/**
* one or multiple quotes from the linked source (language of the resource)
* @default []
*/
quote?: string | string[];
/**
* short description of the resource displayed beside the link (translated)
* @default ""
*/
extra?: string;
/**
* response to the linked resource
*/
response?: string | string[];
}
interface Endorsement {
author: string | null;
title: string;
publisher: string;
/**
* @format uri
*/
link: string;
description: string[];
/**
* valid blog slug to a review for this publication
*/
review?: string;
/**
* dont' show the item before this date (YYYY-MM-DD)
*/
published?: string;
}
export interface AcademicReference {
/**
* year of publication
*/
year: number;
/**
* academic source reference (translated)
*/
reference: string;
/**
* abstract of the reference. each item is a paragraph (language of the reference)
*/
abstract: string[];
/**
* tags for the reference (translated)
*/
tags: string[];
/**
* languages of the reference when it differs from the configured locale
*/
lang?: string[];
}
interface ZineConfig {
/**
* route path for zine (translated)
*/
route: string;
/**
* whether submissions for zine are open
*/
open: boolean;
releases: ZineRelease[];
}
export interface ZineRelease {
/**
* title text of the zine (translated)
*/
title: string;
/**
* references an image of the cover
* @format uri
*/
cover: string;
/**
* references a horizontal image used as banner
* @format uri
*/
banner: string;
/**
* long description as text (translated)
*/
description: string;
/**
* download options
*/
downloads: Record<string, ZineDownload>;
/**
* extra information displayed as list
*/
extra: string[];
}
interface ZineDownload {
/**
* references a downloadable resource
* @format uri
*/
filename: string;
/**
* icon name
* @see https://fontawesome.com/v5/search
*/
icon: string;
}
interface ContactConfig {
/**
* route path for contact (translated)
*/
route: string;
/**
* additional authors supplementing the information from the database
* @default []
*/
authors?: ContactAuthor[];
team: Toggable<ContactTeamConfig>;
}
export type ContactAuthor = {
footerName: string;
username: string;
footerAreas: string;
} | {
footerName: string;
/**
* @format uri
*/
link: string;
group: true;
footerAreas: string;
};
interface ContactTeamConfig {
/**
* route path for team (translated)
*/
route: string;
}
interface WorkshopsConfig {
/**
* route path for workshops (translated)
*/
route: string;
/**
* @format email
*/
email: string;
}
interface SupportConfig {
enabled: boolean;
}
interface UserConfig {
/**
* route path for user account (translated)
*/
route: string;
/**
* route path for terms of service (translated)
*/
termsRoute: string;
/**
* route path for privacy notice (translated)
*/
privacyRoute: string;
}
interface ProfileEditorConfig {
editorEnabled: boolean;
/**
* default words for newly created profiles
*/
defaultWords: ProfileWords[];
/**
* default pronoun used for translating declensions in flags.*
*/
flags?: { defaultPronoun: string };
/**
* whether the limit for name lengths is extended
* `true` means 255 characters, `false` means 32 characters
* @default false
*/
longNames?: boolean;
}
type ProfileConfig = ({ editorEnabled: true } & ProfileEditorConfig) | { editorEnabled: false };
export interface ProfileWords {
/**
* short description of the category in the header (translated)
*/
header: string | null;
/**
* default words in this category (translated)
*/
values: string[];
}
interface CalendarConfig {
/**
* route path for queer calendar (translated)
*/
route: string;
/**
* declares used sources for the queer calendar
* @format uri
* @default []
*/
sources?: string[];
}
interface CensusConfig {
/**
* route path for census (translated)
*/
route: string;
/**
* key for the census run, typically the year
*/
edition: string;
/**
* date when the census opens for submissions
* @format date-time
*/
start: string;
/**
* date when the census closes for submissions
* @format date-time
*/
end: string;
/**
* which json file to use for fetching in-page stats
*/
latestResults: string;
/**
* which answers for the first question are considered to be relevant for the census
*/
relevant: string[];
/**
* questions of the census
*/
questions: CensusQuestion[];
/**
* result pages of past census (key is a valid blog slug, value is its title)
*/
results: Record<string, string | Record<string, string>>;
}
interface CensusBaseQuestion {
/**
* displayed question (translated)
*/
question: string;
/**
* whether this questions only shows depending on the answers of another question
* (referenced by its 0-indexed number)
*/
conditionalOn?: number;
/**
* which value of the referenced question will show this question
*/
conditionalValue?: string | string[];
/**
* optional instructions. each item is a paragraph (translated)
*/
instruction?: string[];
/**
* whether to add an additional writein text input
* @default false
*/
writein?: boolean;
/**
* lists options which should only show after the acception of a content warning
*/
cw?: string[];
/**
* Whether answer helpers are shown in a separate line and with formatting (default: false)
*/
expandedHelp?: boolean;
/**
* whether the answer to this question is not required
* @default false
*/
optional?: boolean;
aggregates?: Record<string, Aggregate>;
}
export interface CensusOptionsQuestion extends CensusBaseQuestion {
type: 'radio' | 'checkbox';
/**
* whether to randomise options
* @default false
*/
randomise?: boolean;
/**
* options always put first when randomising
* @default []
*/
optionsFirst?: string[][];
options: string[][];
/**
* options always put last when randomising
* @default []
*/
optionsLast?: string[][];
}
export interface CensusTextQuestion extends CensusBaseQuestion {
type: 'text' | 'textarea';
}
export interface CensusNumberQuestion extends CensusBaseQuestion {
type: 'number';
min: number;
max: number;
}
type CensusQuestion = CensusOptionsQuestion | CensusTextQuestion | CensusNumberQuestion;
export interface Aggregate {
operation: 'OR' | 'AND';
values: string[];
exclusive?: boolean;
}
interface BlogConfig {
/**
* shortcuts of blog entries directly accessible from the page route
* (key is the defined shortcut, value is a valid blog slug)
*/
shortcuts: Record<string, string>;
/**
* keep the full path (with the blog route path) for a blog entry even though a shortcut exists.
* items are valid blog slugs
* @default []
*/
keepFullPath?: string[];
}
interface AdsConfig {
enabled: boolean;
}
interface RedirectConfig {
from: string;
to: string;
}
interface ApiConfig {
/**
* route path for api documentation (translated)
*/
route: string;
/**
* example routes for the api
* (key is a predefined endpoint descriptor, value are several route paths with concrete examples)
*/
examples: Record<string, string[]>;
}