Skip to content

Commit 5529dd1

Browse files
author
Dean Karn
authored
Merge pull request #12 from go-playground/file-import-export
Add File Import/Export logic
2 parents 03ee896 + 3062944 commit 5529dd1

29 files changed

+2040
-106
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ _testmain.go
2121

2222
*.exe
2323
*.test
24-
*.prof
24+
*.prof

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2016 Go Experimental
3+
Copyright (c) 2016 Go Playground
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+34-68
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## universal-translator
22
<img align="right" src="https://raw.githubusercontent.com/go-playground/universal-translator/master/logo.png">
3-
![Project status](https://img.shields.io/badge/version-0.14.0-green.svg)
3+
![Project status](https://img.shields.io/badge/version-0.15.0-green.svg)
44
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/universal-translator/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/universal-translator)
55
[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator)
66
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator)
@@ -25,8 +25,8 @@ Features
2525
- [x] Contains Date & Time formatting functions
2626
- [x] Contains Number, Currency, Accounting and Percent formatting functions
2727
- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere )
28-
- [ ] Support loading translations from files
29-
- [ ] Exporting translations to file, mainly for getting them professionally translated
28+
- [x] Support loading translations from files
29+
- [x] Exporting translations to file(s), mainly for getting them professionally translated
3030
- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated
3131
- [ ] Tests for all languages, I need help with this, please see [here](https://github.com/go-playground/locales/issues/1)
3232

@@ -35,84 +35,50 @@ Installation
3535

3636
Use go get
3737

38-
```go
38+
```shell
3939
go get github.com/go-playground/universal-translator
4040
```
4141

42-
Usage
42+
Usage & Documentation
4343
-------
44-
```go
45-
package main
4644

47-
import (
48-
"fmt"
45+
Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs
4946

50-
"github.com/go-playground/locales"
51-
"github.com/go-playground/locales/en"
52-
"github.com/go-playground/locales/en_CA"
53-
"github.com/go-playground/locales/fr"
54-
"github.com/go-playground/locales/nl"
55-
"github.com/go-playground/universal-translator"
56-
)
47+
##### Examples:
5748

58-
// only one instance as translators within are shared + goroutine safe
59-
var universalTraslator *ut.UniversalTranslator
49+
- [Basic](https://github.com/go-playground/universal-translator/tree/master/examples/basic)
50+
- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/examples/full-no-files)
51+
- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/examples/full-with-files)
6052

61-
func main() {
53+
File formatting
54+
--------------
55+
All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s);
56+
they are only separated for easy viewing.
6257

63-
// NOTE: this example is omitting a lot of error checking for brevity
64-
e := en.New()
65-
universalTraslator = ut.New(e, e, en_CA.New(), nl.New(), fr.New())
58+
##### Examples:
6659

67-
en, _ := universalTraslator.GetTranslator("en")
60+
- [Formats](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats)
6861

69-
// generally used after parsing an http 'Accept-Language' header
70-
// and this will try to find a matching locale you support or
71-
// fallback locale.
72-
// en, _ := ut.FindTranslator([]string{"en", "en_CA", "nl"})
73-
74-
// this will help
75-
fmt.Println("Cardinal Plural Rules:", en.PluralsCardinal())
76-
fmt.Println("Ordinal Plural Rules:", en.PluralsOrdinal())
77-
fmt.Println("Range Plural Rules:", en.PluralsRange())
78-
79-
// add basic language only translations
80-
// last param indicates if it's ok to override the translation if one already exists
81-
en.Add("welcome", "Welcome {0} to our test", false)
82-
83-
// add language translations dependant on cardinal plural rules
84-
en.AddCardinal("days", "You have {0} day left to register", locales.PluralRuleOne, false)
85-
en.AddCardinal("days", "You have {0} days left to register", locales.PluralRuleOther, false)
86-
87-
// add language translations dependant on ordinal plural rules
88-
en.AddOrdinal("day-of-month", "{0}st", locales.PluralRuleOne, false)
89-
en.AddOrdinal("day-of-month", "{0}nd", locales.PluralRuleTwo, false)
90-
en.AddOrdinal("day-of-month", "{0}rd", locales.PluralRuleFew, false)
91-
en.AddOrdinal("day-of-month", "{0}th", locales.PluralRuleOther, false)
92-
93-
// add language translations dependant on range plural rules
94-
// NOTE: only one plural rule for range in 'en' locale
95-
en.AddRange("between", "It's {0}-{1} days away", locales.PluralRuleOther, false)
96-
97-
// now lets use the translations we just added, in the same order we added them
98-
99-
fmt.Println(en.T("welcome", "Joeybloggs"))
100-
101-
fmt.Println(en.C("days", 1, 0, en.FmtNumber(1, 0))) // you'd normally have variables defined for 1 and 0
102-
fmt.Println(en.C("days", 2, 0, en.FmtNumber(2, 0)))
103-
fmt.Println(en.C("days", 10456.25, 2, en.FmtNumber(10456.25, 2)))
104-
105-
fmt.Println(en.O("day-of-month", 1, 0, en.FmtNumber(1, 0)))
106-
fmt.Println(en.O("day-of-month", 2, 0, en.FmtNumber(2, 0)))
107-
fmt.Println(en.O("day-of-month", 3, 0, en.FmtNumber(3, 0)))
108-
fmt.Println(en.O("day-of-month", 4, 0, en.FmtNumber(4, 0)))
109-
fmt.Println(en.O("day-of-month", 10456.25, 0, en.FmtNumber(10456.25, 0)))
110-
111-
fmt.Println(en.R("between", 0, 0, 1, 0, en.FmtNumber(0, 0), en.FmtNumber(1, 0)))
112-
fmt.Println(en.R("between", 1, 0, 2, 0, en.FmtNumber(1, 0), en.FmtNumber(2, 0)))
113-
fmt.Println(en.R("between", 1, 0, 100, 0, en.FmtNumber(1, 0), en.FmtNumber(100, 0)))
62+
##### Basic Makeup
63+
NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats)
64+
```json
65+
{
66+
"locale": "en",
67+
"key": "days-left",
68+
"trans": "You have {0} day left.",
69+
"type": "Cardinal",
70+
"rule": "One",
71+
"override": false
11472
}
11573
```
74+
|Field|Description|
75+
|---|---|
76+
|locale|The locale for which the translation is for.|
77+
|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.|
78+
|trans|The actual translation text.|
79+
|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)|
80+
|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)|
81+
|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.|
11682

11783
Help With Tests
11884
---------------

errors.go

+60-7
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,20 @@ func (e *ErrExistingTranslator) Error() string {
3131

3232
// ErrConflictingTranslation is the error representing a conflicting translation
3333
type ErrConflictingTranslation struct {
34-
key interface{}
35-
rule locales.PluralRule
36-
text string
34+
locale string
35+
key interface{}
36+
rule locales.PluralRule
37+
text string
3738
}
3839

3940
// Error returns ErrConflictingTranslation's internal error text
4041
func (e *ErrConflictingTranslation) Error() string {
4142

4243
if _, ok := e.key.(string); !ok {
43-
return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s', value being ignored", e.key, e.rule, e.text)
44+
return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
4445
}
4546

46-
return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s', value being ignored", e.key, e.rule, e.text)
47+
return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale)
4748
}
4849

4950
// ErrRangeTranslation is the error representing a range translation error
@@ -79,6 +80,7 @@ func (e *ErrCardinalTranslation) Error() string {
7980
// ErrMissingPluralTranslation is the error signifying a missing translation given
8081
// the locales plural rules.
8182
type ErrMissingPluralTranslation struct {
83+
locale string
8284
key interface{}
8385
rule locales.PluralRule
8486
translationType string
@@ -88,8 +90,59 @@ type ErrMissingPluralTranslation struct {
8890
func (e *ErrMissingPluralTranslation) Error() string {
8991

9092
if _, ok := e.key.(string); !ok {
91-
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v'", e.translationType, e.rule, e.key)
93+
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
9294
}
9395

94-
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s'", e.translationType, e.rule, e.key)
96+
return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale)
97+
}
98+
99+
// ErrMissingBracket is the error representing a missing bracket in a translation
100+
// eg. This is a {0 <-- missing ending '}'
101+
type ErrMissingBracket struct {
102+
locale string
103+
key interface{}
104+
text string
105+
}
106+
107+
// Error returns ErrMissingBracket error message
108+
func (e *ErrMissingBracket) Error() string {
109+
return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text)
110+
}
111+
112+
// ErrBadParamSyntax is the error representing a bad parameter definition in a translation
113+
// eg. This is a {must-be-int}
114+
type ErrBadParamSyntax struct {
115+
locale string
116+
param string
117+
key interface{}
118+
text string
119+
}
120+
121+
// Error returns ErrBadParamSyntax error message
122+
func (e *ErrBadParamSyntax) Error() string {
123+
return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text)
124+
}
125+
126+
// import/export errors
127+
128+
// ErrMissingLocale is the error representing an expected locale that could
129+
// not be found aka locale not registered with the UniversalTranslator Instance
130+
type ErrMissingLocale struct {
131+
locale string
132+
}
133+
134+
// Error returns ErrMissingLocale's internal error text
135+
func (e *ErrMissingLocale) Error() string {
136+
return fmt.Sprintf("error: locale '%s' not registered.", e.locale)
137+
}
138+
139+
// ErrBadPluralDefinition is the error representing an incorrect plural definition
140+
// usually found within translations defined within files during the import process.
141+
type ErrBadPluralDefinition struct {
142+
tl translation
143+
}
144+
145+
// Error returns ErrBadPluralDefinition's internal error text
146+
func (e *ErrBadPluralDefinition) Error() string {
147+
return fmt.Sprintf("error: bad plural definition '%#v'", e.tl)
95148
}

