Skip to content

Commit

Permalink
Finalizing tests, extra readme details
Browse files Browse the repository at this point in the history
  • Loading branch information
samlown committed Mar 15, 2024
1 parent e668dae commit 2c6aa97
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/setup-go@v4
with:
go-version: "1.19"
go-version: "1.21"
cache: false

- name: Check out code
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: "1.19"
go-version: "1.21"
id: go

- name: Check out code
Expand Down
39 changes: 33 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ Key Features:
- Supports Go's embed FS to load data from inside binary.
- Short method names and usage to Gettext like `i18n.T()` or `i18n.N()`.
- Support for simple interpolation using keys, e.g. `Some %{key} text`
- Built in support for [templ templating](https://templ.guide/) which uses context throughout.

## Usage

Expand Down Expand Up @@ -73,19 +72,19 @@ Getting translations is straightforward, you have two options:
1. call methods defined in the package with the context, or,
2. extract the locale from the context and use.

To translate without extracting the locale, you'll need to load the `i18n` helper package:
To translate without extracting the locale, you'll need to load the `i18n` package which contains all the structures and methods used by the main `ctxi18n` without any globals:

```go
import "github.com/invopop/ctxi18n/i18n"
```

This package contains helper methods that allow you to use the context directly:
Then use it with the context:

```go
fmt.Println(i18n.T(ctx, "welcome.title"))
```

Notice in the example that the `title` was previously defined inside the `welcome` object in the source YAML, and we're accessing it here by defining the path `welcome.title`.
Notice in the example that `title` was previously defined inside the `welcome` object in the source YAML, and we're accessing it here by defining the path `welcome.title`.

To use the `Locale` object directly, extract it from the context and call the methods:

Expand Down Expand Up @@ -139,7 +138,7 @@ en:
other: "You have %{count} emails.
```

The `inbox.emails` tag has a sub-object that defines all the translations we need according to the pluralization rules of the language. In the case of English and the default rule set, `zero` is an optional definition that will be used if provided.
The `inbox.emails` tag has a sub-object that defines all the translations we need according to the pluralization rules of the language. In the case of English which uses the default rule set, `zero` is an optional definition that will be used if provided and fallback on `other` if not.

To use these translations, call the `i18n.N` method:

Expand All @@ -150,4 +149,32 @@ fmt.Println(i18n.N(ctx, "inbox.emails", count, i18n.M{"count": count}))

The output from this will be: "You have 2 emails."

In the current implementation of `ctxi18n` there are very few pluralization rules defined, please do submit PRs if you language is not covered!
In the current implementation of `ctxi18n` there are very few pluralization rules defined, please submit PRs if your language is not covered!

## Templ

[Templ](https://templ.guide/) is a templating library that helps you create components that render fragments of HTML and compose them to create screens, pages, documents or apps.

The following "Hello World" example is taken from the [Templ Guide](https://templ.guide) and shows how you can quickly add translations the leverage the built-in `ctx` variable provided by Templ.

```yaml
en:
welcome:
hello: "Hello, %{name}"
```

```go
package main
import "github.com/invopop/ctxi18n/i18n"
templ Hello(name string) {
<div>{ i18n.T(ctx, "welcome.hello", i18n.M{"name": name}) }</div>
}
templ Greeting(person Person) {
<div class="greeting">
@Hello(person.Name)
</div>
}
```
17 changes: 16 additions & 1 deletion ctxi18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,28 @@ var (
ErrMissingLocale = errors.New("locale not defined")
)

func init() {
locales = new(i18n.Locales)
}

// Load walks through all the files in provided File System and prepares
// an internal global list of locales ready to use.
func Load(fs fs.FS) error {
locales = new(i18n.Locales)
return locales.Load(fs)
}

// Get provides the Locale object for the matching code.
func Get(code i18n.Code) *i18n.Locale {
return locales.Get(code)
}

// Match attempts to find the best possible matching locale based on the
// locale string provided. The locale string is parsed according to the
// "Accept-Language" header format defined in RFC9110.
func Match(locale string) *i18n.Locale {
return locales.Match(locale)
}

// WithLocale tries to match the provided code with a locale and ensures
// it is available inside the context.
func WithLocale(ctx context.Context, locale string) (context.Context, error) {
Expand Down
72 changes: 72 additions & 0 deletions ctxi18n_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ctxi18n_test

import (
"context"
"testing"

"github.com/invopop/ctxi18n"
"github.com/invopop/ctxi18n/i18n"
"github.com/invopop/ctxi18n/internal/examples"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestDefaults(t *testing.T) {
assert.Equal(t, i18n.Code("en"), ctxi18n.DefaultLocale)
}

func TestLoad(t *testing.T) {
err := ctxi18n.Load(examples.Content)
assert.NoError(t, err)

l := ctxi18n.Get("en")
assert.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())
}

func TestGet(t *testing.T) {
err := ctxi18n.Load(examples.Content)
assert.NoError(t, err)

l := ctxi18n.Get("en")
assert.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())

l = ctxi18n.Get("bad")
assert.Nil(t, l)
}

func TestMatch(t *testing.T) {
err := ctxi18n.Load(examples.Content)
require.NoError(t, err)

l := ctxi18n.Match("en-US,en;q=0.9,es;q=0.8")
assert.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())
}

func TestWithLocale(t *testing.T) {
err := ctxi18n.Load(examples.Content)
require.NoError(t, err)

ctx := context.Background()
ctx, err = ctxi18n.WithLocale(ctx, "en-US,en;q=0.9,es;q=0.8")
require.NoError(t, err)

l := ctxi18n.Locale(ctx)
assert.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())

// Use the default locale if not set
ctx, err = ctxi18n.WithLocale(ctx, "inv")
assert.NoError(t, err)
l = ctxi18n.Locale(ctx)
assert.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())

ctxi18n.DefaultLocale = "bad"
_, err = ctxi18n.WithLocale(ctx, "inv")
assert.ErrorIs(t, err, ctxi18n.ErrMissingLocale)
ctxi18n.DefaultLocale = "es"

}
4 changes: 4 additions & 0 deletions i18n/locales_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func TestLocalesLoad(t *testing.T) {
require.NotNil(t, l)
assert.Equal(t, "en", l.Code().String())

l = ls.Match("es-ES,es;q=0.9,en;q=0.8")
require.NotNil(t, l)
assert.Equal(t, "es", l.Code().String())

assert.Nil(t, ls.Match("inv"))
}

Expand Down
17 changes: 0 additions & 17 deletions templ/i18n/i18n.templ

This file was deleted.

77 changes: 0 additions & 77 deletions templ/i18n/i18n_templ.go

This file was deleted.

0 comments on commit 2c6aa97

Please sign in to comment.