JavaScript如何将英文字符串变成复数形式

76
15个回答

103

简化版(ES6):

const pluralize = (count, noun, suffix = 's') =>
  `${count} ${noun}${count !== 1 ? suffix : ''}`;

类型Script:

const pluralize = (count: number, noun: string, suffix = 's') =>
  `${count} ${noun}${count !== 1 ? suffix : ''}`;

使用方法:

pluralize(0, 'turtle'); // 0 turtles
pluralize(1, 'turtle'); // 1 turtle
pluralize(2, 'turtle'); // 2 turtles
pluralize(3, 'fox', 'es'); // 3 foxes

显然,这并不能支持所有英语边界情况,但它适用于大多数情况。


4
有人能否提供maybePluralize的其他名称建议吗? - thisshri
1
localize, plural - vixalien
@thisshri 我使用了 pluralize - Thinh NV
翻译怎么样? - Weiyi
1
请注意,这仅适用于某些语言,例如英语。例如在法语中,条件应为 count > 1 而不是 count !== 1 - Maxime Chéramy
有很多单词不仅在末尾加上字母s,这些单词可以是完全不同的单词,它们没有任何附加内容,或者除了s之外还有其他东西。例如固件、鱼、家具、黄金、硬件。 - MrEduar

79

使用Pluralize

有一个很棒的小型库叫做Pluralize,可以在npm和bower中进行打包。

这是使用方法:

import Pluralize from 'pluralize';

Pluralize( 'Towel', 42 );       // "Towels"

Pluralize( 'Towel', 42, true ); // "42 Towels"

您可以在这里获取:

https://github.com/blakeembrey/pluralize


谢谢@JoshuaPinter,我正在处理s然后ies,使用自定义函数,然后想到必须有一些已经存在的东西,正好是我要找的。 - ak85
3
@ak85 添加另一个依赖项并不总是解决方案,但当它是易于理解且不是关键的东西时,这是非常有意义的。站在巨人的肩膀上。 - Joshua Pinter

45
因此,我通过使用JavaScript翻译了Kuwamoto的PHP类并回答了自己的问题。
String.prototype.plural = function(revert){

    var plural = {
        '(quiz)$'               : "$1zes",
        '^(ox)$'                : "$1en",
        '([m|l])ouse$'          : "$1ice",
        '(matr|vert|ind)ix|ex$' : "$1ices",
        '(x|ch|ss|sh)$'         : "$1es",
        '([^aeiouy]|qu)y$'      : "$1ies",
        '(hive)$'               : "$1s",
        '(?:([^f])fe|([lr])f)$' : "$1$2ves",
        '(shea|lea|loa|thie)f$' : "$1ves",
        'sis$'                  : "ses",
        '([ti])um$'             : "$1a",
        '(tomat|potat|ech|her|vet)o$': "$1oes",
        '(bu)s$'                : "$1ses",
        '(alias)$'              : "$1es",
        '(octop)us$'            : "$1i",
        '(ax|test)is$'          : "$1es",
        '(us)$'                 : "$1es",
        '([^s]+)$'              : "$1s"
    };

    var singular = {
        '(quiz)zes$'             : "$1",
        '(matr)ices$'            : "$1ix",
        '(vert|ind)ices$'        : "$1ex",
        '^(ox)en$'               : "$1",
        '(alias)es$'             : "$1",
        '(octop|vir)i$'          : "$1us",
        '(cris|ax|test)es$'      : "$1is",
        '(shoe)s$'               : "$1",
        '(o)es$'                 : "$1",
        '(bus)es$'               : "$1",
        '([m|l])ice$'            : "$1ouse",
        '(x|ch|ss|sh)es$'        : "$1",
        '(m)ovies$'              : "$1ovie",
        '(s)eries$'              : "$1eries",
        '([^aeiouy]|qu)ies$'     : "$1y",
        '([lr])ves$'             : "$1f",
        '(tive)s$'               : "$1",
        '(hive)s$'               : "$1",
        '(li|wi|kni)ves$'        : "$1fe",
        '(shea|loa|lea|thie)ves$': "$1f",
        '(^analy)ses$'           : "$1sis",
        '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$': "$1$2sis",        
        '([ti])a$'               : "$1um",
        '(n)ews$'                : "$1ews",
        '(h|bl)ouses$'           : "$1ouse",
        '(corpse)s$'             : "$1",
        '(us)es$'                : "$1",
        's$'                     : ""
    };

    var irregular = {
        'move'   : 'moves',
        'foot'   : 'feet',
        'goose'  : 'geese',
        'sex'    : 'sexes',
        'child'  : 'children',
        'man'    : 'men',
        'tooth'  : 'teeth',
        'person' : 'people'
    };

    var uncountable = [
        'sheep', 
        'fish',
        'deer',
        'moose',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment'
    ];

    // save some time in the case that singular and plural are the same
    if(uncountable.indexOf(this.toLowerCase()) >= 0)
      return this;

    // check for irregular forms
    for(word in irregular){

      if(revert){
              var pattern = new RegExp(irregular[word]+'$', 'i');
              var replace = word;
      } else{ var pattern = new RegExp(word+'$', 'i');
              var replace = irregular[word];
      }
      if(pattern.test(this))
        return this.replace(pattern, replace);
    }

    if(revert) var array = singular;
         else  var array = plural;

    // check for matches using regular expressions
    for(reg in array){

      var pattern = new RegExp(reg, 'i');

      if(pattern.test(this))
        return this.replace(pattern, array[reg]);
    }

    return this;
}