examples/file-formats/cardinal.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
{
3+
"locale": "en",
4+
"key": "cardinal_test",
5+
"trans": "You have {0} day left.",
6+
"type": "Cardinal",
7+
"rule": "One"
8+
},
9+
{
10+
"locale": "en",
11+
"key": "cardinal_test",
12+
"trans": "You have {0} days left.",
13+
"type": "Cardinal",
14+
"rule": "Other"
15+
}
16+
]

examples/file-formats/ordinal.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"locale": "en",
4+
"key": "day",
5+
"trans": "{0}st",
6+
"type": "Ordinal",
7+
"rule": "One"
8+
},
9+
{
10+
"locale": "en",
11+
"key": "day",
12+
"trans": "{0}nd",
13+
"type": "Ordinal",
14+
"rule": "Two"
15+
},
16+
{
17+
"locale": "en",
18+
"key": "day",
19+
"trans": "{0}rd",
20+
"type": "Ordinal",
21+
"rule": "Few"
22+
},
23+
{
24+
"locale": "en",
25+
"key": "day",
26+
"trans": "{0}th",
27+
"type": "Ordinal",
28+
"rule": "Other"
29+
}
30+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"locale": "en",
4+
"key": "test_trans4",
5+
"trans": "{0}{1}"
6+
},
7+
{
8+
"locale": "en",
9+
"key": "test_trans",
10+
"trans": "Welcome {0} to the {1}."
11+
},
12+
{
13+
"locale": "en",
14+
"key": -1,
15+
"trans": "Welcome {0}"
16+
},
17+
{
18+
"locale": "en",
19+
"key": "test_trans2",
20+
"trans": "{0} to the {1}."
21+
},
22+
{
23+
"locale": "en",
24+
"key": "test_trans3",
25+
"trans": "Welcome {0} to the {1}"
26+
}
27+
]

examples/file-formats/range.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
{
3+
"locale": "nl",
4+
"key": "day",
5+
"trans": "er {0}-{1} dag vertrokken",
6+
"type": "Range",
7+
"rule": "One"
8+
},
9+
{
10+
"locale": "nl",
11+
"key": "day",
12+
"trans": "er zijn {0}-{1} dagen over",
13+
"type": "Range",
14+
"rule": "Other"
15+
}
16+
]

examples/full-no-files/home.tmpl

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{{ define "home" }}
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>Home</title>
6+
</head>
7+
<body>
8+
<p>Locale: {{ .Trans.Locale }}</p>
9+
<p>Trans1: {{ .Trans.C "days-left" 1 0 "1" }}</p>
10+
<p>Trans2: {{ .Trans.C "days-left" 2 0 "2" }}</p>
11+
<p>FmtNumber Positive: {{ .Trans.FmtNumber .PositiveNum 2 }}</p>
12+
<p>FmtNumber Negative: {{ .Trans.FmtNumber .NegativeNum 2 }}</p>
13+
<p>FmtPercent Negative: {{ .Trans.FmtPercent .Percent 2 }}</p>
14+
<p>FmtCurrency Negative: {{ .Trans.FmtCurrency .PositiveNum 2 .Trans.Currency }}</p>
15+
<p>FmtCurrency Negative: {{ .Trans.FmtCurrency .NegativeNum 2 .Trans.Currency }}</p>
16+
<p>FmtAccounting Negative: {{ .Trans.FmtAccounting .PositiveNum 2 .Trans.Currency }}</p>
17+
<p>FmtAccounting Negative: {{ .Trans.FmtAccounting .NegativeNum 2 .Trans.Currency }}</p>
18+
<p>FmtDateShort: {{ .Trans.FmtDateShort .Now }}</p>
19+
<p>FmtDateMedium: {{ .Trans.FmtDateMedium .Now }}</p>
20+
<p>FmtDateLong: {{ .Trans.FmtDateLong .Now }}</p>
21+
<p>FmtDateFull: {{ .Trans.FmtDateFull .Now }}</p>
22+
<p>FmtTimeShort: {{ .Trans.FmtTimeShort .Now }}</p>
23+
<p>FmtTimeMedium: {{ .Trans.FmtTimeMedium .Now }}</p>
24+
<p>FmtTimeLong: {{ .Trans.FmtTimeLong .Now }}</p>
25+
<p>FmtTimeFull: {{ .Trans.FmtTimeFull .Now }}</p>
26+
<p>MonthsAbbreviated: {{ .Trans.MonthsAbbreviated }}</p>
27+
<p>MonthsNarrow: {{ .Trans.MonthsNarrow }}</p>
28+
<p>MonthsWide: {{ .Trans.MonthsWide }}</p>
29+
<p>WeekdaysAbbreviated: {{ .Trans.WeekdaysAbbreviated }}</p>
30+
<p>WeekdaysNarrow: {{ .Trans.WeekdaysNarrow }}</p>
31+
<p>WeekdaysShort: {{ .Trans.WeekdaysShort }}</p>
32+
<p>WeekdaysWide: {{ .Trans.WeekdaysWide }}</p>
33+
</body>
34+
</html>
35+
{{ end }}

0 commit comments

Comments
 (0)