|
| 1 | +<div align="center"> |
| 2 | + |
| 3 | +[![typescript-image]][typescript-url] |
| 4 | +[![npm-image]][npm-url] |
| 5 | +[![npm-download-image]][npm-download-url] |
| 6 | +[![license-image]][license-url] |
| 7 | + |
| 8 | +</div> |
| 9 | + |
| 10 | +`@boringnode/pluralize` is a TypeScript port of the pluralization features from [Doctrine Inflector](https://github.com/doctrine/inflector). |
| 11 | + |
| 12 | +Built-in support for: |
| 13 | +- **English** (`en`, `english`) |
| 14 | +- **French** (`fr`, `french`) |
| 15 | + |
| 16 | +> [!NOTE] |
| 17 | +> Changes to inflection rules (adding, removing, or modifying rules) will not be considered as breaking changes. |
| 18 | +
|
| 19 | +## Installation |
| 20 | + |
| 21 | +```sh |
| 22 | +npm install @boringnode/pluralize |
| 23 | +``` |
| 24 | + |
| 25 | +## Usage |
| 26 | + |
| 27 | +### Simple Functions |
| 28 | + |
| 29 | +The easiest way to use the library is with the simple functions. |
| 30 | + |
| 31 | +```ts |
| 32 | +import { pluralize, singularize } from '@boringnode/pluralize' |
| 33 | + |
| 34 | +pluralize('word') // 'words' |
| 35 | +pluralize('person') // 'people' |
| 36 | +pluralize('child') // 'children' |
| 37 | + |
| 38 | +singularize('words') // 'word' |
| 39 | +singularize('people') // 'person' |
| 40 | +singularize('children') // 'child' |
| 41 | +``` |
| 42 | + |
| 43 | +### Using a Different Locale |
| 44 | + |
| 45 | +You can create an `Inflector` instance with a different locale. |
| 46 | + |
| 47 | +```ts |
| 48 | +import { Inflector } from '@boringnode/pluralize' |
| 49 | + |
| 50 | +const inflector = new Inflector('fr') |
| 51 | + |
| 52 | +inflector.pluralize('cheval') // 'chevaux' |
| 53 | +inflector.pluralize('bijou') // 'bijoux' |
| 54 | +inflector.singularize('chevaux') // 'cheval' |
| 55 | +``` |
| 56 | + |
| 57 | +### Custom Rules with Inflector Class |
| 58 | + |
| 59 | +You can create an `Inflector` instance to add custom rules. |
| 60 | + |
| 61 | +```ts |
| 62 | +import { Inflector } from '@boringnode/pluralize' |
| 63 | + |
| 64 | +const inflector = new Inflector() |
| 65 | + .addIrregular('gex', 'gexes') |
| 66 | + .addUninflected('pokemon') |
| 67 | + .addPluralRule(/(.*)gon$/i, '$1gons') |
| 68 | + .addSingularRule(/(.*)gons$/i, '$1gon') |
| 69 | + |
| 70 | +inflector.pluralize('gex') // 'gexes' |
| 71 | +inflector.pluralize('pokemon') // 'pokemon' |
| 72 | +inflector.singularize('dragons') // 'dragon' |
| 73 | +``` |
| 74 | + |
| 75 | +### Creating a Custom Language Ruleset |
| 76 | + |
| 77 | +You can create and register custom language rulesets for other languages. |
| 78 | + |
| 79 | +```ts |
| 80 | +import { Inflector, type LanguageRuleset } from '@boringnode/pluralize' |
| 81 | +import { |
| 82 | + Ruleset, |
| 83 | + Transformations, |
| 84 | + Transformation, |
| 85 | + Patterns, |
| 86 | + Pattern, |
| 87 | + Substitutions, |
| 88 | + Substitution, |
| 89 | +} from '@boringnode/pluralize/builder' |
| 90 | + |
| 91 | +const FrenchRuleset: LanguageRuleset = { |
| 92 | + getSingularRuleset: () => |
| 93 | + new Ruleset( |
| 94 | + new Transformations( |
| 95 | + new Transformation(new Pattern('aux$'), 'al'), |
| 96 | + new Transformation(new Pattern('s$'), '') |
| 97 | + ), |
| 98 | + new Patterns(new Pattern('cas'), new Pattern('os')), |
| 99 | + new Substitutions(new Substitution('yeux', 'oeil')) |
| 100 | + ), |
| 101 | + getPluralRuleset: () => |
| 102 | + new Ruleset( |
| 103 | + new Transformations( |
| 104 | + new Transformation(new Pattern('al$'), 'aux'), |
| 105 | + new Transformation(new Pattern('$'), 's') |
| 106 | + ), |
| 107 | + new Patterns(new Pattern('cas'), new Pattern('os')), |
| 108 | + new Substitutions(new Substitution('oeil', 'yeux')) |
| 109 | + ), |
| 110 | +} |
| 111 | + |
| 112 | +// Register the ruleset |
| 113 | +Inflector.register('fr', FrenchRuleset) |
| 114 | + |
| 115 | +// Use it |
| 116 | +const inflector = new Inflector('fr') |
| 117 | +inflector.pluralize('cheval') // 'chevaux' |
| 118 | +inflector.singularize('chevaux') // 'cheval' |
| 119 | +``` |
| 120 | + |
| 121 | +## API |
| 122 | + |
| 123 | +### Functions |
| 124 | + |
| 125 | +- `pluralize(word: string): string` - Returns the plural form of a word |
| 126 | +- `singularize(word: string): string` - Returns the singular form of a word |
| 127 | + |
| 128 | +### Inflector Class |
| 129 | + |
| 130 | +| Method | Description | |
| 131 | +| --------------------------------------- | ----------------------------------------------------------- | |
| 132 | +| `new Inflector(locale?: string)` | Creates a new inflector instance (defaults to `'en'`) | |
| 133 | +| `Inflector.register(locale, ruleset)` | Registers a custom language ruleset | |
| 134 | +| `pluralize(word)` | Returns the plural form of a word | |
| 135 | +| `singularize(word)` | Returns the singular form of a word | |
| 136 | +| `addIrregular(singular, plural)` | Adds an irregular word mapping | |
| 137 | +| `addUninflected(word)` | Adds a word that doesn't change between singular and plural | |
| 138 | +| `addPluralRule(pattern, replacement)` | Adds a custom pluralization rule | |
| 139 | +| `addSingularRule(pattern, replacement)` | Adds a custom singularization rule | |
| 140 | + |
| 141 | +### Builder Exports (`@boringnode/pluralize/builder`) |
| 142 | + |
| 143 | +For creating custom language rulesets: |
| 144 | + |
| 145 | +| Export | Description | |
| 146 | +| ----------------- | ----------------------------------------------------- | |
| 147 | +| `Pattern` | A regex pattern for matching words | |
| 148 | +| `Patterns` | A collection of patterns for uninflected words | |
| 149 | +| `Transformation` | A pattern + replacement for regular inflection rules | |
| 150 | +| `Transformations` | A collection of transformations | |
| 151 | +| `Substitution` | A direct word-to-word mapping for irregular words | |
| 152 | +| `Substitutions` | A collection of substitutions | |
| 153 | +| `Ruleset` | Combines transformations, patterns, and substitutions | |
| 154 | + |
| 155 | +[npm-image]: https://img.shields.io/npm/v/@boringnode/pluralize.svg?style=for-the-badge&logo=npm |
| 156 | +[npm-url]: https://www.npmjs.com/package/@boringnode/pluralize |
| 157 | +[npm-download-image]: https://img.shields.io/npm/dm/@boringnode/pluralize?style=for-the-badge |
| 158 | +[npm-download-url]: https://www.npmjs.com/package/@boringnode/pluralize |
| 159 | +[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript |
| 160 | +[typescript-url]: https://www.typescriptlang.org |
| 161 | +[license-image]: https://img.shields.io/npm/l/@boringnode/pluralize?color=blueviolet&style=for-the-badge |
| 162 | +[license-url]: LICENSE.md |
0 commit comments