Files
kycnotme/web/src/lib/pluralize.ts
2025-07-03 08:38:11 +00:00

130 lines
3.3 KiB
TypeScript

import { transformCase } from './strings'
const knownPlurals = {
is: {
singular: 'Is',
plural: 'Are',
},
service: {
singular: 'Service',
plural: 'Services',
},
user: {
singular: 'User',
plural: 'Users',
},
note: {
singular: 'Note',
plural: 'Notes',
},
result: {
singular: 'Result',
plural: 'Results',
},
request: {
singular: 'Request',
plural: 'Requests',
},
day: {
singular: 'Day',
plural: 'Days',
},
something: {
singular: 'Something',
plural: 'Somethings',
},
} as const satisfies Record<
string,
{
singular: string
plural: string
}
>
type KnownPlural = keyof typeof knownPlurals
const synonyms = {
are: 'is',
} as const satisfies Record<string, KnownPlural>
type Synonym = keyof typeof synonyms
export type KnownPluralOrSynonym = KnownPlural | Synonym
function isKnownPlural(key: string): key is KnownPlural {
return key in knownPlurals
}
function isKnownPluralSynonym(key: string): key is Synonym {
return key in synonyms
}
/**
* Formats name into singular or plural form, and case type.
*
* @param entity - Entity name or synonym.
* @param count - Number of entities.
* @param caseType - Case type to apply to the entity name.
*/
export function pluralize<T extends KnownPluralOrSynonym>(
entity: T,
count: number | null = 1,
caseType: Exclude<Parameters<typeof transformCase>[1], 'original'> = 'lower'
) {
return pluralizeGeneric(entity, count, caseType)
}
/**
* Use {@link pluralize} preferably.
*
* Formats name into singular or plural form, and case type.
* If the provided entity is not from the {@link knownPlurals} object, it will return the string with the case type applied.
*
* @param entity - Entity name or synonym.
* @param count - Number of entities.
* @param caseType - Case type to apply to the entity name.
*/
export function pluralizeGeneric<T extends KnownPluralOrSynonym>(
entity: T,
count: number | null,
caseType: Exclude<Parameters<typeof transformCase>[1], 'original'>
): string
export function pluralizeGeneric<T extends string>(
entity: T,
count: number | null,
caseType: Exclude<Parameters<typeof transformCase>[1], 'original'>
): string
export function pluralizeGeneric<T extends string>(
entity: T,
count: number | null = 1,
caseType: Exclude<Parameters<typeof transformCase>[1], 'original'> = 'lower'
): string {
const originalEntity = isKnownPluralSynonym(entity) ? synonyms[entity] : entity
if (!isKnownPlural(originalEntity)) {
console.warn(`getEntityName: Unknown entity "${originalEntity}"`)
return transformCase(originalEntity, caseType)
}
const { singular, plural } = knownPlurals[originalEntity]
return pluralizeAny(singular, plural, count, caseType)
}
/**
* Use {@link pluralize} preferably.
*
* Formats name into singular or plural form, and case type.
*
* @param singular - Singular form of the entity.
* @param plural - Plural form of the entity.
* @param count - Number of entities.
* @param caseType - Case type to apply to the entity name.
*/
export function pluralizeAny(
singular: string,
plural: string,
count: number | null = 1,
caseType: Exclude<Parameters<typeof transformCase>[1], 'original'> = 'lower'
): string {
const name = count === 1 ? singular : plural
return transformCase(name, caseType)
}