feat(i18n): add count support to translation context
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
996fb4c849
commit
92add102a8
5 changed files with 77 additions and 8 deletions
44
context.go
44
context.go
|
|
@ -1,5 +1,7 @@
|
|||
package i18n
|
||||
|
||||
import "dappco.re/go/core"
|
||||
|
||||
// TranslationContext provides disambiguation for translations.
|
||||
//
|
||||
// T("direction.right", C("navigation")) // "rechts" (German)
|
||||
|
|
@ -9,12 +11,14 @@ type TranslationContext struct {
|
|||
Gender string
|
||||
Location string
|
||||
Formality Formality
|
||||
count int
|
||||
countSet bool
|
||||
Extra map[string]any
|
||||
}
|
||||
|
||||
// C creates a TranslationContext.
|
||||
func C(context string) *TranslationContext {
|
||||
return &TranslationContext{Context: context}
|
||||
return &TranslationContext{Context: context, count: 1}
|
||||
}
|
||||
|
||||
func (c *TranslationContext) WithGender(gender string) *TranslationContext {
|
||||
|
|
@ -57,6 +61,16 @@ func (c *TranslationContext) WithFormality(f Formality) *TranslationContext {
|
|||
return c
|
||||
}
|
||||
|
||||
// Count sets the count used for plural-sensitive translations.
|
||||
func (c *TranslationContext) Count(n int) *TranslationContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
c.count = n
|
||||
c.countSet = true
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *TranslationContext) Set(key string, value any) *TranslationContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
|
|
@ -109,3 +123,31 @@ func (c *TranslationContext) FormalityValue() Formality {
|
|||
}
|
||||
return c.Formality
|
||||
}
|
||||
|
||||
// CountInt returns the current count value.
|
||||
func (c *TranslationContext) CountInt() int {
|
||||
if c == nil {
|
||||
return 1
|
||||
}
|
||||
return c.count
|
||||
}
|
||||
|
||||
// CountString returns the current count value formatted as text.
|
||||
func (c *TranslationContext) CountString() string {
|
||||
if c == nil {
|
||||
return "1"
|
||||
}
|
||||
return core.Sprintf("%d", c.count)
|
||||
}
|
||||
|
||||
// IsPlural reports whether the count is plural.
|
||||
func (c *TranslationContext) IsPlural() bool {
|
||||
return c != nil && c.count != 1
|
||||
}
|
||||
|
||||
func (c *TranslationContext) countValue() (int, bool) {
|
||||
if c == nil {
|
||||
return 1, false
|
||||
}
|
||||
return c.count, c.countSet
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func contextMapValuesAny(values map[string]any) map[string]any {
|
|||
extra := make(map[string]any, len(values))
|
||||
for key, value := range values {
|
||||
switch key {
|
||||
case "Context", "Gender", "Location", "Formality":
|
||||
case "Context", "Gender", "Location", "Formality", "Count", "IsPlural":
|
||||
continue
|
||||
case "Extra", "extra", "Extras", "extras":
|
||||
mergeContextExtra(extra, value)
|
||||
|
|
@ -69,7 +69,8 @@ func contextMapValuesString(values map[string]string) map[string]any {
|
|||
extra := make(map[string]any, len(values))
|
||||
for key, value := range values {
|
||||
switch key {
|
||||
case "Context", "Gender", "Location", "Formality", "Extra", "extra", "Extras", "extras":
|
||||
case "Context", "Gender", "Location", "Formality", "Count", "IsPlural",
|
||||
"Extra", "extra", "Extras", "extras":
|
||||
continue
|
||||
default:
|
||||
extra[key] = value
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ func TestC_Good(t *testing.T) {
|
|||
assert.Equal(t, "navigation", ctx.Context)
|
||||
assert.Equal(t, "navigation", ctx.ContextString())
|
||||
assert.Equal(t, "navigation", ctx.String())
|
||||
assert.Equal(t, 1, ctx.CountInt())
|
||||
assert.Equal(t, "1", ctx.CountString())
|
||||
assert.False(t, ctx.IsPlural())
|
||||
}
|
||||
|
||||
func TestC_Good_EmptyContext(t *testing.T) {
|
||||
|
|
@ -28,6 +31,7 @@ func TestC_Good_EmptyContext(t *testing.T) {
|
|||
func TestTranslationContext_NilReceiver_Good(t *testing.T) {
|
||||
var ctx *TranslationContext
|
||||
|
||||
assert.Nil(t, ctx.Count(2))
|
||||
assert.Nil(t, ctx.WithGender("masculine"))
|
||||
assert.Nil(t, ctx.In("workspace"))
|
||||
assert.Nil(t, ctx.Formal())
|
||||
|
|
@ -39,6 +43,9 @@ func TestTranslationContext_NilReceiver_Good(t *testing.T) {
|
|||
assert.Equal(t, "", ctx.GenderString())
|
||||
assert.Equal(t, "", ctx.LocationString())
|
||||
assert.Equal(t, FormalityNeutral, ctx.FormalityValue())
|
||||
assert.Equal(t, 1, ctx.CountInt())
|
||||
assert.Equal(t, "1", ctx.CountString())
|
||||
assert.False(t, ctx.IsPlural())
|
||||
}
|
||||
|
||||
// --- WithGender ---
|
||||
|
|
@ -119,12 +126,16 @@ func TestTranslationContext_Get_Bad_NilExtra(t *testing.T) {
|
|||
|
||||
func TestTranslationContext_FullChain_Good(t *testing.T) {
|
||||
ctx := C("medical").
|
||||
Count(3).
|
||||
WithGender("feminine").
|
||||
In("clinic").
|
||||
Formal().
|
||||
Set("speciality", "cardiology")
|
||||
|
||||
assert.Equal(t, "medical", ctx.ContextString())
|
||||
assert.Equal(t, 3, ctx.CountInt())
|
||||
assert.Equal(t, "3", ctx.CountString())
|
||||
assert.True(t, ctx.IsPlural())
|
||||
assert.Equal(t, "feminine", ctx.GenderString())
|
||||
assert.Equal(t, "clinic", ctx.LocationString())
|
||||
assert.Equal(t, FormalityFormal, ctx.FormalityValue())
|
||||
|
|
|
|||
10
i18n.go
10
i18n.go
|
|
@ -252,11 +252,21 @@ func templateDataForRendering(data any) any {
|
|||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
count, explicit := v.countValue()
|
||||
if !explicit && v.Extra != nil {
|
||||
if c, ok := v.Extra["Count"]; ok {
|
||||
count = toInt(c)
|
||||
} else if c, ok := v.Extra["count"]; ok {
|
||||
count = toInt(c)
|
||||
}
|
||||
}
|
||||
rendered := map[string]any{
|
||||
"Context": v.Context,
|
||||
"Gender": v.Gender,
|
||||
"Location": v.Location,
|
||||
"Formality": v.Formality,
|
||||
"Count": count,
|
||||
"IsPlural": count != 1,
|
||||
"Extra": v.Extra,
|
||||
}
|
||||
for key, value := range v.Extra {
|
||||
|
|
|
|||
15
transform.go
15
transform.go
|
|
@ -11,14 +11,19 @@ func getCount(data any) int {
|
|||
}
|
||||
return d.CountInt()
|
||||
case *TranslationContext:
|
||||
if d == nil || d.Extra == nil {
|
||||
if d == nil {
|
||||
return 0
|
||||
}
|
||||
if c, ok := d.Extra["Count"]; ok {
|
||||
return toInt(c)
|
||||
if count, ok := d.countValue(); ok {
|
||||
return count
|
||||
}
|
||||
if c, ok := d.Extra["count"]; ok {
|
||||
return toInt(c)
|
||||
if d.Extra != nil {
|
||||
if c, ok := d.Extra["Count"]; ok {
|
||||
return toInt(c)
|
||||
}
|
||||
if c, ok := d.Extra["count"]; ok {
|
||||
return toInt(c)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
case map[string]any:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue