Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1128 +/- ##
==========================================
+ Coverage 66.81% 68.44% +1.62%
==========================================
Files 214 221 +7
Lines 14050 14319 +269
==========================================
+ Hits 9387 9800 +413
+ Misses 4287 4144 -143
+ Partials 376 375 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.
| Benchmark suite | Current: 72966f3 | Previous: bff7225 | Ratio |
|---|---|---|---|
BenchmarkFile_ReadWrite |
329027 ns/op 6202 B/op 97 allocs/op |
205065 ns/op 6201 B/op 97 allocs/op |
1.60 |
BenchmarkFile_ReadWrite - ns/op |
329027 ns/op |
205065 ns/op |
1.60 |
This comment was automatically generated by workflow using github-action-benchmark.
|
Thanks, will check this after v1.16 is released. |
|
And please add the |
|
@hwbrzzl , this package is needed to resolve the table name from the struct name. Currently, it only supports English, but it can be easily extended to support other languages. Ref: https://laravel.com/docs/12.x/localization#pluralization-language |
support/pluralizer/english.go
Outdated
There was a problem hiding this comment.
Where does the rule come from, please?
There was a problem hiding this comment.
I referred to multiple sources:
support/pluralizer/pluralizer.go
Outdated
| } | ||
|
|
||
| func Plural(word string) string { | ||
| return instance.Plural(word) |
There was a problem hiding this comment.
There is no way to add and switch language, add a UseLanguage as well?
There was a problem hiding this comment.
Yeah, I have introduced a new method for this
support/pluralizer/inflector.go
Outdated
| "unicode" | ||
| ) | ||
|
|
||
| type inflector struct { |
There was a problem hiding this comment.
Can it be used by user when implementing a new language?
There was a problem hiding this comment.
Yeah, I’ve exposed two new methods for this:
UseLanguage: Switches the current global language if it already existsRegisterLanguage: Allows you to register a new language
** recommended to use during application boot time
There was a problem hiding this comment.
Em, the logic is a bit complicated now. If so, I don't suggest releasing it to v1.16. A deep review is required. Hence, we can delay this PR as well, given that the another related PR (table -> model) spent many days to be merged.
We don't need to merge them hurriedly, it's fine to release them in v1.17. As we know, errors always appear in this situation.
|
Yeah, pluralization is a bit tricky, so the logic ended up being a bit more complex. The earlier version wasn’t very flexible, so I changed it to make it more future-proof. No worries, take your time, we can include it in V1.17. The second PR depends on this, so we can delay that too. |
|
@hwbrzzl Converting this PR to a draft for now, need to do more research on English word dictionaries. I'm exploring different sources to improve pattern accuracy. |
|
This package may not always get pluralization and singularization 100% right, since English (like many languages) has lots of exceptions. But the goal is to keep improving it over time as we come across edge cases. Think of it as a work in progress that gets better the more we use it. |
Yes, we can provide a way for users to extend the special cases. |
I've added a few methods to allow users to extend irregular and uninflected cases. |
hwbrzzl
left a comment
There was a problem hiding this comment.
Amazing PR, could you add the usage in the description? And I wonder if there is an existing package that can implement such features as well?
support/pluralizer/init.go
Outdated
| func RegisterIrregular(lang string, substitutions ...pluralizer.Substitution) bool { | ||
| if len(substitutions) == 0 { | ||
| return false | ||
| } | ||
|
|
||
| language, factory, exists := getLanguageInstance(lang) | ||
| if !exists { | ||
| return false | ||
| } | ||
|
|
||
| language.PluralRuleset().AddIrregular(substitutions...) | ||
|
|
||
| flipped := rules.GetFlippedSubstitutions(substitutions...) | ||
| language.SingularRuleset().AddIrregular(flipped...) | ||
|
|
||
| factory.SetLanguage(language) | ||
| return true | ||
| } | ||
|
|
||
| func RegisterUninflected(lang string, words ...string) bool { | ||
| if len(words) == 0 { | ||
| return false | ||
| } | ||
|
|
||
| language, factory, exists := getLanguageInstance(lang) | ||
| if !exists { | ||
| return false | ||
| } | ||
|
|
||
| language.PluralRuleset().AddUninflected(words...) | ||
| language.SingularRuleset().AddUninflected(words...) | ||
|
|
||
| factory.SetLanguage(language) | ||
| return true | ||
| } | ||
|
|
||
| func RegisterPluralUninflected(lang string, words ...string) bool { | ||
| if len(words) == 0 { | ||
| return false | ||
| } | ||
|
|
||
| language, factory, exists := getLanguageInstance(lang) | ||
| if !exists { | ||
| return false | ||
| } | ||
|
|
||
| language.PluralRuleset().AddUninflected(words...) | ||
| factory.SetLanguage(language) | ||
| return true | ||
| } | ||
|
|
||
| func RegisterSingularUninflected(lang string, words ...string) bool { | ||
| if len(words) == 0 { | ||
| return false | ||
| } | ||
|
|
||
| language, factory, exists := getLanguageInstance(lang) | ||
| if !exists { | ||
| return false | ||
| } | ||
|
|
||
| language.SingularRuleset().AddUninflected(words...) | ||
| factory.SetLanguage(language) | ||
| return true | ||
| } |
There was a problem hiding this comment.
How about add these functions to type Language interface? When users want to extend:
language := pluralizer.GetLanguage()
language.SingularRuleset.Add...
language.PluralRuleset.Add...
language.Add...There was a problem hiding this comment.
No, if we add these methods to Language interface, the user would have to implement them for each new custom language, which is unnecessary since the logic will be the same for all languages. Language interface should be minimal.
Yeah, there are some existing packages, but most of them are limited to English. If we want to support additional languages in the future, they won’t be sufficient. So it's better to implement it ourselves. Examples: |
…to kkumar-gcc/str-plural-method
| PluralizerNoSubstitutionsGiven = New("no substitutions provided").SetModule(ModulePluralizer) | ||
| PluralizerNoWordsGiven = New("no words provided").SetModule(ModulePluralizer) |
There was a problem hiding this comment.
Not sure if we should throw an error in these two cases.
hwbrzzl
left a comment
There was a problem hiding this comment.
LGTM, please update the description.
hwbrzzl
left a comment
There was a problem hiding this comment.
Please add str.Plural and str.Singular in the description as well.
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a comprehensive pluralizer package for the Goravel framework, providing English word inflection capabilities (singular ↔ plural) with an extensible architecture for supporting additional languages. The implementation includes integration with the existing string utility package and follows established design patterns with comprehensive test coverage.
Key changes include:
- Complete pluralizer package with inflector engine, English language rules, and pattern-based transformation system
- Integration with the str package through new Plural() and Singular() methods
- Comprehensive contract interfaces and error handling for language management
Reviewed Changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| support/str/str.go | Added Plural() and Singular() methods with optional count parameter |
| support/str/str_test.go | Comprehensive test coverage for new string inflection methods |
| support/pluralizer/ | Complete pluralizer package implementation with rules engine |
| contracts/support/pluralizer/ | Interface contracts for language, inflector, and rule components |
| errors/ | New pluralizer-specific error definitions and module registration |
| mocks/support/pluralizer/ | Generated mock implementations for all pluralizer interfaces |
Comments suppressed due to low confidence (13)
support/pluralizer/inflector/inflector.go:19
- [nitpick] The receiver variable name 'r' is ambiguous for an Inflector type. Consider using 'i' or 'inf' to better represent the type.
func (r *Inflector) Language() pluralizer.Language {
support/pluralizer/inflector/inflector.go:23
- [nitpick] The receiver variable name 'r' is ambiguous for an Inflector type. Consider using 'i' or 'inf' to better represent the type.
func (r *Inflector) Plural(word string) string {
support/pluralizer/inflector/inflector.go:27
- [nitpick] The receiver variable name 'r' is ambiguous for an Inflector type. Consider using 'i' or 'inf' to better represent the type.
func (r *Inflector) SetLanguage(language pluralizer.Language) pluralizer.Inflector {
support/pluralizer/inflector/inflector.go:33
- [nitpick] The receiver variable name 'r' is ambiguous for an Inflector type. Consider using 'i' or 'inf' to better represent the type.
func (r *Inflector) Singular(word string) string {
support/pluralizer/inflector/inflector.go:37
- [nitpick] The receiver variable name 'r' is ambiguous for an Inflector type. Consider using 'i' or 'inf' to better represent the type.
func (r *Inflector) inflect(word string, ruleset pluralizer.Ruleset) string {
support/pluralizer/rules/transformation.go:23
- [nitpick] The receiver variable name 'r' is ambiguous for a Transformation type. Consider using 't' or 'trans' to better represent the type.
func (r *Transformation) Apply(word string) string {
support/pluralizer/rules/substitution.go:21
- [nitpick] The receiver variable name 'r' is ambiguous for a Substitution type. Consider using 's' or 'sub' to better represent the type.
func (r *Substitution) From() string {
support/pluralizer/rules/substitution.go:25
- [nitpick] The receiver variable name 'r' is ambiguous for a Substitution type. Consider using 's' or 'sub' to better represent the type.
func (r *Substitution) To() string {
support/pluralizer/rules/ruleset.go:21
- [nitpick] The receiver variable name 'r' is ambiguous for a Ruleset type. Consider using 'rs' or 'ruleset' to better represent the type.
func (r *Ruleset) AddIrregular(substitutions ...pluralizer.Substitution) pluralizer.Ruleset {
support/pluralizer/rules/pattern.go:20
- [nitpick] The receiver variable name 'r' is ambiguous for a Pattern type. Consider using 'p' or 'pat' to better represent the type.
func (r *Pattern) Matches(word string) bool {
support/pluralizer/english/english.go:63
- [nitpick] The receiver variable name 'r' is ambiguous for a Language type. Consider using 'l' or 'lang' to better represent the type.
func (r *Language) Name() string {
support/pluralizer/english/english.go:67
- [nitpick] The receiver variable name 'r' is ambiguous for a Language type. Consider using 'l' or 'lang' to better represent the type.
func (r *Language) SingularRuleset() pluralizer.Ruleset {
support/pluralizer/english/english.go:71
- [nitpick] The receiver variable name 'r' is ambiguous for a Language type. Consider using 'l' or 'lang' to better represent the type.
func (r *Language) PluralRuleset() pluralizer.Ruleset {
📑 Description
Closes goravel/goravel#731
What’s in this PR
pluralizerpackage for GoQuick Start
Swap the Global Inflector
Do this at app startup (not inside goroutines)
Custom Rules
Add Your Own Inflector
Implement this interface:
Register & use it in
init():✅ Checks