refactor(i18n): standardise code patterns and naming

- Add nil safety to chainable Subject methods (Count, Gender, In, Formal, Informal, Formality)
- Rename getters to use return type suffix pattern:
  - CountValue → CountInt
  - GenderValue → GenderString
  - Location → LocationString
  - NounValue → NounString
  - FormalityValue → FormalityString
- Fix comment style for plural rule functions to follow Go doc conventions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-30 17:53:11 +00:00
parent 1c7ff0511b
commit 9645cea7d6
3 changed files with 70 additions and 34 deletions

View file

@ -27,53 +27,77 @@ func NewSubject(noun string, value any) *Subject {
// Count sets the count for pluralization.
// Used to determine singular/plural forms in templates.
// Panics if called on nil receiver; use S() to create subjects.
//
// S("file", files).Count(len(files))
func (s *Subject) Count(n int) *Subject {
if s == nil {
return nil
}
s.count = n
return s
}
// Gender sets the grammatical gender for languages that require it.
// Common values: "masculine", "feminine", "neuter"
// Panics if called on nil receiver; use S() to create subjects.
//
// S("user", user).Gender("female")
func (s *Subject) Gender(g string) *Subject {
if s == nil {
return nil
}
s.gender = g
return s
}
// In sets the location context for the subject.
// Used in templates to provide spatial context.
// Panics if called on nil receiver; use S() to create subjects.
//
// S("file", "config.yaml").In("workspace")
func (s *Subject) In(location string) *Subject {
if s == nil {
return nil
}
s.location = location
return s
}
// Formal sets the formality level to formal (Sie, vous, usted).
// Use for polite/professional address in languages that distinguish formality.
// Panics if called on nil receiver; use S() to create subjects.
//
// S("colleague", name).Formal()
func (s *Subject) Formal() *Subject {
if s == nil {
return nil
}
s.formality = FormalityFormal
return s
}
// Informal sets the formality level to informal (du, tu, tú).
// Use for casual/friendly address in languages that distinguish formality.
// Panics if called on nil receiver; use S() to create subjects.
//
// S("friend", name).Informal()
func (s *Subject) Informal() *Subject {
if s == nil {
return nil
}
s.formality = FormalityInformal
return s
}
// Formality sets the formality level explicitly.
// Panics if called on nil receiver; use S() to create subjects.
//
// S("user", name).Formality(FormalityFormal)
func (s *Subject) Formality(f Formality) *Subject {
if s == nil {
return nil
}
s.formality = f
return s
}
@ -94,41 +118,41 @@ func (s *Subject) IsPlural() bool {
return s != nil && s.count != 1
}
// CountValue returns the count value.
func (s *Subject) CountValue() int {
// CountInt returns the count value.
func (s *Subject) CountInt() int {
if s == nil {
return 1
}
return s.count
}
// GenderValue returns the grammatical gender.
func (s *Subject) GenderValue() string {
// GenderString returns the grammatical gender.
func (s *Subject) GenderString() string {
if s == nil {
return ""
}
return s.gender
}
// Location returns the location context.
func (s *Subject) Location() string {
// LocationString returns the location context.
func (s *Subject) LocationString() string {
if s == nil {
return ""
}
return s.location
}
// NounValue returns the noun type.
func (s *Subject) NounValue() string {
// NounString returns the noun type.
func (s *Subject) NounString() string {
if s == nil {
return ""
}
return s.Noun
}
// FormalityValue returns the formality level.
// FormalityString returns the formality level.
// Returns FormalityNeutral if not explicitly set.
func (s *Subject) FormalityValue() Formality {
func (s *Subject) FormalityString() Formality {
if s == nil {
return FormalityNeutral
}

View file

@ -33,26 +33,26 @@ func TestSubject_Good(t *testing.T) {
t.Run("with count", func(t *testing.T) {
s := S("file", "*.go").Count(5)
assert.Equal(t, 5, s.CountValue())
assert.Equal(t, 5, s.CountInt())
assert.True(t, s.IsPlural())
})
t.Run("with gender", func(t *testing.T) {
s := S("user", "alice").Gender("female")
assert.Equal(t, "female", s.GenderValue())
assert.Equal(t, "female", s.GenderString())
})
t.Run("with location", func(t *testing.T) {
s := S("file", "config.yaml").In("workspace")
assert.Equal(t, "workspace", s.Location())
assert.Equal(t, "workspace", s.LocationString())
})
t.Run("chained methods", func(t *testing.T) {
s := S("repo", "core-php").Count(3).Gender("neuter").In("organisation")
assert.Equal(t, "repo", s.NounValue())
assert.Equal(t, 3, s.CountValue())
assert.Equal(t, "neuter", s.GenderValue())
assert.Equal(t, "organisation", s.Location())
assert.Equal(t, "repo", s.NounString())
assert.Equal(t, 3, s.CountInt())
assert.Equal(t, "neuter", s.GenderString())
assert.Equal(t, "organisation", s.LocationString())
})
}
@ -104,10 +104,10 @@ func TestSubject_IsPlural(t *testing.T) {
func TestSubject_Getters(t *testing.T) {
t.Run("nil safety", func(t *testing.T) {
var s *Subject
assert.Equal(t, "", s.NounValue())
assert.Equal(t, 1, s.CountValue())
assert.Equal(t, "", s.GenderValue())
assert.Equal(t, "", s.Location())
assert.Equal(t, "", s.NounString())
assert.Equal(t, 1, s.CountInt())
assert.Equal(t, "", s.GenderString())
assert.Equal(t, "", s.LocationString())
})
}
@ -193,31 +193,31 @@ func TestNewTemplateData(t *testing.T) {
func TestSubject_Formality(t *testing.T) {
t.Run("default is neutral", func(t *testing.T) {
s := S("user", "name")
assert.Equal(t, FormalityNeutral, s.FormalityValue())
assert.Equal(t, FormalityNeutral, s.FormalityString())
assert.False(t, s.IsFormal())
assert.False(t, s.IsInformal())
})
t.Run("Formal()", func(t *testing.T) {
s := S("user", "name").Formal()
assert.Equal(t, FormalityFormal, s.FormalityValue())
assert.Equal(t, FormalityFormal, s.FormalityString())
assert.True(t, s.IsFormal())
})
t.Run("Informal()", func(t *testing.T) {
s := S("user", "name").Informal()
assert.Equal(t, FormalityInformal, s.FormalityValue())
assert.Equal(t, FormalityInformal, s.FormalityString())
assert.True(t, s.IsInformal())
})
t.Run("Formality() explicit", func(t *testing.T) {
s := S("user", "name").Formality(FormalityFormal)
assert.Equal(t, FormalityFormal, s.FormalityValue())
assert.Equal(t, FormalityFormal, s.FormalityString())
})
t.Run("nil safety", func(t *testing.T) {
var s *Subject
assert.Equal(t, FormalityNeutral, s.FormalityValue())
assert.Equal(t, FormalityNeutral, s.FormalityString())
assert.False(t, s.IsFormal())
assert.False(t, s.IsInformal())
})

View file

@ -67,7 +67,8 @@ func IsRTLLanguage(lang string) bool {
return false
}
// English: one (n=1), other
// pluralRuleEnglish returns the plural category for English.
// Categories: one (n=1), other.
func pluralRuleEnglish(n int) PluralCategory {
if n == 1 {
return PluralOne
@ -75,12 +76,14 @@ func pluralRuleEnglish(n int) PluralCategory {
return PluralOther
}
// German: same as English
// pluralRuleGerman returns the plural category for German.
// Categories: same as English.
func pluralRuleGerman(n int) PluralCategory {
return pluralRuleEnglish(n)
}
// French: one (n=0,1), other
// pluralRuleFrench returns the plural category for French.
// Categories: one (n=0,1), other.
func pluralRuleFrench(n int) PluralCategory {
if n == 0 || n == 1 {
return PluralOne
@ -88,7 +91,8 @@ func pluralRuleFrench(n int) PluralCategory {
return PluralOther
}
// Spanish: one (n=1), many (n=0 or n>=1000000), other
// pluralRuleSpanish returns the plural category for Spanish.
// Categories: one (n=1), other.
func pluralRuleSpanish(n int) PluralCategory {
if n == 1 {
return PluralOne
@ -96,7 +100,8 @@ func pluralRuleSpanish(n int) PluralCategory {
return PluralOther
}
// Russian: one (n%10=1, n%100!=11), few (n%10=2-4, n%100!=12-14), many (others)
// pluralRuleRussian returns the plural category for Russian.
// Categories: one (n%10=1, n%100!=11), few (n%10=2-4, n%100!=12-14), many (others).
func pluralRuleRussian(n int) PluralCategory {
mod10 := n % 10
mod100 := n % 100
@ -110,7 +115,8 @@ func pluralRuleRussian(n int) PluralCategory {
return PluralMany
}
// Polish: one (n=1), few (n%10=2-4, n%100!=12-14), many (others)
// pluralRulePolish returns the plural category for Polish.
// Categories: one (n=1), few (n%10=2-4, n%100!=12-14), many (others).
func pluralRulePolish(n int) PluralCategory {
if n == 1 {
return PluralOne
@ -123,7 +129,8 @@ func pluralRulePolish(n int) PluralCategory {
return PluralMany
}
// Arabic: zero (n=0), one (n=1), two (n=2), few (n%100=3-10), many (n%100=11-99), other
// pluralRuleArabic returns the plural category for Arabic.
// Categories: zero (n=0), one (n=1), two (n=2), few (n%100=3-10), many (n%100=11-99), other.
func pluralRuleArabic(n int) PluralCategory {
if n == 0 {
return PluralZero
@ -144,15 +151,20 @@ func pluralRuleArabic(n int) PluralCategory {
return PluralOther
}
// Chinese/Japanese/Korean: other (no plural distinction)
// pluralRuleChinese returns the plural category for Chinese.
// Categories: other (no plural distinction).
func pluralRuleChinese(n int) PluralCategory {
return PluralOther
}
// pluralRuleJapanese returns the plural category for Japanese.
// Categories: other (no plural distinction).
func pluralRuleJapanese(n int) PluralCategory {
return PluralOther
}
// pluralRuleKorean returns the plural category for Korean.
// Categories: other (no plural distinction).
func pluralRuleKorean(n int) PluralCategory {
return PluralOther
}