易于使用:

alert("page".plural()); // return plural form => pages
alert("mouse".plural()); // return plural form => mice
alert("women".plural(true)); // return singular form => woman

DEMO


9
扩展内置对象原型通常不是一个好主意。你可能想看一下这个模块:https://github.com/blakeembrey/pluralize - Paolo Moretti
1
复数数组有一个小错误。最后一项应该是'$' : 's'而不是's$' : 's' - wout
@pmrotule 仍然存在一个问题。使用 '(.)$' : '$1s'page 进行复数形式处理将导致结果为 pags。因此,我改回了 '$' : '$1s' 并在上面添加了一个规则以处理空字符串:'^$' : ''。现在在我的端上它按预期工作。 - wout
@wout - 这很奇怪。使用 $1,它不应该替换任何现有字符。对我来说它很好用(page 将变成 pages - 请参见演示)。但仍然存在一个问题,如果已经是复数形式,它将导致 pagess,因此我将其修改为 '([^s]+)$' : '$1s' - pmrotule
1
@SoaperGEM - 我是加拿大人,竟然忘了这个。真丢脸!我已经编辑了不可数名词。 - pmrotule
显示剩余8条评论

24

基于 @pmrotule 的答案,加上一些 TypeScript 魔法和对不可数数组的一些补充,我在这里添加了复数和单数函数。

复数版本:

/**
 * Returns the plural of an English word.
 *
 * @export
 * @param {string} word
 * @param {number} [amount]
 * @returns {string}
 */
export function plural(word: string, amount?: number): string {
    if (amount !== undefined && amount === 1) {
        return word
    }
    const plural: { [key: string]: string } = {
        '(quiz)$'               : "$1zes",
        '^(ox)$'                : "$1en",
        '([m|l])ouse$'          : "$1ice",
        '(matr|vert|ind)ix|ex$' : "$1ices",
        '(x|ch|ss|sh)$'         : "$1es",
        '([^aeiouy]|qu)y$'      : "$1ies",
        '(hive)$'               : "$1s",
        '(?:([^f])fe|([lr])f)$' : "$1$2ves",
        '(shea|lea|loa|thie)f$' : "$1ves",
        'sis$'                  : "ses",
        '([ti])um$'             : "$1a",
        '(tomat|potat|ech|her|vet)o$': "$1oes",
        '(bu)s$'                : "$1ses",
        '(alias)$'              : "$1es",
        '(octop)us$'            : "$1i",
        '(ax|test)is$'          : "$1es",
        '(us)$'                 : "$1es",
        '([^s]+)$'              : "$1s"
    }
    const irregular: { [key: string]: string } = {
        'move'   : 'moves',
        'foot'   : 'feet',
        'goose'  : 'geese',
        'sex'    : 'sexes',
        'child'  : 'children',
        'man'    : 'men',
        'tooth'  : 'teeth',
        'person' : 'people'
    }
    const uncountable: string[] = [
        'sheep',
        'fish',
        'deer',
        'moose',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment',
        'bison',
        'cod',
        'offspring',
        'pike',
        'salmon',
        'shrimp',
        'swine',
        'trout',
        'aircraft',
        'hovercraft',
        'spacecraft',
        'sugar',
        'tuna',
        'you',
        'wood'
    ]
    // save some time in the case that singular and plural are the same
    if (uncountable.indexOf(word.toLowerCase()) >= 0) {
        return word
    }
    // check for irregular forms
    for (const w in irregular) {
        const pattern = new RegExp(`${w}$`, 'i')
        const replace = irregular[w]
        if (pattern.test(word)) {
            return word.replace(pattern, replace)
        }
    }
    // check for matches using regular expressions
    for (const reg in plural) {
        const pattern = new RegExp(reg, 'i')
        if (pattern.test(word)) {
            return word.replace(pattern, plural[reg])
        }
    }
    return word
}

而单数形式为:

/**
 * Returns the singular of an English word.
 *
 * @export
 * @param {string} word
 * @param {number} [amount]
 * @returns {string}
 */
export function singular(word: string, amount?: number): string {
    if (amount !== undefined && amount !== 1) {
        return word
    }
    const singular: { [key: string]: string } = {
        '(quiz)zes$'             : "$1",
        '(matr)ices$'            : "$1ix",
        '(vert|ind)ices$'        : "$1ex",
        '^(ox)en$'               : "$1",
        '(alias)es$'             : "$1",
        '(octop|vir)i$'          : "$1us",
        '(cris|ax|test)es$'      : "$1is",
        '(shoe)s$'               : "$1",
        '(o)es$'                 : "$1",
        '(bus)es$'               : "$1",
        '([m|l])ice$'            : "$1ouse",
        '(x|ch|ss|sh)es$'        : "$1",
        '(m)ovies$'              : "$1ovie",
        '(s)eries$'              : "$1eries",
        '([^aeiouy]|qu)ies$'     : "$1y",
        '([lr])ves$'             : "$1f",
        '(tive)s$'               : "$1",
        '(hive)s$'               : "$1",
        '(li|wi|kni)ves$'        : "$1fe",
        '(shea|loa|lea|thie)ves$': "$1f",
        '(^analy)ses$'           : "$1sis",
        '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$': "$1$2sis",
        '([ti])a$'               : "$1um",
        '(n)ews$'                : "$1ews",
        '(h|bl)ouses$'           : "$1ouse",
        '(corpse)s$'             : "$1",
        '(us)es$'                : "$1",
        's$'                     : ""
    }
    const irregular: { [key: string]: string } = {
        'move'   : 'moves',
        'foot'   : 'feet',
        'goose'  : 'geese',
        'sex'    : 'sexes',
        'child'  : 'children',
        'man'    : 'men',
        'tooth'  : 'teeth',
        'person' : 'people'
    }
    const uncountable: string[] = [
        'sheep',
        'fish',
        'deer',
        'moose',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment',
        'bison',
        'cod',
        'offspring',
        'pike',
        'salmon',
        'shrimp',
        'swine',
        'trout',
        'aircraft',
        'hovercraft',
        'spacecraft',
        'sugar',
        'tuna',
        'you',
        'wood'
    ]
    // save some time in the case that singular and plural are the same
    if (uncountable.indexOf(word.toLowerCase()) >= 0) {
        return word
    }
    // check for irregular forms
    for (const w in irregular) {
        const pattern = new RegExp(`${irregular[w]}$`, 'i')
        const replace = w
        if (pattern.test(word)) {
            return word.replace(pattern, replace)
        }
    }
    // check for matches using regular expressions
    for (const reg in singular) {
        const pattern = new RegExp(reg, 'i')
        if (pattern.test(word)) {
            return word.replace(pattern, singular[reg])
        }
    }
    return word
}

谢谢您清理它。很多答案都会从像这样的纯函数和闭合形式函数中受益。❤️ - Gleno
只需要完美的答案,以下是在Angular中如何实现:https://gist.github.com/ezzabuzaid/1d49eac3c2887702636c0975a3a06ace - Ezzabuzaid
Fuses 返回 fus 而不是 fuse。Uses 返回 us 而不是 use。 - rory.ap
go 返回 gos,do 返回 dos - undefined

7

我使用这个简单的内联语句

const number = 2;
const string = `${number} trutle${number === 1 ? "" : "s"}`; //this one
console.log(string)


7

7

以下内容摘自我的博客:https://sergiotapia.me/pluralizing-strings-in-javascript-es6-b5d4d651d403


您可以使用pluralize库来实现这一点。

NPM:
npm install pluralize --save

Yarn:
yarn add pluralize

无论您想在哪里使用这个库,都可以轻松地进行 require。

var pluralize = require('pluralize')

我喜欢将它添加到window对象中,这样我就可以在需要的地方直接调用pluralize()。在我的application.js根文件中:

window.pluralize = require('pluralize')

然后你可以在任何地方使用它,包括React组件或纯JavaScript:
<span className="pull-left">
  {`${item.score} ${pluralize('point', item.score)}`}
</span>

console.log(pluralize('point', item.score))

19
我有点困惑,我的“使用Pluralize”问题的回答是否不够清楚,以至于你需要写这个回答? - Joshua Pinter

3
如果单词以 y 结尾,则根据倒数第二个字母使用 -ies 或 -s; 如果单词以 ‑s,-ss,-sh,-ch,-x 或 -z 结尾,请使用 -es; 如果单词是不规则复数形式,则使用查找表;否则,请使用 -s。请保留 HTML 标签。

