Beautiful and natural-sounding text in human interfaces.
`Add ${count} person to group.`
// ❌ "Add 2 person to group."
g`Add ${count} person to group.`
// ✅ "Add 2 people to group." |
Lightweight automatic grammar agreement.
Warning
Experimental: This is an experimental JavaScript API for automatic grammar agreement, modeled after the equivalent Swift API.
-
✅ What it is: A lightweight engine for automatic grammar agreement – scoped specifically for human interfaces in software products. Provide fast, automatic grammar agreement to cover ~99% of real-world cases. Take the edge-case handling out of UI.
-
❌ What it is NOT: Perfect or feature-complete grammar correction, stemming/lemmatization, lexical analysis, or sentiment analysis. This package will never
Intl.MessageFormat provides manual support for plural/selects. But it is entirely manual, and up to developers to predict all variance. It's also very challenging to correctly translate any string where a parameter (i.e. a noun) is interpolated.
Example:
const notification = t("I was in {country}")
// German: "Ich war in {country}"
const switzerland = t("Switzerland")
// German: "die Schweiz"
notification.format({ country: switzerland })
// "Ich war in die Schweiz" ❌
// The feminine dative article is "der" (not
// "die") so it should instead be:
// "Ich war in der Schweiz" ✅
In this example if you supported 180+ countries, you might have a much bigger problem.
So to summarize, ICU MessageFormat is a great tool, but its focused primarily on a different problem.
import { Grammar, Plurals } from "gcomply";
Plurals.getLang("en").addTagged(Grammar.PartOfSpeech.noun,
{ one: "sausage", other: "sausages" },
{ one: "twist", other: "twists" },
);
- ⚡️ Instant grammar agreement — plurals, gender, articles, etc.
- 📦 Highly pluggable — compatible with any other framework
- 🌐 Localizable — add support for any natural language
- 🪶 Lightweight — <5kB, zero dependencies
Add custom transforms, domain terminology, nouns, pronouns, and more.
- Syntax:
g``
The g
tagged template will coerce inline grammar
(in accordance to any global options).
import { g } from "gcomply";
let views = content.viewCount,
shares = content.shareCount
let text = g`${views} new views, ${shares} new shares`
// "35 new views, 1 new share"
- Syntax:
inflect(text, options?)
The inflect()
function provides a little more flexibility for complex
grammatical agreement, and may also be passed additional external options:
import { inflect } from "gcomply";
let text = "Votre conseiller est prêt."
let opts = {
language: "fr",
morphology: { grammaticalGender: "feminine" }
}
inflect(text) // "Votre conseiller est prêt."
inflect(text, opts) // "Votre conseillère est prête."
Note
Terminology:
Parts of the API terminology is modeled to be consistent with the emerging equivalent Swift APIs for morphology and inflection.
Notably though, there is no direct proxy for Swift's AttributedString in JavaScript.
These are the Swift agreement features:
Status | Agreement Type | Methodology | Code Changes | Description |
---|---|---|---|---|
partial | inflect |
Proximity-based | No | Reference neighboring elements. |
❌ | agreeWithArgument |
Explicit reference, same string | No | Directly reference other elements in the strings. |
❌ | agreeWithConcept |
Explicit reference, injected | Yes | Agree with a Concept passed in via context. |
Note
Source: See WWDC2023 @ 6:15
Contributions are welcome, such as:
- New language packs
- More granular inflection rules
- Bug fixes and test cases
- Integrations for other frameworks or i18n tools
Important
Keep in mind, the goal of this package is not to provide perfect and comprehensive language support that covers all possible permutations out-of-the-box.