├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── lib ├── inflection.dart └── src │ ├── irregular_past_verbs.dart │ ├── irregular_plural_nouns.dart │ ├── irregular_plural_verbs.dart │ ├── past.dart │ ├── plural.dart │ ├── plural_verb.dart │ ├── singular.dart │ ├── singular_verb.dart │ ├── snake_case.dart │ ├── spinal_case.dart │ ├── uncountable_nouns.dart │ ├── util.dart │ └── verbs_ending_with_ed.dart ├── pubspec.yaml ├── test ├── all_test.dart ├── inflection_test.dart ├── past_test.dart ├── plural_test.dart ├── plural_verb_test.dart ├── singular_test.dart ├── singular_verb_test.dart ├── snake_case_test.dart └── spinal_case_test.dart └── tool └── travis.sh /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | docs/ 4 | .buildlog 5 | pubspec.lock 6 | packages 7 | .*.un~ 8 | .project 9 | .settings 10 | .idea 11 | .packages 12 | .dart_tool -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | script: ./tool/travis.sh 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.1 4 | 5 | - Cleanup, follow latest conventions and developments in the Dartiverse. 6 | - Added past inflections. 7 | 8 | ## 0.3.2 9 | 10 | - Cleanup. 11 | 12 | ## 0.3.1 13 | 14 | - Case conversion handles sentences delimited with whitespace. 15 | 16 | ## 0.3.0 17 | 18 | - Handle irregular plural nouns. 19 | - Added shortcut functions. 20 | 21 | ## 0.2.0 22 | 23 | - Handle uncountable nouns. 24 | - Added singular inflection. 25 | - Added plural inflection. 26 | - Refactored to use a dart:convert based API. 27 | 28 | ## 0.1.0 29 | 30 | - Initial version, convertToSnakeCase / convertToSpinalCase. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, George Moschovitis . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inflection 2 | 3 | [![Build Status](https://travis-ci.org/gmosx/dart-inflection.svg?branch=master)](https://travis-ci.org/gmosx/dart-inflection) 4 | 5 | In grammar, inflection or inflexion is the modification of a word to express 6 | different grammatical categories such as tense, mood, voice, aspect, person, 7 | number, gender and case. 8 | 9 | A port of the Rails/ActiveSupport inflector library to Dart. 10 | 11 | ## Usage 12 | 13 | A simple usage example: 14 | 15 | ```dart 16 | import 'package:inflection/inflection.dart'; 17 | 18 | main() { 19 | // Using 'shortcut' functions. 20 | 21 | print(pluralize("house")); // => "houses" 22 | print(convertToPlural("house")); // => "houses", alias for pluralize 23 | print(pluralizeVerb("goes")); // => "go" 24 | print(singularize("axes")); // => "axis" 25 | print(convertToSingular("axes")); // => "axis", alias for pluralize 26 | print(singularizeVerb("write")); // => "writes" 27 | print(convertToSnakeCase("CamelCaseName")); // => "camel_case_name" 28 | print(convertToSpinalCase("CamelCaseName")); // => "camel-case-name" 29 | print(past("forgo")); // => "forwent" 30 | 31 | // Using default encoders. 32 | 33 | print(PLURAL.convert("virus")); // => "viri" 34 | print(SINGULAR.convert("Matrices")); // => "Matrix" 35 | print(SINGULAR.convert("species")); // => "species" 36 | print(SNAKE_CASE.convert("CamelCaseName")); // => "camel_case_name" 37 | print(SPINAL_CASE.convert("CamelCaseName")); // => "camel-case-name" 38 | print(PAST.convert("miss")); // => "missed" 39 | } 40 | ``` 41 | 42 | ## Features and bugs 43 | 44 | Please file feature requests and bugs at the 45 | [issue tracker](https://github.com/gmosx/dart-inflection/issues). 46 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: 3 | implicit-casts: false -------------------------------------------------------------------------------- /lib/inflection.dart: -------------------------------------------------------------------------------- 1 | /// In grammar, inflection or inflexion is the modification of a word to express 2 | /// different grammatical categories such as tense, mood, voice, aspect, person, 3 | /// number, gender and case. 4 | /// 5 | /// [ActiveSupport Inflector](https://github.com/rails/rails/tree/master/activesupport/lib/active_support/inflector) 6 | /// [Letter case](http://en.wikipedia.org/wiki/Letter_case#Special_case_styles) 7 | library inflection; 8 | 9 | import 'src/past.dart'; 10 | import 'src/plural.dart'; 11 | import 'src/plural_verb.dart'; 12 | import 'src/singular.dart'; 13 | import 'src/singular_verb.dart'; 14 | import 'src/snake_case.dart'; 15 | import 'src/spinal_case.dart'; 16 | 17 | export 'src/past.dart'; 18 | export 'src/plural.dart'; 19 | export 'src/plural_verb.dart'; 20 | export 'src/singular.dart'; 21 | export 'src/singular_verb.dart'; 22 | export 'src/snake_case.dart'; 23 | export 'src/spinal_case.dart'; 24 | 25 | String convertToPlural(String word) => PLURAL.convert(word); 26 | String pluralize(String word) => PLURAL.convert(word); 27 | 28 | String convertToPluralVerb(String word) => PLURALVERB.convert(word); 29 | String pluralizeVerb(String word) => PLURALVERB.convert(word); 30 | 31 | String convertToSingular(String word) => SINGULAR.convert(word); 32 | String singularize(String word) => SINGULAR.convert(word); 33 | 34 | String convertToSingularVerb(String word) => SINGULARVERB.convert(word); 35 | String singularizeVerb(String word) => SINGULARVERB.convert(word); 36 | 37 | String convertToSnakeCase(String word) => SNAKE_CASE.convert(word); 38 | 39 | String convertToSpinalCase(String word) => SPINAL_CASE.convert(word); 40 | 41 | String convertToPast(String word) => PAST.convert(word); 42 | String past(String word) => PAST.convert(word); 43 | -------------------------------------------------------------------------------- /lib/src/irregular_past_verbs.dart: -------------------------------------------------------------------------------- 1 | library inflection.irregular_past_verbs; 2 | 3 | /// A collection of verbs with irregular past. 4 | final Map irregularPastVerbs = const { 5 | "backslidden": "backslid", 6 | "forbidden": "forbade", 7 | "stridden": "strode", 8 | "stricken": "struck", 9 | "brought": "brought", 10 | "striven": "strove", 11 | "swollen": "swelled", 12 | "thought": "thought", 13 | "trodden": "trod", 14 | "forbade": "forbade", 15 | "arisen": "arose", 16 | "awoken": "awoke", 17 | "beaten": "beat", 18 | "became": "became", 19 | "become": "became", 20 | "bidden": "bid", 21 | "bitten": "bit", 22 | "broken": "broke", 23 | "bought": "bought", 24 | "caught": "caught", 25 | "choose": "chose", 26 | "chosen": "chose", 27 | "forbid": "forbade", 28 | "dreamt": "dreamt", 29 | "driven": "drove", 30 | "fallen": "fell", 31 | "fought": "fought", 32 | "freeze": "froze", 33 | "frozen": "froze", 34 | "gotten": "got", 35 | "ground": "ground", 36 | "hidden": "hid", 37 | "learnt": "learnt", 38 | "proven": "proved", 39 | "sought": "sought", 40 | "shaken": "shook", 41 | "shaven": "shaved", 42 | "shrank": "shrank", 43 | "shrink": "shrank", 44 | "shrunk": "shrank", 45 | "spoken": "spoke", 46 | "spoilt": "spoilt", 47 | "spread": "spread", 48 | "sprang": "sprang", 49 | "spring": "sprang", 50 | "sprung": "sprang", 51 | "stolen": "stole", 52 | "strewn": "strewed", 53 | "strode": "strode", 54 | "stride": "strode", 55 | "strike": "struck", 56 | "strove": "strove", 57 | "struck": "struck", 58 | "string": "strung", 59 | "strung": "strung", 60 | "taught": "taught", 61 | "thrown": "threw", 62 | "thrust": "thrust", 63 | "arise": "arose", 64 | "arose": "arose", 65 | "awake": "awoke", 66 | "awoke": "awoke", 67 | "borne": "bore", 68 | "began": "began", 69 | "begin": "began", 70 | "begun": "began", 71 | "bound": "bound", 72 | "bleed": "bled", 73 | "blown": "blew", 74 | "break": "broke", 75 | "breed": "bred", 76 | "bring": "brought", 77 | "broke": "broke", 78 | "build": "built", 79 | "built": "built", 80 | "burnt": "burnt", 81 | "catch": "caught", 82 | "chose": "chose", 83 | "cling": "clung", 84 | "clung": "clung", 85 | "creep": "crept", 86 | "crept": "crept", 87 | "dealt": "dealt", 88 | "wound": "wound", 89 | "wring": "wrung", 90 | "wrung": "wrung", 91 | "write": "wrote", 92 | "wrote": "wrote", 93 | "drawn": "drew", 94 | "drank": "drank", 95 | "drink": "drank", 96 | "drunk": "drank", 97 | "drive": "drove", 98 | "drove": "drove", 99 | "dwelt": "dwelt", 100 | "eaten": "ate", 101 | "fight": "fought", 102 | "found": "found", 103 | "fling": "flung", 104 | "flung": "flung", 105 | "flown": "flew", 106 | "froze": "froze", 107 | "given": "gave", 108 | "grind": "ground", 109 | "grown": "grew", 110 | "heard": "heard", 111 | "knelt": "knelt", 112 | "known": "knew", 113 | "leant": "leant", 114 | "leapt": "leapt", 115 | "leave": "left", 116 | "meant": "meant", 117 | "risen": "rose", 118 | "shake": "shook", 119 | "shorn": "sheared", 120 | "shone": "shone", 121 | "shook": "shook", 122 | "shoot": "shot", 123 | "shown": "showed", 124 | "slain": "slew", 125 | "sleep": "slept", 126 | "slept": "slept", 127 | "slide": "slid", 128 | "sling": "slung", 129 | "slung": "slung", 130 | "slunk": "slunk", 131 | "smelt": "smelt", 132 | "snuck": "snuck", 133 | "speak": "spoke", 134 | "spelt": "spelt", 135 | "spend": "spent", 136 | "spent": "spent", 137 | "spilt": "spilt", 138 | "split": "split", 139 | "spoke": "spoke", 140 | "stand": "stood", 141 | "stood": "stood", 142 | "steal": "stole", 143 | "stick": "stuck", 144 | "stole": "stole", 145 | "stuck": "stuck", 146 | "sting": "stung", 147 | "stung": "stung", 148 | "stank": "stank", 149 | "stink": "stank", 150 | "stunk": "stank", 151 | "swear": "swore", 152 | "swore": "swore", 153 | "sworn": "swore", 154 | "sweep": "swept", 155 | "swept": "swept", 156 | "swing": "swung", 157 | "swung": "swung", 158 | "taken": "took", 159 | "teach": "taught", 160 | "think": "thought", 161 | "threw": "threw", 162 | "throw": "threw", 163 | "tread": "trod", 164 | "wake": "woke", 165 | "woken": "woke", 166 | "woven": "wove", 167 | "bear": "bore", 168 | "bore": "bore", 169 | "born": "bore", 170 | "beat": "beat", 171 | "bend": "bent", 172 | "bent": "bent", 173 | "bind": "bound", 174 | "bite": "bit", 175 | "bled": "bled", 176 | "blew": "blew", 177 | "blow": "blew", 178 | "bred": "bred", 179 | "cast": "cast", 180 | "clad": "clad", 181 | "come": "came", 182 | "cost": "cost", 183 | "deal": "dealt", 184 | "does": "did", 185 | "done": "did", 186 | "draw": "drew", 187 | "drew": "drew", 188 | "fall": "fell", 189 | "feed": "fed", 190 | "feel": "felt", 191 | "fell": "fell", 192 | "felt": "felt", 193 | "find": "found", 194 | "flee": "fled", 195 | "fled": "fled", 196 | "flew": "flew", 197 | "gave": "gave", 198 | "give": "gave", 199 | "gone": "went", 200 | "grew": "grew", 201 | "grow": "grew", 202 | "hang": "hung", 203 | "hung": "hung", 204 | "have": "had", 205 | "hear": "heard", 206 | "hewn": "hewed", 207 | "hide": "hid", 208 | "hold": "held", 209 | "held": "held", 210 | "hurt": "hurt", 211 | "keep": "kept", 212 | "kept": "kept", 213 | "knew": "knew", 214 | "know": "knew", 215 | "laid": "laid", 216 | "lead": "led", 217 | "left": "left", 218 | "lend": "lent", 219 | "lent": "lent", 220 | "lain": "lay", 221 | "lose": "lost", 222 | "lost": "lost", 223 | "make": "made", 224 | "made": "made", 225 | "mean": "meant", 226 | "meet": "met", 227 | "mown": "mowed", 228 | "paid": "paid", 229 | "pled": "pled", 230 | "read": "read", 231 | "ride": "rode", 232 | "rode": "rode", 233 | "ring": "rang", 234 | "rung": "rang", 235 | "rise": "rose", 236 | "rose": "rose", 237 | "sang": "sang", 238 | "sawn": "sawed", 239 | "said": "said", 240 | "seen": "saw", 241 | "seek": "sought", 242 | "sell": "sold", 243 | "slew": "slew", 244 | "sold": "sold", 245 | "send": "sent", 246 | "sent": "sent", 247 | "sewn": "sewed", 248 | "shed": "shed", 249 | "shot": "shot", 250 | "shut": "shut", 251 | "sing": "sang", 252 | "sung": "sang", 253 | "slid": "slid", 254 | "slit": "slit", 255 | "sown": "sowed", 256 | "sped": "sped", 257 | "spin": "spun", 258 | "spun": "spun", 259 | "spit": "spit", 260 | "spat": "spat", 261 | "swam": "swam", 262 | "swim": "swam", 263 | "swum": "swam", 264 | "take": "took", 265 | "tear": "tore", 266 | "tore": "tore", 267 | "torn": "tore", 268 | "tell": "told", 269 | "told": "told", 270 | "took": "took", 271 | "trod": "trod", 272 | "wear": "wore", 273 | "wore": "wore", 274 | "worn": "wore", 275 | "weep": "wept", 276 | "went": "went", 277 | "wept": "wept", 278 | "were": "were", 279 | "wind": "wound", 280 | "woke": "woke", 281 | "wove": "wove", 282 | "are": "were", 283 | "ate": "ate", 284 | "bet": "bet", 285 | "bid": "bid", 286 | "bit": "bit", 287 | "buy": "bought", 288 | "cut": "cut", 289 | "did": "did", 290 | "dig": "dug", 291 | "dug": "dug", 292 | "eat": "ate", 293 | "fed": "fed", 294 | "fly": "flew", 295 | "get": "got", 296 | "got": "got", 297 | "had": "had", 298 | "has": "had", 299 | "hid": "hid", 300 | "hit": "hit", 301 | "lay": "laid", 302 | "led": "led", 303 | "let": "let", 304 | "lit": "lit", 305 | "met": "met", 306 | "pay": "paid", 307 | "put": "put", 308 | "ran": "ran", 309 | "rid": "rid", 310 | "run": "ran", 311 | "saw": "saw", 312 | "say": "said", 313 | "see": "saw", 314 | "sit": "sat", 315 | "sat": "sat", 316 | "set": "set", 317 | "was": "was", 318 | "win": "won", 319 | "won": "won", 320 | "do": "did", 321 | "go": "went", 322 | "is": "was", 323 | }; 324 | -------------------------------------------------------------------------------- /lib/src/irregular_plural_nouns.dart: -------------------------------------------------------------------------------- 1 | library inflection.irregular_plural_nouns; 2 | 3 | /// A collection of nouns with irregular plurals. 4 | /// 5 | /// [A List of 100 Irregular Plural Nouns in English](http://grammar.about.com/od/words/a/A-List-Of-Irregular-Plural-Nouns-In-English.htm) 6 | final Map irregularPluralNouns = const { 7 | "person": "people", 8 | "man": "men", 9 | "child": "children", 10 | "sex": "sexes" 11 | }; 12 | -------------------------------------------------------------------------------- /lib/src/irregular_plural_verbs.dart: -------------------------------------------------------------------------------- 1 | library inflection.irregular_plural_verbs; 2 | 3 | /// A collection of verbs with irregular plurals. 4 | final Map irregularPluralVerbs = const { 5 | "is": "are", 6 | "am": "are", 7 | "was": "were", 8 | "has": "have" 9 | }; 10 | -------------------------------------------------------------------------------- /lib/src/past.dart: -------------------------------------------------------------------------------- 1 | library inflection.past; 2 | 3 | import 'dart:convert'; 4 | 5 | import 'irregular_past_verbs.dart'; 6 | import 'verbs_ending_with_ed.dart'; 7 | import 'util.dart'; 8 | 9 | class PastEncoder extends Converter { 10 | final List _inflectionRules = []; 11 | 12 | PastEncoder() { 13 | irregularPastVerbs.forEach((String presentOrParticiple, String past) { 14 | addIrregularInflectionRule(presentOrParticiple, past); 15 | }); 16 | [ 17 | [r'.+', (Match m) => '${m[0]}ed'], 18 | [r'([^aeiou])y$', (Match m) => '${m[1]}ied'], 19 | [r'([aeiou]e)$', (Match m) => '${m[1]}d'], 20 | [r'[aeiou][^aeiou]e$', (Match m) => '${m[0]}d'] 21 | ] 22 | .reversed 23 | .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); 24 | } 25 | 26 | void addInflectionRule(String presentOrParticiple, dynamic past) { 27 | _inflectionRules 28 | .add([new RegExp(presentOrParticiple, caseSensitive: false), past]); 29 | } 30 | 31 | void addIrregularInflectionRule(String presentOrParticiple, String past) { 32 | _inflectionRules.add([ 33 | new RegExp( 34 | r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)?' + 35 | presentOrParticiple + 36 | r'$', 37 | caseSensitive: false), 38 | (Match m) => (m[1] == null) ? past : m[1] + past 39 | ]); 40 | } 41 | 42 | @override 43 | String convert(String word) { 44 | if (!word.isEmpty) { 45 | if (word.contains("ed", word.length - 2)) { 46 | RegExp reg = new RegExp( 47 | r'^(back|dis|for|fore|in|inter|mis|off|over|out|par|pre|re|type|un|under|up)(.+)$'); 48 | if (reg.hasMatch(word)) { 49 | if (!verbsEndingWithEd.contains(reg.firstMatch(word).group(2))) 50 | return word; 51 | } else if (!verbsEndingWithEd.contains(word)) { 52 | return word; 53 | } 54 | } 55 | 56 | for (var r in _inflectionRules) { 57 | RegExp pattern = r.first; 58 | if (pattern.hasMatch(word)) { 59 | return word.replaceAllMapped(pattern, r.last as MatchToString); 60 | } 61 | } 62 | } 63 | 64 | return word; 65 | } 66 | } 67 | 68 | final Converter PAST = new PastEncoder(); 69 | -------------------------------------------------------------------------------- /lib/src/plural.dart: -------------------------------------------------------------------------------- 1 | library inflection.plural; 2 | 3 | import 'dart:convert'; 4 | 5 | import 'uncountable_nouns.dart'; 6 | import 'irregular_plural_nouns.dart'; 7 | import 'util.dart'; 8 | 9 | class PluralEncoder extends Converter { 10 | final List _inflectionRules = []; 11 | 12 | PluralEncoder() { 13 | irregularPluralNouns.forEach((singular, plural) { 14 | addIrregularInflectionRule(singular, plural); 15 | }); 16 | 17 | [ 18 | [r'$', (Match m) => 's'], 19 | [r's$', (Match m) => 's'], 20 | [r'^(ax|test)is$', (Match m) => '${m[1]}es'], 21 | [r'(octop|vir)us$', (Match m) => '${m[1]}i'], 22 | [r'(octop|vir)i$', (Match m) => m[0]], 23 | [r'(alias|status)$', (Match m) => '${m[1]}es'], 24 | [r'(bu)s$', (Match m) => '${m[1]}ses'], 25 | [r'(buffal|tomat)o$', (Match m) => '${m[1]}oes'], 26 | [r'([ti])um$', (Match m) => '${m[1]}a'], 27 | [r'([ti])a$', (Match m) => m[0]], 28 | [r'sis$', (Match m) => 'ses'], 29 | [r'(?:([^f])fe|([lr])f)$', (Match m) => '${m[1]}${m[2]}ves'], 30 | [r'([^aeiouy]|qu)y$', (Match m) => '${m[1]}ies'], 31 | [r'(x|ch|ss|sh)$', (Match m) => '${m[1]}es'], 32 | [r'(matr|vert|ind)(?:ix|ex)$', (Match m) => '${m[1]}ices'], 33 | [r'^(m|l)ouse$', (Match m) => '${m[1]}ice'], 34 | [r'^(m|l)ice$', (Match m) => m[0]], 35 | [r'^(ox)$', (Match m) => '${m[1]}en'], 36 | [r'^(oxen)$', (Match m) => m[1]], 37 | [r'(quiz)$', (Match m) => '${m[1]}zes'] 38 | ] 39 | .reversed 40 | .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); 41 | } 42 | 43 | void addInflectionRule(String singular, dynamic plural) { 44 | _inflectionRules.add([new RegExp(singular, caseSensitive: false), plural]); 45 | } 46 | 47 | void addIrregularInflectionRule(String singular, String plural) { 48 | final s0 = singular.substring(0, 1); 49 | final srest = singular.substring(1); 50 | final p0 = plural.substring(0, 1); 51 | final prest = plural.substring(1); 52 | 53 | if (s0.toUpperCase() == p0.toUpperCase()) { 54 | addInflectionRule('(${s0})${srest}\$', (Match m) => '${m[1]}${prest}'); 55 | addInflectionRule('(${p0})${prest}\$', (Match m) => '${m[1]}${prest}'); 56 | } else { 57 | addInflectionRule('${s0.toUpperCase()}(?i)${srest}\$', 58 | (Match m) => '${p0.toUpperCase()}${prest}'); 59 | addInflectionRule('${s0.toLowerCase()}(?i)${srest}\$', 60 | (Match m) => '${p0.toUpperCase()}${prest}'); 61 | addInflectionRule('${p0.toUpperCase()}(?i)${prest}\$', 62 | (Match m) => '${p0.toUpperCase()}${prest}'); 63 | addInflectionRule('${p0.toLowerCase()}(?i)${prest}\$', 64 | (Match m) => '${p0.toLowerCase()}${prest}'); 65 | } 66 | } 67 | 68 | @override 69 | String convert(String word) { 70 | if (!word.isEmpty) { 71 | if (uncountableNouns.contains(word.toLowerCase())) { 72 | return word; 73 | } else { 74 | for (var r in _inflectionRules) { 75 | RegExp pattern = r.first; 76 | if (pattern.hasMatch(word)) { 77 | return word.replaceAllMapped(pattern, r.last as MatchToString); 78 | } 79 | } 80 | } 81 | } 82 | 83 | return word; 84 | } 85 | } 86 | 87 | final Converter PLURAL = new PluralEncoder(); 88 | -------------------------------------------------------------------------------- /lib/src/plural_verb.dart: -------------------------------------------------------------------------------- 1 | library inflection.plural_verb; 2 | 3 | import 'dart:convert'; 4 | 5 | import 'irregular_plural_verbs.dart'; 6 | import 'util.dart'; 7 | 8 | class PluralVerbEncoder extends Converter { 9 | final List _inflectionRules = []; 10 | 11 | PluralVerbEncoder() { 12 | irregularPluralVerbs.forEach((singular, plural) { 13 | addInflectionRule(singular, (Match m) => plural); 14 | }); 15 | 16 | [ 17 | [r'e?s$', (Match m) => ''], 18 | [r'ies$', (Match m) => 'y'], 19 | [r'([^h|z|o|i])es$', (Match m) => '${m[1]}e'], 20 | [r'ses$', (Match m) => 's'], 21 | [r'zzes$', (Match m) => 'zz'], 22 | [r'([cs])hes$', (Match m) => '${m[1]}h'], 23 | [r'xes$', (Match m) => 'x'], 24 | [r'sses$', (Match m) => 'ss'] 25 | ] 26 | .reversed 27 | .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); 28 | } 29 | 30 | void addInflectionRule(String singular, dynamic plural) { 31 | _inflectionRules.add([new RegExp(singular, caseSensitive: false), plural]); 32 | } 33 | 34 | @override 35 | String convert(String word) { 36 | if (!word.isEmpty) { 37 | for (var r in _inflectionRules) { 38 | RegExp pattern = r.first; 39 | if (pattern.hasMatch(word)) { 40 | return word.replaceAllMapped(pattern, r.last as MatchToString); 41 | } 42 | } 43 | } 44 | 45 | return word; 46 | } 47 | } 48 | 49 | final Converter PLURALVERB = new PluralVerbEncoder(); 50 | -------------------------------------------------------------------------------- /lib/src/singular.dart: -------------------------------------------------------------------------------- 1 | library inflection.singular; 2 | 3 | import 'dart:convert'; 4 | 5 | import 'uncountable_nouns.dart'; 6 | import 'irregular_plural_nouns.dart'; 7 | import 'util.dart'; 8 | 9 | class SingularEncoder extends Converter { 10 | final List _inflectionRules = []; 11 | 12 | SingularEncoder() { 13 | irregularPluralNouns.forEach((singular, plural) { 14 | addIrregularInflectionRule(singular, plural); 15 | }); 16 | 17 | [ 18 | [r's$', (Match m) => ''], 19 | [r'(ss)$', (Match m) => m[1]], 20 | [r'(n)ews$', (Match m) => '${m[1]}ews'], // TODO: uncountable? 21 | [r'([ti])a$', (Match m) => '${m[1]}um'], 22 | [ 23 | r'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$', 24 | (Match m) => '${m[1]}sis' 25 | ], 26 | [r'(^analy)(sis|ses)$', (Match m) => '${m[1]}sis'], // TODO: not needed? 27 | [r'([^f])ves$', (Match m) => '${m[1]}fe'], 28 | [r'(hive|tive)s$', (Match m) => m[1]], 29 | [r'([lr])ves$', (Match m) => '${m[1]}f'], 30 | [r'([^aeiouy]|qu)ies$', (Match m) => '${m[1]}y'], 31 | [r'(s)eries$', (Match m) => '${m[1]}eries'], // TODO: uncountable 32 | [r'(m)ovies$', (Match m) => '${m[1]}ovie'], 33 | [r'(x|ch|ss|sh)es$', (Match m) => m[1]], 34 | [r'^(m|l)ice$', (Match m) => '${m[1]}ouse'], 35 | [r'(bus)(es)?$', (Match m) => m[1]], 36 | [r'(shoe)s$', (Match m) => m[1]], 37 | [r'(cris|test)(is|es)$', (Match m) => '${m[1]}is'], 38 | [r'^(a)x[ie]s$', (Match m) => '${m[1]}xis'], 39 | [r'(octop|vir)(us|i)$', (Match m) => '${m[1]}us'], 40 | [r'(alias|status)(es)?$', (Match m) => m[1]], 41 | [r'^(ox)en', (Match m) => m[1]], 42 | [r'(vert|ind)ices$', (Match m) => '${m[1]}ex'], 43 | [r'(matr)ices$', (Match m) => '${m[1]}ix'], 44 | [r'(quiz)zes$', (Match m) => m[1]], 45 | [r'(database)s$', (Match m) => m[1]] 46 | ] 47 | .reversed 48 | .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); 49 | } 50 | 51 | void addInflectionRule(String plural, dynamic singular) { 52 | _inflectionRules.add([new RegExp(plural, caseSensitive: false), singular]); 53 | } 54 | 55 | void addIrregularInflectionRule(String singular, String plural) { 56 | final s0 = singular.substring(0, 1); 57 | final srest = singular.substring(1); 58 | final p0 = plural.substring(0, 1); 59 | final prest = plural.substring(1); 60 | 61 | if (s0.toUpperCase() == p0.toUpperCase()) { 62 | addInflectionRule('(${s0})${srest}\$', (Match m) => '${m[1]}${srest}'); 63 | addInflectionRule('(${p0})${prest}\$', (Match m) => '${m[1]}${srest}'); 64 | } else { 65 | addInflectionRule('${s0.toUpperCase()}(?i)${srest}\$', 66 | (Match m) => '${s0.toUpperCase()}${srest}'); 67 | addInflectionRule('${s0.toLowerCase()}(?i)${srest}\$', 68 | (Match m) => '${s0.toUpperCase()}${srest}'); 69 | addInflectionRule('${p0.toUpperCase()}(?i)${prest}\$', 70 | (Match m) => '${s0.toUpperCase()}${srest}'); 71 | addInflectionRule('${p0.toLowerCase()}(?i)${prest}\$', 72 | (Match m) => '${s0.toLowerCase()}${srest}'); 73 | } 74 | } 75 | 76 | @override 77 | String convert(String word) { 78 | if (!word.isEmpty) { 79 | if (uncountableNouns.contains(word.toLowerCase())) { 80 | return word; 81 | } else { 82 | for (var r in _inflectionRules) { 83 | RegExp pattern = r.first; 84 | if (pattern.hasMatch(word)) { 85 | return word.replaceAllMapped(pattern, r.last as MatchToString); 86 | } 87 | } 88 | } 89 | } 90 | 91 | return word; 92 | } 93 | } 94 | 95 | final Converter SINGULAR = new SingularEncoder(); 96 | -------------------------------------------------------------------------------- /lib/src/singular_verb.dart: -------------------------------------------------------------------------------- 1 | library inflection.singular_verb; 2 | 3 | import 'dart:convert'; 4 | 5 | import 'irregular_plural_verbs.dart'; 6 | import 'util.dart'; 7 | 8 | class SingularVerbEncoder extends Converter { 9 | final List _inflectionRules = []; 10 | 11 | SingularVerbEncoder() { 12 | irregularPluralVerbs.forEach((singular, plural) { 13 | addInflectionRule(plural, (Match m) => singular); 14 | }); 15 | 16 | [ 17 | [r'$', (Match m) => 's'], 18 | [r'([^aeiou])y$', (Match m) => '${m[1]}ies'], 19 | [r'(z)$', (Match m) => '${m[1]}es'], 20 | [r'(ss|zz|x|h|o|us)$', (Match m) => '${m[1]}es'], 21 | [r'(ed)$', (Match m) => '${m[1]}'] 22 | ] 23 | .reversed 24 | .forEach((rule) => addInflectionRule(rule.first as String, rule.last)); 25 | } 26 | 27 | void addInflectionRule(String singular, dynamic plural) { 28 | _inflectionRules.add([new RegExp(singular, caseSensitive: false), plural]); 29 | } 30 | 31 | @override 32 | String convert(String word) { 33 | if (!word.isEmpty) { 34 | for (var r in _inflectionRules) { 35 | RegExp pattern = r.first; 36 | if (pattern.hasMatch(word)) { 37 | return word.replaceAllMapped(pattern, r.last as MatchToString); 38 | } 39 | } 40 | } 41 | 42 | return word; 43 | } 44 | } 45 | 46 | final Converter SINGULARVERB = new SingularVerbEncoder(); 47 | -------------------------------------------------------------------------------- /lib/src/snake_case.dart: -------------------------------------------------------------------------------- 1 | library inflection.snake_case; 2 | 3 | import 'dart:convert'; 4 | 5 | final _underscoreRE0 = new RegExp(r'''([A-Z\d]+)([A-Z][a-z])'''); 6 | final _underscoreRE1 = new RegExp(r'''([a-z\d])([A-Z])'''); 7 | final _underscoreRE2 = new RegExp(r'[-\s]'); 8 | 9 | class SnakeCaseEncoder extends Converter { 10 | const SnakeCaseEncoder(); 11 | 12 | /// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited, 13 | /// lowercase form. Also known as 'kebab case' or 'lisp case'. 14 | @override 15 | String convert(String phrase) { 16 | return phrase 17 | .replaceAllMapped(_underscoreRE0, (match) => "${match[1]}_${match[2]}") 18 | .replaceAllMapped(_underscoreRE1, (match) => "${match[1]}_${match[2]}") 19 | .replaceAll(_underscoreRE2, "_") 20 | .toLowerCase(); 21 | } 22 | } 23 | 24 | const Converter SNAKE_CASE = const SnakeCaseEncoder(); 25 | -------------------------------------------------------------------------------- /lib/src/spinal_case.dart: -------------------------------------------------------------------------------- 1 | library inflection.spinal_case; 2 | 3 | import 'dart:convert'; 4 | 5 | final _underscoreRE0 = new RegExp(r'''([A-Z\d]+)([A-Z][a-z])'''); 6 | final _underscoreRE1 = new RegExp(r'''([a-z\d])([A-Z])'''); 7 | final _underscoreRE2 = new RegExp(r'[_\s]'); 8 | 9 | class SpinalCaseEncoder extends Converter { 10 | const SpinalCaseEncoder(); 11 | 12 | /// Converts the input [phrase] to 'spinal case', i.e. a hyphen-delimited, 13 | /// lowercase form. Also known as 'kebab case' or 'lisp case'. 14 | @override 15 | String convert(String phrase) { 16 | return phrase 17 | .replaceAllMapped(_underscoreRE0, (match) => "${match[1]}-${match[2]}") 18 | .replaceAllMapped(_underscoreRE1, (match) => "${match[1]}-${match[2]}") 19 | .replaceAll(_underscoreRE2, "-") 20 | .toLowerCase(); 21 | } 22 | } 23 | 24 | const Converter SPINAL_CASE = const SpinalCaseEncoder(); 25 | -------------------------------------------------------------------------------- /lib/src/uncountable_nouns.dart: -------------------------------------------------------------------------------- 1 | library inflection.uncountable; 2 | 3 | /// Uncountable nouns are substances, concepts etc that we cannot divide into 4 | /// separate elements. We cannot "count" them. 5 | final Set uncountableNouns = new Set.from(const [ 6 | "equipment", 7 | "information", 8 | "rice", 9 | "money", 10 | "species", 11 | "series", 12 | "fish", 13 | "sheep", 14 | "jeans", 15 | "police" 16 | ]); 17 | -------------------------------------------------------------------------------- /lib/src/util.dart: -------------------------------------------------------------------------------- 1 | typedef String MatchToString(Match m); 2 | -------------------------------------------------------------------------------- /lib/src/verbs_ending_with_ed.dart: -------------------------------------------------------------------------------- 1 | library inflection.verbs_ending_with_ed; 2 | 3 | /// A collection of verbs ending with -ed. 4 | final List verbsEndingWithEd = const [ 5 | "bed", 6 | "bleed", 7 | "breed", 8 | "embed", 9 | "exceed", 10 | "feed", 11 | "heed", 12 | "need", 13 | "proceed", 14 | "seed", 15 | "shred" 16 | "speed", 17 | "succeed", 18 | "ted", 19 | "wed", 20 | "weed" 21 | ]; 22 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: inflection 2 | version: 0.4.1 3 | environment: 4 | sdk: ">=1.8.0 <3.0.0" 5 | author: George Moschovitis 6 | description: Grammatical Inflection encoders. 7 | homepage: https://github.com/gmosx/dart-inflection 8 | dev_dependencies: 9 | test: any 10 | -------------------------------------------------------------------------------- /test/all_test.dart: -------------------------------------------------------------------------------- 1 | import 'inflection_test.dart' as inflection_test; 2 | import 'past_test.dart' as past_test; 3 | import 'plural_test.dart' as plural_test; 4 | import 'plural_verb_test.dart' as plural_verb_test; 5 | import 'singular_test.dart' as singular_test; 6 | import 'singular_verb_test.dart' as singular_verb_test; 7 | import 'snake_case_test.dart' as snake_case_test; 8 | import 'spinal_case_test.dart' as spinal_case_test; 9 | 10 | void main() { 11 | inflection_test.main(); 12 | past_test.main(); 13 | plural_test.main(); 14 | plural_verb_test.main(); 15 | singular_test.main(); 16 | singular_verb_test.main(); 17 | snake_case_test.main(); 18 | spinal_case_test.main(); 19 | } 20 | -------------------------------------------------------------------------------- /test/inflection_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/inflection.dart'; 6 | 7 | void main() { 8 | group("The inflection library", () { 9 | test("provides a few convenient helper functions", () { 10 | expect(pluralize("axis"), equals("axes")); 11 | expect(convertToPlural("axis"), equals("axes")); 12 | expect(singularize("Houses"), equals("House")); 13 | expect(convertToSingular("Houses"), equals("House")); 14 | expect(convertToSnakeCase("CamelCase"), equals("camel_case")); 15 | expect(convertToSpinalCase("CamelCase"), equals("camel-case")); 16 | }); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/past_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.past.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/past.dart'; 6 | import 'package:inflection/src/irregular_past_verbs.dart'; 7 | 8 | void main() { 9 | group("The PastEncoder", () { 10 | test("converts verbs from present or participle to past", () { 11 | expect(PAST.convert(""), equals("")); 12 | expect(PAST.convert("ask"), equals("asked")); 13 | expect(PAST.convert("close"), equals("closed")); 14 | expect(PAST.convert("die"), equals("died")); 15 | expect(PAST.convert("phone"), equals("phoned")); 16 | expect(PAST.convert("play"), equals("played")); 17 | expect(PAST.convert("destroy"), equals("destroyed")); 18 | expect(PAST.convert("show"), equals("showed")); 19 | expect(PAST.convert("marry"), equals("married")); 20 | expect(PAST.convert("study"), equals("studied")); 21 | expect(PAST.convert("visit"), equals("visited")); 22 | expect(PAST.convert("miss"), equals("missed")); 23 | expect(PAST.convert("watch"), equals("watched")); 24 | expect(PAST.convert("finish"), equals("finished")); 25 | expect(PAST.convert("fix"), equals("fixed")); 26 | expect(PAST.convert("buzz"), equals("buzzed")); 27 | expect(PAST.convert("asked"), equals("asked")); 28 | expect(PAST.convert("closed"), equals("closed")); 29 | expect(PAST.convert("reopened"), equals("reopened")); 30 | expect(PAST.convert("unseed"), equals("unseeded")); 31 | }); 32 | 33 | test("handles irregular past verbs", () { 34 | irregularPastVerbs.forEach((String presentOrParticiple, String past) { 35 | expect(PAST.convert(presentOrParticiple), equals(past)); 36 | }); 37 | expect(PAST.convert("forgo"), equals("forwent")); 38 | expect(PAST.convert("undo"), equals("undid")); 39 | expect(PAST.convert("outsell"), equals("outsold")); 40 | expect(PAST.convert("rebreed"), equals("rebred")); 41 | expect(PAST.convert("arose"), equals("arose")); 42 | expect(PAST.convert("backslid"), equals("backslid")); 43 | expect(PAST.convert("forbade"), equals("forbade")); 44 | }); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /test/plural_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.plural.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/plural.dart'; 6 | import 'package:inflection/src/uncountable_nouns.dart'; 7 | 8 | void main() { 9 | group("The PluralEncoder", () { 10 | test("converts nouns from singular to plural", () { 11 | expect(PLURAL.convert(""), equals("")); 12 | expect(PLURAL.convert("House"), equals("Houses")); 13 | expect(PLURAL.convert("house"), equals("houses")); 14 | expect(PLURAL.convert("dog"), equals("dogs")); 15 | expect(PLURAL.convert("axis"), equals("axes")); 16 | expect(PLURAL.convert("testis"), equals("testes")); 17 | expect(PLURAL.convert("octopus"), equals("octopi")); 18 | expect(PLURAL.convert("virus"), equals("viri")); 19 | expect(PLURAL.convert("octopi"), equals("octopi")); 20 | expect(PLURAL.convert("viri"), equals("viri")); 21 | expect(PLURAL.convert("alias"), equals("aliases")); 22 | expect(PLURAL.convert("status"), equals("statuses")); 23 | expect(PLURAL.convert("bus"), equals("buses")); 24 | expect(PLURAL.convert("buffalo"), equals("buffaloes")); 25 | expect(PLURAL.convert("tomato"), equals("tomatoes")); 26 | expect(PLURAL.convert("ultimatum"), equals("ultimata")); 27 | expect(PLURAL.convert("pentium"), equals("pentia")); 28 | expect(PLURAL.convert("ultimata"), equals("ultimata")); 29 | expect(PLURAL.convert("pentia"), equals("pentia")); 30 | expect(PLURAL.convert("nemesis"), equals("nemeses")); 31 | expect(PLURAL.convert("hive"), equals("hives")); 32 | expect(PLURAL.convert("fly"), equals("flies")); 33 | expect(PLURAL.convert("dish"), equals("dishes")); 34 | expect(PLURAL.convert("bench"), equals("benches")); 35 | expect(PLURAL.convert("matrix"), equals("matrices")); 36 | expect(PLURAL.convert("vertex"), equals("vertices")); 37 | expect(PLURAL.convert("index"), equals("indices")); 38 | expect(PLURAL.convert("mouse"), equals("mice")); 39 | expect(PLURAL.convert("louse"), equals("lice")); 40 | expect(PLURAL.convert("mice"), equals("mice")); 41 | expect(PLURAL.convert("lice"), equals("lice")); 42 | expect(PLURAL.convert("ox"), equals("oxen")); 43 | expect(PLURAL.convert("ox"), equals("oxen")); 44 | expect(PLURAL.convert("oxen"), equals("oxen")); 45 | expect(PLURAL.convert("quiz"), equals("quizzes")); 46 | }); 47 | 48 | test("handles uncountable nouns", () { 49 | uncountableNouns.forEach((noun) { 50 | expect(PLURAL.convert(noun), equals(noun)); 51 | }); 52 | 53 | uncountableNouns.forEach((noun) { 54 | final upperNoun = noun.toUpperCase(); 55 | expect(PLURAL.convert(upperNoun), equals(upperNoun)); 56 | }); 57 | }); 58 | 59 | test("handles irregular plural nouns", () { 60 | expect(PLURAL.convert("person"), equals("people")); 61 | expect(PLURAL.convert("Child"), equals("Children")); 62 | expect(PLURAL.convert("children"), equals("children")); 63 | expect(PLURAL.convert("man"), equals("men")); 64 | }); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /test/plural_verb_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.plural_verb.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/plural_verb.dart'; 6 | 7 | void main() { 8 | group("The PluralVerbEncoder", () { 9 | test("converts verbs from singular to plural", () { 10 | expect(PLURALVERB.convert(""), equals("")); 11 | expect(PLURALVERB.convert("eats"), equals("eat")); 12 | expect(PLURALVERB.convert("goes"), equals("go")); 13 | expect(PLURALVERB.convert("boxes"), equals("box")); 14 | expect(PLURALVERB.convert("pays"), equals("pay")); 15 | expect(PLURALVERB.convert("rides"), equals("ride")); 16 | expect(PLURALVERB.convert("writes"), equals("write")); 17 | expect(PLURALVERB.convert("wears"), equals("wear")); 18 | expect(PLURALVERB.convert("steals"), equals("steal")); 19 | expect(PLURALVERB.convert("springs"), equals("spring")); 20 | expect(PLURALVERB.convert("speaks"), equals("speak")); 21 | expect(PLURALVERB.convert("sings"), equals("sing")); 22 | expect(PLURALVERB.convert("buses"), equals("bus")); 23 | expect(PLURALVERB.convert("knows"), equals("know")); 24 | expect(PLURALVERB.convert("hides"), equals("hide")); 25 | expect(PLURALVERB.convert("catches"), equals("catch")); 26 | }); 27 | 28 | test("handles irregular plural verbs", () { 29 | expect(PLURALVERB.convert("am"), equals("are")); 30 | expect(PLURALVERB.convert("is"), equals("are")); 31 | expect(PLURALVERB.convert("was"), equals("were")); 32 | expect(PLURALVERB.convert("has"), equals("have")); 33 | }); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /test/singular_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.singular.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/singular.dart'; 6 | import 'package:inflection/src/uncountable_nouns.dart'; 7 | 8 | void main() { 9 | group("The SingularEncoder", () { 10 | test("converts nouns from plural to singular", () { 11 | expect(SINGULAR.convert(""), equals("")); 12 | expect(SINGULAR.convert("Houses"), equals("House")); 13 | expect(SINGULAR.convert("houses"), equals("house")); 14 | expect(SINGULAR.convert("ultimata"), equals("ultimatum")); 15 | expect(SINGULAR.convert("pentia"), equals("pentium")); 16 | expect(SINGULAR.convert("analyses"), equals("analysis")); 17 | expect(SINGULAR.convert("diagnoses"), equals("diagnosis")); 18 | expect(SINGULAR.convert("Parentheses"), equals("Parenthesis")); 19 | expect(SINGULAR.convert("lives"), equals("life")); 20 | expect(SINGULAR.convert("hives"), equals("hive")); 21 | expect(SINGULAR.convert("tives"), equals("tive")); 22 | expect(SINGULAR.convert("shelves"), equals("shelf")); 23 | expect(SINGULAR.convert("qualities"), equals("quality")); 24 | expect(SINGULAR.convert("series"), equals("series")); 25 | expect(SINGULAR.convert("movies"), equals("movie")); 26 | expect(SINGULAR.convert("benches"), equals("bench")); 27 | expect(SINGULAR.convert("fishes"), equals("fish")); 28 | expect(SINGULAR.convert("mice"), equals("mouse")); 29 | expect(SINGULAR.convert("lice"), equals("louse")); 30 | expect(SINGULAR.convert("buses"), equals("bus")); 31 | expect(SINGULAR.convert("shoes"), equals("shoe")); 32 | expect(SINGULAR.convert("testis"), equals("testis")); 33 | expect(SINGULAR.convert("crisis"), equals("crisis")); 34 | expect(SINGULAR.convert("axes"), equals("axis")); 35 | expect(SINGULAR.convert("axis"), equals("axis")); 36 | expect(SINGULAR.convert("viri"), equals("virus")); 37 | expect(SINGULAR.convert("octopi"), equals("octopus")); 38 | expect(SINGULAR.convert("aliases"), equals("alias")); 39 | expect(SINGULAR.convert("statuses"), equals("status")); 40 | expect(SINGULAR.convert("vertices"), equals("vertex")); 41 | expect(SINGULAR.convert("indices"), equals("index")); 42 | expect(SINGULAR.convert("Matrices"), equals("Matrix")); 43 | expect(SINGULAR.convert("quizzes"), equals("quiz")); 44 | expect(SINGULAR.convert("databases"), equals("database")); 45 | }); 46 | 47 | test("handles uncountable nouns", () { 48 | uncountableNouns.forEach((noun) { 49 | expect(SINGULAR.convert(noun), equals(noun)); 50 | }); 51 | 52 | uncountableNouns.forEach((noun) { 53 | final upperNoun = noun.toUpperCase(); 54 | expect(SINGULAR.convert(upperNoun), equals(upperNoun)); 55 | }); 56 | }); 57 | 58 | test("handles irregular plural nouns", () { 59 | expect(SINGULAR.convert("people"), equals("person")); 60 | expect(SINGULAR.convert("Children"), equals("Child")); 61 | expect(SINGULAR.convert("child"), equals("child")); 62 | expect(SINGULAR.convert("men"), equals("man")); 63 | }); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /test/singular_verb_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.singular_verb.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/singular_verb.dart'; 6 | 7 | void main() { 8 | group("The SingularVerbEncoder", () { 9 | test("converts verbs from singular to plural", () { 10 | expect(SINGULARVERB.convert(""), equals("")); 11 | expect(SINGULARVERB.convert("eat"), equals("eats")); 12 | expect(SINGULARVERB.convert("go"), equals("goes")); 13 | expect(SINGULARVERB.convert("box"), equals("boxes")); 14 | expect(SINGULARVERB.convert("pay"), equals("pays")); 15 | expect(SINGULARVERB.convert("ride"), equals("rides")); 16 | expect(SINGULARVERB.convert("write"), equals("writes")); 17 | expect(SINGULARVERB.convert("wear"), equals("wears")); 18 | expect(SINGULARVERB.convert("steal"), equals("steals")); 19 | expect(SINGULARVERB.convert("spring"), equals("springs")); 20 | expect(SINGULARVERB.convert("speak"), equals("speaks")); 21 | expect(SINGULARVERB.convert("sing"), equals("sings")); 22 | expect(SINGULARVERB.convert("bus"), equals("buses")); 23 | expect(SINGULARVERB.convert("know"), equals("knows")); 24 | expect(SINGULARVERB.convert("hide"), equals("hides")); 25 | expect(SINGULARVERB.convert("catch"), equals("catches")); 26 | }); 27 | 28 | test("handles irregular plural verbs", () { 29 | expect(SINGULARVERB.convert("are"), equals("is")); 30 | expect(SINGULARVERB.convert("were"), equals("was")); 31 | expect(SINGULARVERB.convert("have"), equals("has")); 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /test/snake_case_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.snake_case.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/snake_case.dart'; 6 | 7 | void main() { 8 | group("The SnakeCaseEncoder", () { 9 | test("converts phrases to 'snake_case'", () { 10 | expect(SNAKE_CASE.convert(''), equals('')); 11 | expect(SNAKE_CASE.convert("CamelCaseName"), equals("camel_case_name")); 12 | expect(SNAKE_CASE.convert("propertyName"), equals("property_name")); 13 | expect(SNAKE_CASE.convert("property"), equals("property")); 14 | expect(SNAKE_CASE.convert("lisp-case"), equals("lisp_case")); 15 | expect(SNAKE_CASE.convert("This is a nice article"), 16 | equals("this_is_a_nice_article")); 17 | }); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /test/spinal_case_test.dart: -------------------------------------------------------------------------------- 1 | library inflection.spinal_case.test; 2 | 3 | import 'package:test/test.dart'; 4 | 5 | import 'package:inflection/src/spinal_case.dart'; 6 | 7 | void main() { 8 | group("The SpinalCaseEncoder", () { 9 | test("converts phrases to 'spinal-case'", () { 10 | expect(SPINAL_CASE.convert(''), equals('')); 11 | expect(SPINAL_CASE.convert("CamelCaseName"), equals("camel-case-name")); 12 | expect(SPINAL_CASE.convert("propertyName"), equals("property-name")); 13 | expect(SPINAL_CASE.convert("property"), equals("property")); 14 | expect(SPINAL_CASE.convert("snake_case"), equals("snake-case")); 15 | expect(SPINAL_CASE.convert("This is a nice article"), 16 | equals("this-is-a-nice-article")); 17 | }); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /tool/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fast fail the script on failures. 4 | set -e 5 | 6 | # Verify that the libraries are error free. 7 | dartanalyzer --fatal-warnings \ 8 | lib/inflection.dart \ 9 | test/all_test.dart 10 | 11 | # Run the tests. 12 | dart test/all_test.dart 13 | --------------------------------------------------------------------------------