Skip to content

Commit

Permalink
Merge pull request #373 from Skyenought/feat/i18n
Browse files Browse the repository at this point in the history
✨ Add fiberi18n middleware
  • Loading branch information
ReneWerner87 authored Mar 20, 2023
2 parents 49b9092 + 0b60b08 commit 9ecbea0
Show file tree
Hide file tree
Showing 19 changed files with 789 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ updates:
- "🤖 Dependencies"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/fiberi18n"
labels:
- "🤖 Dependencies"
schedule:
interval: "daily"
43 changes: 43 additions & 0 deletions .github/release-drafter-fiberi18n.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name-template: 'Fiberi18n - v$RESOLVED_VERSION'
tag-template: 'fiberi18n/v$RESOLVED_VERSION'
tag-prefix: fiberi18n/v
include-paths:
- fiberi18n
categories:
- title: '🚀 New'
labels:
- '✏️ Feature'
- title: '🧹 Updates'
labels:
- '🧹 Updates'
- '🤖 Dependencies'
- title: '🐛 Fixes'
labels:
- '☢️ Bug'
- title: '📚 Documentation'
labels:
- '📒 Documentation'
change-template: '- $TITLE (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- 'major'
minor:
labels:
- 'minor'
- '✏️ Feature'
patch:
labels:
- 'patch'
- '📒 Documentation'
- '☢️ Bug'
- '🤖 Dependencies'
- '🧹 Updates'
default: patch
template: |
$CHANGES
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...fiberi18n/v$RESOLVED_VERSION
Thank you $CONTRIBUTORS for making this update possible.
19 changes: 19 additions & 0 deletions .github/workflows/release-drafter-fiberi18n.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Release Drafter Fiberi18n
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
- main
paths:
- 'fiberi18n/**'
jobs:
draft_release_fiberi18n:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-fiberi18n.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 4 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ jobs:
working-directory: ./opafiber
run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
# -----
- name: Run Gosec (fiberi18n)
working-directory: ./fiberi18n
run: "`go env GOPATH`/bin/gosec -exclude-dir=internal ./..."
# -----
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ jobs:
run: cd ./fibernewrelic && go test ./... -v -race
- name: Test opafiber Middleware
run: cd ./opafiber && go test ./... -v -race
- name: Test fiberi18n Middleware
run: cd ./fiberi18n && go test ./... -v -race
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ Repository for third party middlewares with dependencies
* [Open Policy Agent](/opafiber) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Tests%22">
<img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B">
</a>
* [Fiberi18n](/fiberi18n) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Tests%22">
<img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B">
</a>
74 changes: 74 additions & 0 deletions fiberi18n/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Fiberi18n

