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
|
package i18n
|
||||||
|
|
||||||
|
import "dappco.re/go/core"
|
||||||
|
|
||||||
// TranslationContext provides disambiguation for translations.
|
// TranslationContext provides disambiguation for translations.
|
||||||
//
|
//
|
||||||
// T("direction.right", C("navigation")) // "rechts" (German)
|
// T("direction.right", C("navigation")) // "rechts" (German)
|
||||||
|
|
@ -9,12 +11,14 @@ type TranslationContext struct {
|
||||||
Gender string
|
Gender string
|
||||||
Location string
|
Location string
|
||||||
Formality Formality
|
Formality Formality
|
||||||
|
count int
|
||||||
|
countSet bool
|
||||||
Extra map[string]any
|
Extra map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// C creates a TranslationContext.
|
// C creates a TranslationContext.
|
||||||
func C(context string) *TranslationContext {
|
func C(context string) *TranslationContext {
|
||||||
return &TranslationContext{Context: context}
|
return &TranslationContext{Context: context, count: 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TranslationContext) WithGender(gender string) *TranslationContext {
|
func (c *TranslationContext) WithGender(gender string) *TranslationContext {
|
||||||
|
|
@ -57,6 +61,16 @@ func (c *TranslationContext) WithFormality(f Formality) *TranslationContext {
|
||||||
return c
|
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 {
|
func (c *TranslationContext) Set(key string, value any) *TranslationContext {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -109,3 +123,31 @@ func (c *TranslationContext) FormalityValue() Formality {
|
||||||
}
|
}
|
||||||
return c.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))
|
extra := make(map[string]any, len(values))
|
||||||
for key, value := range values {
|
for key, value := range values {
|
||||||
switch key {
|
switch key {
|
||||||
case "Context", "Gender", "Location", "Formality":
|
case "Context", "Gender", "Location", "Formality", "Count", "IsPlural":
|
||||||
continue
|
continue
|
||||||
case "Extra", "extra", "Extras", "extras":
|
case "Extra", "extra", "Extras", "extras":
|
||||||
mergeContextExtra(extra, value)
|
mergeContextExtra(extra, value)
|
||||||
|
|
@ -69,7 +69,8 @@ func contextMapValuesString(values map[string]string) map[string]any {
|
||||||
extra := make(map[string]any, len(values))
|
extra := make(map[string]any, len(values))
|
||||||
for key, value := range values {
|
for key, value := range values {
|
||||||
switch key {
|
switch key {
|
||||||
case "Context", "Gender", "Location", "Formality", "Extra", "extra", "Extras", "extras":
|
case "Context", "Gender", "Location", "Formality", "Count", "IsPlural",
|
||||||
|
"Extra", "extra", "Extras", "extras":
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
extra[key] = value
|
extra[key] = value
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ func TestC_Good(t *testing.T) {
|
||||||
assert.Equal(t, "navigation", ctx.Context)
|
assert.Equal(t, "navigation", ctx.Context)
|
||||||
assert.Equal(t, "navigation", ctx.ContextString())
|
assert.Equal(t, "navigation", ctx.ContextString())
|
||||||
assert.Equal(t, "navigation", ctx.String())
|
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) {
|
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) {
|
func TestTranslationContext_NilReceiver_Good(t *testing.T) {
|
||||||
var ctx *TranslationContext
|
var ctx *TranslationContext
|
||||||
|
|
||||||
|
assert.Nil(t, ctx.Count(2))
|
||||||
assert.Nil(t, ctx.WithGender("masculine"))
|
assert.Nil(t, ctx.WithGender("masculine"))
|
||||||
assert.Nil(t, ctx.In("workspace"))
|
assert.Nil(t, ctx.In("workspace"))
|
||||||
assert.Nil(t, ctx.Formal())
|
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.GenderString())
|
||||||
assert.Equal(t, "", ctx.LocationString())
|
assert.Equal(t, "", ctx.LocationString())
|
||||||
assert.Equal(t, FormalityNeutral, ctx.FormalityValue())
|
assert.Equal(t, FormalityNeutral, ctx.FormalityValue())
|
||||||
|
assert.Equal(t, 1, ctx.CountInt())
|
||||||
|
assert.Equal(t, "1", ctx.CountString())
|
||||||
|
assert.False(t, ctx.IsPlural())
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- WithGender ---
|
// --- WithGender ---
|
||||||
|
|
@ -119,12 +126,16 @@ func TestTranslationContext_Get_Bad_NilExtra(t *testing.T) {
|
||||||
|
|
||||||
func TestTranslationContext_FullChain_Good(t *testing.T) {
|
func TestTranslationContext_FullChain_Good(t *testing.T) {
|
||||||
ctx := C("medical").
|
ctx := C("medical").
|
||||||
|
Count(3).
|
||||||
WithGender("feminine").
|
WithGender("feminine").
|
||||||
In("clinic").
|
In("clinic").
|
||||||
Formal().
|
Formal().
|
||||||
Set("speciality", "cardiology")
|
Set("speciality", "cardiology")
|
||||||
|
|
||||||
assert.Equal(t, "medical", ctx.ContextString())
|
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, "feminine", ctx.GenderString())
|
||||||
assert.Equal(t, "clinic", ctx.LocationString())
|
assert.Equal(t, "clinic", ctx.LocationString())
|
||||||
assert.Equal(t, FormalityFormal, ctx.FormalityValue())
|
assert.Equal(t, FormalityFormal, ctx.FormalityValue())
|
||||||
|
|
|
||||||
10
i18n.go
10
i18n.go
|
|
@ -252,11 +252,21 @@ func templateDataForRendering(data any) any {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return 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{
|
rendered := map[string]any{
|
||||||
"Context": v.Context,
|
"Context": v.Context,
|
||||||
"Gender": v.Gender,
|
"Gender": v.Gender,
|
||||||
"Location": v.Location,
|
"Location": v.Location,
|
||||||
"Formality": v.Formality,
|
"Formality": v.Formality,
|
||||||
|
"Count": count,
|
||||||
|
"IsPlural": count != 1,
|
||||||
"Extra": v.Extra,
|
"Extra": v.Extra,
|
||||||
}
|
}
|
||||||
for key, value := range 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()
|
return d.CountInt()
|
||||||
case *TranslationContext:
|
case *TranslationContext:
|
||||||
if d == nil || d.Extra == nil {
|
if d == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if c, ok := d.Extra["Count"]; ok {
|
if count, ok := d.countValue(); ok {
|
||||||
return toInt(c)
|
return count
|
||||||
}
|
}
|
||||||
if c, ok := d.Extra["count"]; ok {
|
if d.Extra != nil {
|
||||||
return toInt(c)
|
if c, ok := d.Extra["Count"]; ok {
|
||||||
|
return toInt(c)
|
||||||
|
}
|
||||||
|
if c, ok := d.Extra["count"]; ok {
|
||||||
|
return toInt(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue