-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from lucasmdrs/v2
v2.0.0
- Loading branch information
Showing
9 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
## go-loco v2 | ||
|
||
A simple lib to fetch translation files from [Localise](https://localise.biz/) and save it in a [go-i18n](https://github.com/nicksnyder/go-i18n) compatible format. | ||
|
||
## Features | ||
- Multiple project management | ||
- Automatic download new translations | ||
|
||
Checkout some [examples](./examples). | ||
|
||
## Basic Usage | ||
|
||
```golang | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
loco "github.com/lucasmdrs/go-loco/v2" | ||
"github.com/nicksnyder/go-i18n/i18n" | ||
) | ||
|
||
func main() { | ||
g := loco.Init() | ||
|
||
if err := g.AddProject("MY_LOCALISE_KEY", "./"); err != nil { | ||
panic(err) | ||
} | ||
|
||
g.FetchTranslations(context.TODO()) | ||
|
||
i18n.MustLoadTranslationFile("en-US.json") | ||
i18n.MustLoadTranslationFile("pt-BR.json") | ||
|
||
T, _ := i18n.Tfunc("pt-BR") | ||
|
||
fmt.Println(T("hello")) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
|
||
loco "github.com/lucasmdrs/go-loco/v2" | ||
) | ||
|
||
func main() { | ||
g := loco.Init() | ||
|
||
if err := g.AddProject("KEY_1", "project1"); err != nil { | ||
log.Fatalln(err.Error()) | ||
} | ||
|
||
if err := g.AddProject("KEY_2", "project2"); err != nil { | ||
log.Fatalln(err.Error()) | ||
} | ||
g.FetchTranslations(context.TODO()) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
loco "github.com/lucasmdrs/go-loco/v2" | ||
) | ||
|
||
func main() { | ||
g := loco.Init() | ||
|
||
if err := g.AddProject("KEY_1", "project1"); err != nil { | ||
log.Fatalln(err.Error()) | ||
} | ||
|
||
if err := g.AddProject("KEY_2", "project2"); err != nil { | ||
log.Fatalln(err.Error()) | ||
} | ||
|
||
ch, err := g.StartPoller() | ||
if err != nil { | ||
log.Fatalln(err.Error()) | ||
} | ||
|
||
for { | ||
select { | ||
case <-ch: | ||
log.Println("changed!") | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/lucasmdrs/go-loco/v2 | ||
|
||
go 1.13 | ||
|
||
require github.com/lucasmdrs/ctxpoller v1.0.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
github.com/lucasmdrs/ctxpoller v1.0.0 h1:0/tGTbahfyjMu2FgzoAnaAijPlEGQta6sMyQyzDF4lY= | ||
github.com/lucasmdrs/ctxpoller v1.0.0/go.mod h1:9xiEKn7CgFaY6hvpVxQxctYAShigf9LKKNH4uZuEogc= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package loco | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"log" | ||
|
||
"github.com/lucasmdrs/ctxpoller" | ||
) | ||
|
||
const baseURL = "https://localise.biz/api/" | ||
const authEndpoint = "auth/verify" | ||
const filenameTemplate = "%s.json" | ||
const endpointTemplate = "export/" + filenameTemplate | ||
const authParameter = "?key=%s" | ||
|
||
// goloco is a structure that holds the required information and implements the GoLoco service interface | ||
type goloco struct { | ||
notifier chan interface{} | ||
poller ctxpoller.Poller | ||
projects map[uint]*project | ||
} | ||
|
||
// GoLoco defines the service interface | ||
type GoLoco interface { | ||
AddProject(key, assetsPath string) error | ||
StartPoller() (chan interface{}, error) | ||
StopPoller() | ||
FetchTranslations(context.Context) | ||
} | ||
|
||
// Init initializes the service | ||
func Init() GoLoco { | ||
return &goloco{ | ||
notifier: make(chan interface{}), | ||
projects: make(map[uint]*project, 0), | ||
} | ||
} | ||
|
||
// AddProject includes a new Loco project from a API Key | ||
// and sets the assets destination. | ||
func (g *goloco) AddProject(key string, assetsDestinationPath string) error { | ||
p, err := getProjectInformation(key) | ||
if err != nil { | ||
return err | ||
} | ||
if _, exists := g.projects[p.ID]; exists { | ||
return errors.New("Duplicated project") | ||
} | ||
p.assetsPath = assetsDestinationPath | ||
g.projects[p.ID] = &p | ||
g.notifier = make(chan interface{}, len(g.projects)) | ||
return nil | ||
} | ||
|
||
// StartPoller keep a poller for any changes in the projects translations | ||
// notifying it in the returned channel whenever the assets changed | ||
func (g *goloco) StartPoller() (chan interface{}, error) { | ||
g.poller = ctxpoller.DefaultPoller(g.FetchTranslations) | ||
return g.notifier, g.poller.Start() | ||
} | ||
|
||
// StopPoller stops looking for changes in the translations | ||
func (g *goloco) StopPoller() { | ||
g.poller.Stop() | ||
} | ||
|
||
// FetchTranslations fetches the translations from Loco and save it assets | ||
func (g *goloco) FetchTranslations(context.Context) { | ||
for _, p := range g.projects { | ||
if err := p.fetchProjectTranslations(g.notifier); err != nil { | ||
log.Fatalf("Failed to retrieve translations for %s: %s", p.Name, err.Error()) | ||
} | ||
} | ||
if g.poller == nil || !g.poller.IsActive() { | ||
emptyChannel(g.notifier) | ||
} | ||
} | ||
|
||
func emptyChannel(ch chan interface{}) { | ||
for len(ch) > 0 { | ||
<-ch | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package loco | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"path" | ||
"reflect" | ||
) | ||
|
||
type authVerifyResponse struct { | ||
project `json:"project"` | ||
} | ||
type project struct { | ||
key string | ||
ID uint `json:"id"` | ||
Name string `json:"name"` | ||
assetsPath string | ||
lastResult translationResponse | ||
} | ||
|
||
func getProjectInformation(key string) (project, error) { | ||
res, err := http.Get(fmt.Sprintf(baseURL+authEndpoint+authParameter, key)) | ||
if err != nil { | ||
return project{}, err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != http.StatusOK { | ||
return project{}, errors.New("Invalid Key") | ||
} | ||
var data authVerifyResponse | ||
if err := json.NewDecoder(res.Body).Decode(&data); err != nil { | ||
return project{}, err | ||
} | ||
data.project.key = key | ||
data.project.lastResult = make(translationResponse) | ||
return data.project, nil | ||
} | ||
|
||
func (p *project) fetchProjectTranslations(notifier chan interface{}) error { | ||
uri := fmt.Sprintf(baseURL+endpointTemplate+authParameter, "all", p.key) | ||
res, err := http.Get(uri) | ||
if err != nil { | ||
return err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != http.StatusOK { | ||
return errors.New("Failed to request project translations") | ||
} | ||
|
||
var locoData translationResponse | ||
if err := json.NewDecoder(res.Body).Decode(&locoData); err != nil { | ||
return err | ||
} | ||
|
||
if reflect.DeepEqual(locoData, p.lastResult) { | ||
return nil | ||
} | ||
|
||
for lang, keys := range locoData { | ||
if reflect.DeepEqual(keys, p.lastResult[lang]) { | ||
continue | ||
} | ||
|
||
json, err := keys.toJSONTranslationList() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := writeToFile(json, p.assetsPath, lang); err != nil { | ||
return err | ||
} | ||
|
||
p.lastResult[lang] = keys | ||
log.Println(p.Name, ":", lang) | ||
} | ||
|
||
notifier <- nil | ||
return nil | ||
} | ||
|
||
func writeToFile(content []byte, destination, lang string) error { | ||
destination = path.Join(destination, "%s.json") | ||
return ioutil.WriteFile(fmt.Sprintf(destination, lang), content, 0644) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package loco | ||
|
||
import "encoding/json" | ||
|
||
// translation represents a translation from Loco | ||
// as structure that is compatible with go-i18n | ||
|
||
type keyValue map[string]string | ||
|
||
type translationResponse map[string]keyValue | ||
|
||
func (kv *keyValue) toTranslationList() []translation { | ||
list := make([]translation, 0) | ||
for id, t := range *kv { | ||
list = append(list, translation{ID: id, Translation: t}) | ||
} | ||
return list | ||
} | ||
|
||
func (kv *keyValue) toJSONTranslationList() ([]byte, error) { | ||
return json.Marshal(kv.toTranslationList()) | ||
} | ||
|
||
type translation struct { | ||
ID string `json:"id"` | ||
Translation string `json:"translation"` | ||
} |