![Release](https://img.shields.io/github/release/gofiber/contrib.svg)
[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord)
![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg)
![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg)
![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg)

[go-i18n](https://github.com/nicksnyder/go-i18n) support for Fiber.

### Install

This middleware supports Fiber v2.

```
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/fiberi18n
```

### Signature

```
fiberi18n.New(config ...*Config) fiber.Handler
```

### Config

| Property | Type | Description | Default |
| ---------------- | ------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Next | `func(c *fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` |
| RootPath | `string` | The i18n template folder path. | `"./example/localize"` |
| AcceptLanguages | `[]language.Tag` | A collection of languages that can be processed. | `[]language.Tag{language.Chinese, language.English}` |
| FormatBundleFile | `string` | The type of the template file. | `"yaml"` |
| DefaultLanguage | `language.Tag` | The default returned language type. | `language.English` |
| Loader | `Loader` | The implementation of the Loader interface, which defines how to read the file. We provide both os.ReadFile and embed.FS.ReadFile. | `LoaderFunc(os.ReadFile)` |
| UnmarshalFunc | `i18n.UnmarshalFunc` | The function used for decoding template files. | `yaml.Unmarshal` |
| LangHandler | `func(ctx *fiber.Ctx, defaultLang string) string` | Used to get the kind of language handled by *fiber.Ctx and defaultLang. | Retrieved from the request header `Accept-Language` or query parameter `lang`. |

### Example

```go
package main

import (
"github.com/gofiber/contrib/fiberi18n"
"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)

func main() {
app := fiber.New()
app.Use(
fiberi18n.New(&fiberi18n.Config{
RootPath: "./example/localize",
AcceptLanguages: []language.Tag{language.Chinese, language.English},
DefaultLanguage: language.Chinese,
}),
)
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString(fiberi18n.MustGetMessage("welcome"))
})
app.Get("/:name", func(ctx *fiber.Ctx) error {
return ctx.SendString(fiberi18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: "welcomeWithName",
TemplateData: map[string]string{
"name": ctx.Params("name"),
},
}))
})
app.Listen("127.0.0.1:3000")
}
```

136 changes: 136 additions & 0 deletions fiberi18n/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package fiberi18n

import (
"os"

"github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
"gopkg.in/yaml.v2"
)

type Config struct {
// Next defines a function to skip this middleware when returned true.
//
// Optional. Default: nil
Next func(c *fiber.Ctx) bool

// RootPath is i18n template folder path
//
// Default: ./example/localize
RootPath string

// AcceptLanguages is a collection of languages that can be processed
//
// Optional. Default: []language.Tag{language.Chinese, language.English}
AcceptLanguages []language.Tag

// FormatBundleFile is type of template file.
//
// Optional. Default: "yaml"
FormatBundleFile string

// DefaultLanguage is the default returned language type
//
// Optional. Default: language.English
DefaultLanguage language.Tag

// Loader implements the Loader interface, which defines how to read the file.
// We provide both os.ReadFile and embed.FS.ReadFile
// Optional. Default: LoaderFunc(os.ReadFile)
Loader Loader

// UnmarshalFunc for decoding template files
//
// Optional. Default: yaml.Unmarshal
UnmarshalFunc i18n.UnmarshalFunc

// LangHandler is used to get the kind of language handled by *fiber.Ctx and defaultLang
//
// Optional. Default: The language type is retrieved from the request header: `Accept-Language` or query param : `lang`
LangHandler func(ctx *fiber.Ctx, defaultLang string) string

ctx *fiber.Ctx
bundle *i18n.Bundle
localizerMap map[string]*i18n.Localizer
}

type Loader interface {
LoadMessage(path string) ([]byte, error)
}

type LoaderFunc func(path string) ([]byte, error)

func (f LoaderFunc) LoadMessage(path string) ([]byte, error) {
return f(path)
}

var ConfigDefault = &Config{
RootPath: "./example/localize",
DefaultLanguage: language.English,
AcceptLanguages: []language.Tag{language.Chinese, language.English},
FormatBundleFile: "yaml",
UnmarshalFunc: yaml.Unmarshal,
Loader: LoaderFunc(os.ReadFile),
LangHandler: func(c *fiber.Ctx, defaultLang string) string {
if c == nil {
return defaultLang
}
lang := c.Get("Accept-Language")
if lang != "" {
return lang
}
lang = c.Query("lang")
if lang == "" {
return defaultLang
}
return lang
},
}

func configDefault(config ...*Config) *Config {
// Return default config if nothing provided
if len(config) == 0 {
return ConfigDefault
}

// Override default config
cfg := config[0]

if cfg.Next == nil {
cfg.Next = ConfigDefault.Next
}

if cfg.RootPath == "" {
cfg.RootPath = ConfigDefault.RootPath
}

if cfg.DefaultLanguage == language.Und {
cfg.DefaultLanguage = ConfigDefault.DefaultLanguage
}

if cfg.UnmarshalFunc == nil {
cfg.UnmarshalFunc = ConfigDefault.UnmarshalFunc
}

if cfg.FormatBundleFile == "" {
cfg.FormatBundleFile = ConfigDefault.FormatBundleFile
}

if cfg.AcceptLanguages == nil {
cfg.AcceptLanguages = ConfigDefault.AcceptLanguages
}

if cfg.Loader == nil {
cfg.Loader = ConfigDefault.Loader
}

if cfg.UnmarshalFunc == nil {
cfg.UnmarshalFunc = ConfigDefault.UnmarshalFunc
}

if cfg.LangHandler == nil {
cfg.LangHandler = ConfigDefault.LangHandler
}
return cfg
}
13 changes: 13 additions & 0 deletions fiberi18n/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build go1.16

package fiberi18n

import "embed"

type EmbedLoader struct {
FS embed.FS
}

func (e *EmbedLoader) LoadMessage(path string) ([]byte, error) {
return e.FS.ReadFile(path)
}
Loading

0 comments on commit 9ecbea0

Please sign in to comment.