var pluralize = (function () {
    const vowels = "aeiou";

    const irregulars = { "addendum": "addenda", "aircraft": "aircraft", "alumna": "alumnae", "alumnus": "alumni", "analysis": "analyses", "antenna": "antennae", "antithesis": "antitheses", "apex": "apices", "appendix": "appendices", "axis": "axes", "bacillus": "bacilli", "bacterium": "bacteria", "basis": "bases", "beau": "beaux", "bison": "bison", "bureau": "bureaux", "cactus": "cacti", "château": "châteaux", "child": "children", "codex": "codices", "concerto": "concerti", "corpus": "corpora", "crisis": "crises", "criterion": "criteria", "curriculum": "curricula", "datum": "data", "deer": "deer", "diagnosis": "diagnoses", "die": "dice", "dwarf": "dwarves", "ellipsis": "ellipses", "erratum": "errata", "faux pas": "faux pas", "fez": "fezzes", "fish": "fish", "focus": "foci", "foot": "feet", "formula": "formulae", "fungus": "fungi", "genus": "genera", "goose": "geese", "graffito": "graffiti", "grouse": "grouse", "half": "halves", "hoof": "hooves", "hypothesis": "hypotheses", "index": "indices", "larva": "larvae", "libretto": "libretti", "loaf": "loaves", "locus": "loci", "louse": "lice", "man": "men", "matrix": "matrices", "medium": "media", "memorandum": "memoranda", "minutia": "minutiae", "moose": "moose", "mouse": "mice", "nebula": "nebulae", "nucleus": "nuclei", "oasis": "oases", "offspring": "offspring", "opus": "opera", "ovum": "ova", "ox": "oxen", "parenthesis": "parentheses", "phenomenon": "phenomena", "phylum": "phyla", "quiz": "quizzes", "radius": "radii", "referendum": "referenda", "salmon": "salmon", "scarf": "scarves", "self": "selves", "series": "series", "sheep": "sheep", "shrimp": "shrimp", "species": "species", "stimulus": "stimuli", "stratum": "strata", "swine": "swine", "syllabus": "syllabi", "symposium": "symposia", "synopsis": "synopses", "tableau": "tableaux", "thesis": "theses", "thief": "thieves", "tooth": "teeth", "trout": "trout", "tuna": "tuna", "vertebra": "vertebrae", "vertex": "vertices", "vita": "vitae", "vortex": "vortices", "wharf": "wharves", "wife": "wives", "wolf": "wolves", "woman": "women", "guy": "guys", "buy": "buys", "person": "people" };

    function pluralize(word) {
        word = word.toLowerCase();


        if (irregulars[word]) {
            return irregulars[word];
        }

        if (word.length >= 2 && vowels.includes(word[word.length - 2])) {
            return word + "s";
        }

        if (word.endsWith("s") || word.endsWith("sh") || word.endsWith("ch") || word.endsWith("x") || word.endsWith("z")) {
            return word + "es";
        }

        if (word.endsWith("y")) {
            return word.slice(0, -1) + "ies";
        }


        return word + "s";
    }

    return pluralize;
})();

////////////////////////////////////////
console.log(pluralize("dog"));
console.log(pluralize("cat"));
console.log(pluralize("fox"));
console.log(pluralize("dwarf"));
console.log(pluralize("guy"));
console.log(pluralize("play"));

显然,这并不能支持所有英语边缘情况,但它支持最常见的情况。

需要一个更通用的测试,以检查以“y”结尾且倒数第二个字母是元音字母的单词。您正在检查“guy”和“buy”,但我认为它应该被概括,包括所有其他单词,例如“day”。if (word.length >= 2 && word[word.length - 2].toLowerCase().match(/[aeiou]/gi)) { return word + 's'; } - Andrew McGrath
@AndrewMcGrath 我已经在编辑中修复了它。 - Nirvana

2
我创建了一个非常简单的 JavaScript 库,可以用于单词复数形式。它透明地使用 CLDR 数据库支持多种语言环境,因此支持几乎任何您想要使用的语言。它的 API 非常简约,集成非常简单。它被称为 Numerous
我还写了一篇小介绍文章:《如何使用 JavaScript 在不同语言中将任何单词变为复数形式?》。
请随意在您的项目中使用它。我也很乐意听取您的反馈。

2
为了提供一种简单易读的选项(ES6):
export function pluralizeAndStringify(value, word, suffix = 's'){
   if (value == 1){
    return value + ' ' + word;
   }
   else {
    return value + ' ' + word + suffix;
   }
}

如果您输入类似于pluralizeAndStringify(5, 'dog')的内容,输出结果将会是"5 dogs"。

3
像"category"这样的词应该是"categories"。 - Mohammad Hossein
这很好,但当我执行pluralizeAndStringify(5,'man')时,却没有得到预期的5 men - Relcode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接