├── go-i18n
├── goi18n
│ ├── testdata
│ │ ├── input
│ │ │ ├── fr-FR.json
│ │ │ ├── en-US.two.json
│ │ │ ├── en-US.one.json
│ │ │ ├── ar-AR.one.json
│ │ │ └── ar-AR.two.json
│ │ ├── expected
│ │ │ ├── en-US.untranslated.json
│ │ │ ├── fr-FR.all.json
│ │ │ ├── en-US.all.json
│ │ │ ├── fr-FR.untranslated.json
│ │ │ ├── ar-AR.all.json
│ │ │ └── ar-AR.untranslated.json
│ │ └── en-US.yaml
│ ├── gendoc.sh
│ ├── merge_test.go
│ ├── doc.go
│ ├── goi18n.go
│ └── merge.go
├── i18n
│ ├── translation
│ │ ├── translation_test.go
│ │ ├── single_translation.go
│ │ ├── template.go
│ │ ├── plural_translation.go
│ │ ├── translation.go
│ │ ├── template_test.go
│ │ └── plural_translation_test.go
│ ├── plural
│ │ ├── plural_test.go
│ │ ├── plural.go
│ │ ├── operands_test.go
│ │ └── operands.go
│ ├── locale
│ │ ├── locale_test.go
│ │ └── locale.go
│ ├── exampletemplate_test.go
│ ├── example_test.go
│ ├── bundle
│ │ ├── bundle_test.go
│ │ └── bundle.go
│ ├── i18n.go
│ └── language
│ │ ├── language.go
│ │ └── language_test.go
├── LICENSE
└── README.md
├── conf
└── app.conf
├── views
├── buoy
│ ├── page-head.html
│ ├── modal
│ │ └── pv_station-detail.html
│ └── content.html
└── shared
│ ├── header.html
│ ├── modal.html
│ └── basic-layout.html
├── static
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── css
│ ├── style.css
│ ├── main.css
│ └── bootstrap-select.css
└── js
│ ├── buoy
│ └── buoy.js
│ ├── service.js
│ └── bootstrap.min.js
├── zscripts
├── run_endpoint_tests.sh
├── run_service_tests.sh
├── runbuild.sh
└── runbuild_with_bee.sh
├── .gitignore
├── utilities
├── helper
│ ├── constants.go
│ └── catch.go
└── mongo
│ └── mongo.go
├── localize
├── en-US.go
└── messages.go
├── routes
└── routes.go
├── main.go
├── test
├── endpointTests
│ ├── endpointTests.go
│ └── buoyEndpoints_test.go
└── serviceTests
│ ├── buoyService_test.go
│ └── serviceTests.go
├── LICENSE
├── services
├── services.go
└── buoyService
│ └── buoyService.go
├── models
└── buoyModels
│ └── buoyModels.go
├── README.md
└── controllers
├── buoyController.go
└── baseController
└── baseController.go
/go-i18n/goi18n/testdata/input/fr-FR.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/en-US.untranslated.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/conf/app.conf:
--------------------------------------------------------------------------------
1 | appname = Beego-mgo
2 | httpport = 9003
3 | runmode = dev
--------------------------------------------------------------------------------
/views/buoy/page-head.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goinggo/beego-mgo/HEAD/static/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goinggo/beego-mgo/HEAD/static/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goinggo/beego-mgo/HEAD/static/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/views/shared/header.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
Beego is a Very Powerful Web Framework
6 |
--------------------------------------------------------------------------------
/zscripts/run_endpoint_tests.sh:
--------------------------------------------------------------------------------
1 | export MGO_HOSTS=ds035428.mongolab.com:35428
2 | export MGO_DATABASE=goinggo
3 | export MGO_USERNAME=guest
4 | export MGO_PASSWORD=welcome
5 | export BUOY_DATABASE=goinggo
6 |
7 | cd $GOPATH/src/github.com/goinggo/beego-mgo/test/endpointTests
8 | go test -v
--------------------------------------------------------------------------------
/zscripts/run_service_tests.sh:
--------------------------------------------------------------------------------
1 | export MGO_HOSTS=ds035428.mongolab.com:35428
2 | export MGO_DATABASE=goinggo
3 | export MGO_USERNAME=guest
4 | export MGO_PASSWORD=welcome
5 | export BUOY_DATABASE=goinggo
6 |
7 | cd $GOPATH/src/github.com/goinggo/beego-mgo/test/serviceTests
8 | go test -v
--------------------------------------------------------------------------------
/zscripts/runbuild.sh:
--------------------------------------------------------------------------------
1 | export MGO_HOSTS=ds035428.mongolab.com:35428
2 | export MGO_DATABASE=goinggo
3 | export MGO_USERNAME=guest
4 | export MGO_PASSWORD=welcome
5 | export BUOY_DATABASE=goinggo
6 |
7 | cd $GOPATH/src/github.com/goinggo/beego-mgo
8 | go clean -i
9 | go build
10 |
11 | ./beego-mgo
--------------------------------------------------------------------------------
/zscripts/runbuild_with_bee.sh:
--------------------------------------------------------------------------------
1 | export MGO_HOSTS=ds035428.mongolab.com:35428
2 | export MGO_DATABASE=goinggo
3 | export MGO_USERNAME=guest
4 | export MGO_PASSWORD=welcome
5 | export BUOY_DATABASE=goinggo
6 |
7 | cd $GOPATH/src/github.com/goinggo/beego-mgo
8 | go clean -i
9 | go build
10 |
11 | bee run watchall
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 |
24 | beego-mgo
25 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/gendoc.sh:
--------------------------------------------------------------------------------
1 | go install
2 | echo "// The goi18n command formats and merges translation files." > doc.go
3 | echo "//" >> doc.go
4 | echo "// go get -u github.com/nicksnyder/go-i18n/goi18n" >> doc.go
5 | echo "// goi18n -help" >> doc.go
6 | echo "//" >> doc.go
7 | echo "// Help documentation:" >> doc.go
8 | echo "//" >> doc.go
9 | goi18n -help | sed -e 's/^/\/\/ /' >> doc.go
10 | echo "package main" >> doc.go
11 |
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/translation_test.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "sort"
5 | "testing"
6 | )
7 |
8 | // Check this here to avoid unnecessary import of sort package.
9 | var _ = sort.Interface(make(SortableByID, 0, 0))
10 |
11 | func TestNewSingleTranslation(t *testing.T) {
12 | t.Skipf("not implemented")
13 | }
14 |
15 | func TestNewPluralTranslation(t *testing.T) {
16 | t.Skipf("not implemented")
17 | }
18 |
--------------------------------------------------------------------------------
/utilities/helper/constants.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package helper : constants.go implements boilerplate code for the web service.
6 | package helper
7 |
8 | //** CONSTANTS
9 |
10 | const (
11 | // MainGoRoutine is just a label for logging.
12 | MainGoRoutine = "main"
13 | )
14 |
--------------------------------------------------------------------------------
/localize/en-US.go:
--------------------------------------------------------------------------------
1 | // Package localize : en-US.go provides the localized messages for English in the United States
2 | package localize
3 |
4 | // EnUS contains english United States translations.
5 | var EnUS = `[
6 | {
7 | "id": "invalid_credentials",
8 | "translation": "Invalid Credentials were supplied."
9 | },
10 | {
11 | "id": "application_error",
12 | "translation": "An Application Error has occured."
13 | },
14 | {
15 | "id": "invalid_station_id",
16 | "translation": "Invalid Station Id Or Missing"
17 | }
18 | ]`
19 |
--------------------------------------------------------------------------------
/routes/routes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package routes initializes the routes for the web service.
6 | package routes
7 |
8 | import (
9 | "github.com/astaxie/beego"
10 | "github.com/goinggo/beego-mgo/controllers"
11 | )
12 |
13 | func init() {
14 | beego.Router("/", new(controllers.BuoyController), "get:Index")
15 | beego.Router("/buoy/retrievestation", new(controllers.BuoyController), "post:RetrieveStation")
16 | beego.Router("/buoy/station/:stationId", new(controllers.BuoyController), "get,post:RetrieveStationJSON")
17 | }
18 |
--------------------------------------------------------------------------------
/views/shared/modal.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/input/en-US.two.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "person_greeting",
4 | "translation": "Hello {{.Person}}"
5 | },
6 | {
7 | "id": "person_unread_email_count",
8 | "translation": {
9 | "one": "{{.Person}} has {{.Count}} unread email.",
10 | "other": "{{.Person}} has {{.Count}} unread emails."
11 | }
12 | },
13 | {
14 | "id": "person_unread_email_count_timeframe",
15 | "translation": {
16 | "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
17 | }
18 | },
19 | {
20 | "id": "d_days",
21 | "translation": {
22 | "one": "{{.Count}} day",
23 | "other": "{{.Count}} days"
24 | }
25 | }
26 | ]
27 |
--------------------------------------------------------------------------------
/utilities/helper/catch.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package helper : catch.go implements boilerplate code for the web service.
6 | package helper
7 |
8 | import (
9 | "fmt"
10 | "runtime"
11 | )
12 |
13 | // CatchPanic is used to catch any Panic and log exceptions to Stdout. It will also write the stack trace
14 | func CatchPanic(err *error, sessionID string, functionName string) {
15 | if r := recover(); r != nil {
16 | buf := make([]byte, 10000)
17 | runtime.Stack(buf, false)
18 |
19 | if err != nil {
20 | *err = fmt.Errorf("%v", r)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/views/buoy/modal/pv_station-detail.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - {{.Station.Name}}
5 | - Location Description: {{.Station.LocDesc}}
6 | - Wind Speed: {{.Station.Condition.WindSpeed}}
7 | - Wind Direction: {{.Station.Condition.WindDirection}}
8 | - Wind Gust: {{.Station.Condition.WindGust}}
9 | - Location: {{index .Station.Location.Coordinates 1}},{{index .Station.Location.Coordinates 0}}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/go-i18n/i18n/plural/plural_test.go:
--------------------------------------------------------------------------------
1 | package plural
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestNewCategory(t *testing.T) {
8 | tests := []struct {
9 | src string
10 | cat Category
11 | err bool
12 | }{
13 | {"zero", Zero, false},
14 | {"one", One, false},
15 | {"two", Two, false},
16 | {"few", Few, false},
17 | {"many", Many, false},
18 | {"other", Other, false},
19 | {"asdf", Invalid, true},
20 | }
21 |
22 | for _, test := range tests {
23 | cat, err := NewCategory(test.src)
24 | wrongErr := (err != nil && !test.err) || (err == nil && test.err)
25 | if cat != test.cat || wrongErr {
26 | t.Errorf("New(%#v) returned %#v,%#v; expected %#v", test.src, cat, err, test.cat)
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/input/en-US.one.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "program_greeting",
4 | "translation": "Hello world"
5 | },
6 | {
7 | "id": "your_unread_email_count",
8 | "translation": {
9 | "one": "You have {{.Count}} unread email.",
10 | "other": "You have {{.Count}} unread emails."
11 | }
12 | },
13 | {
14 | "id": "my_height_in_meters",
15 | "translation": {
16 | "one": "I am {{.Count}} meter tall.",
17 | "other": "I am {{.Count}} meters tall."
18 | }
19 | },
20 | {
21 | "id": "person_unread_email_count_timeframe",
22 | "translation": {
23 | "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}."
24 | }
25 | },
26 | {
27 | "id": "d_days",
28 | "translation": "this should get overwritten"
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/en-US.yaml:
--------------------------------------------------------------------------------
1 | - id: program_greeting
2 | translation: "Hello world"
3 |
4 | - id: person_greeting
5 | translation: "Hello {{.Person}}"
6 |
7 | - id: your_unread_email_count
8 | translations:
9 | one: "You have {{.Count}} unread email."
10 | other: "You have {{.Count}} unread emails."
11 |
12 | - id: person_unread_email_count
13 | translations:
14 | one: "{{.Person}} has {{.Count}} unread email."
15 | other: "{{.Person}} has {{.Count}} unread emails."
16 |
17 | - id: person_unread_email_count_timeframe
18 | translations:
19 | one: "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}."
20 | other: "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
21 |
22 | - id: d_days
23 | translations:
24 | one: "{{.Count}} day"
25 | other: "{{.Count}} days"
26 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/fr-FR.all.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "one": "",
6 | "other": ""
7 | }
8 | },
9 | {
10 | "id": "my_height_in_meters",
11 | "translation": {
12 | "one": "",
13 | "other": ""
14 | }
15 | },
16 | {
17 | "id": "person_greeting",
18 | "translation": ""
19 | },
20 | {
21 | "id": "person_unread_email_count",
22 | "translation": {
23 | "one": "",
24 | "other": ""
25 | }
26 | },
27 | {
28 | "id": "person_unread_email_count_timeframe",
29 | "translation": {
30 | "one": "",
31 | "other": ""
32 | }
33 | },
34 | {
35 | "id": "program_greeting",
36 | "translation": ""
37 | },
38 | {
39 | "id": "your_unread_email_count",
40 | "translation": {
41 | "one": "",
42 | "other": ""
43 | }
44 | }
45 | ]
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package main provides sample web application for beego and mgo.
6 | package main
7 |
8 | import (
9 | "github.com/astaxie/beego"
10 | "github.com/goinggo/beego-mgo/localize"
11 | _ "github.com/goinggo/beego-mgo/routes"
12 | "github.com/goinggo/beego-mgo/utilities/helper"
13 | "github.com/goinggo/beego-mgo/utilities/mongo"
14 | "github.com/goinggo/tracelog"
15 | "os"
16 | )
17 |
18 | func main() {
19 | tracelog.Start(tracelog.LevelTrace)
20 |
21 | // Init mongo
22 | tracelog.Started("main", "Initializing Mongo")
23 | err := mongo.Startup(helper.MainGoRoutine)
24 | if err != nil {
25 | tracelog.CompletedError(err, helper.MainGoRoutine, "initApp")
26 | os.Exit(1)
27 | }
28 |
29 | // Load message strings
30 | localize.Init("en-US")
31 |
32 | beego.Run()
33 |
34 | tracelog.Completed(helper.MainGoRoutine, "Website Shutdown")
35 | tracelog.Stop()
36 | }
37 |
--------------------------------------------------------------------------------
/go-i18n/i18n/plural/plural.go:
--------------------------------------------------------------------------------
1 | // Package plural defines CLDR plural categories.
2 | package plural
3 |
4 | import (
5 | "fmt"
6 | )
7 |
8 | // Category represents a language pluralization form as defined here:
9 | // http://cldr.unicode.org/index/cldr-spec/plural-rules
10 | type Category string
11 |
12 | // All defined plural categories.
13 | const (
14 | Invalid Category = "invalid"
15 | Zero = "zero"
16 | One = "one"
17 | Two = "two"
18 | Few = "few"
19 | Many = "many"
20 | Other = "other"
21 | )
22 |
23 | // NewCategory returns src as a Category
24 | // or Invalid and a non-nil error if src is not a valid Category.
25 | func NewCategory(src string) (Category, error) {
26 | switch src {
27 | case "zero":
28 | return Zero, nil
29 | case "one":
30 | return One, nil
31 | case "two":
32 | return Two, nil
33 | case "few":
34 | return Few, nil
35 | case "many":
36 | return Many, nil
37 | case "other":
38 | return Other, nil
39 | }
40 | return Invalid, fmt.Errorf("invalid plural category %s", src)
41 | }
42 |
--------------------------------------------------------------------------------
/test/endpointTests/endpointTests.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package endpointTests implements boilerplate code for all testing.
6 | package endpointTests
7 |
8 | import (
9 | "github.com/goinggo/beego-mgo/localize"
10 | _ "github.com/goinggo/beego-mgo/routes" // Initalize routes
11 | "github.com/goinggo/beego-mgo/utilities/helper"
12 | "github.com/goinggo/beego-mgo/utilities/mongo"
13 | log "github.com/goinggo/tracelog"
14 | )
15 |
16 | //** CONSTANTS
17 |
18 | const (
19 | // SessionID is just mocking the id for testing.
20 | SessionID = "testing"
21 | )
22 |
23 | //** INIT
24 |
25 | // init initializes all required packages and systems
26 | func init() {
27 | log.Start(log.LevelTrace)
28 |
29 | // Init mongo
30 | log.Started("main", "Initializing Mongo")
31 | err := mongo.Startup(helper.MainGoRoutine)
32 | if err != nil {
33 | log.CompletedError(err, helper.MainGoRoutine, "initTesting")
34 | return
35 | }
36 |
37 | // Load message strings
38 | localize.Init("en-US")
39 | }
40 |
--------------------------------------------------------------------------------
/go-i18n/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/go-i18n/i18n/locale/locale_test.go:
--------------------------------------------------------------------------------
1 | package locale
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n/language"
5 | "testing"
6 | )
7 |
8 | func TestNew(t *testing.T) {
9 | tests := []struct {
10 | localeID string
11 | lang *language.Language
12 | }{
13 | {"en-US", language.LanguageWithID("en")},
14 | {"en_US", language.LanguageWithID("en")},
15 | {"zh-CN", language.LanguageWithID("zh")},
16 | {"zh-TW", language.LanguageWithID("zh")},
17 | {"pt-BR", language.LanguageWithID("pt-BR")},
18 | {"pt_BR", language.LanguageWithID("pt-BR")},
19 | {"pt-PT", language.LanguageWithID("pt")},
20 | {"pt_PT", language.LanguageWithID("pt")},
21 | {"zh-Hans-CN", nil},
22 | {"zh-Hant-TW", nil},
23 | {"xx-Yyen-US", nil},
24 | {"en US", nil},
25 | {"en-US-en-US", nil},
26 | {".en-US..en-US.", nil},
27 | }
28 | for _, test := range tests {
29 | loc, err := New(test.localeID)
30 | if loc == nil && test.lang != nil {
31 | t.Errorf("New(%q) = , %q; expected %q, ", test.localeID, err, test.lang)
32 | }
33 | if loc != nil && loc.Language != test.lang {
34 | t.Errorf("New(%q) = %q; expected %q", test.localeID, loc.Language, test.lang)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/go-i18n/i18n/exampletemplate_test.go:
--------------------------------------------------------------------------------
1 | package i18n_test
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n"
5 | "os"
6 | "text/template"
7 | )
8 |
9 | var funcMap = map[string]interface{}{
10 | "T": i18n.IdentityTfunc,
11 | }
12 |
13 | var tmpl = template.Must(template.New("").Funcs(funcMap).Parse(`
14 | {{T "program_greeting"}}
15 | {{T "person_greeting" .}}
16 | {{T "your_unread_email_count" 0}}
17 | {{T "your_unread_email_count" 1}}
18 | {{T "your_unread_email_count" 2}}
19 | {{T "person_unread_email_count" 0 .}}
20 | {{T "person_unread_email_count" 1 .}}
21 | {{T "person_unread_email_count" 2 .}}
22 | `))
23 |
24 | func Example_template() {
25 | i18n.MustLoadTranslationFile("../goi18n/testdata/expected/en-US.all.json")
26 |
27 | T, _ := i18n.Tfunc("en-US")
28 | tmpl.Funcs(map[string]interface{}{
29 | "T": T,
30 | })
31 |
32 | tmpl.Execute(os.Stdout, map[string]interface{}{
33 | "Person": "Bob",
34 | "Timeframe": T("d_days", 1),
35 | })
36 |
37 | // Output:
38 | // Hello world
39 | // Hello Bob
40 | // You have 0 unread emails.
41 | // You have 1 unread email.
42 | // You have 2 unread emails.
43 | // Bob has 0 unread emails.
44 | // Bob has 1 unread email.
45 | // Bob has 2 unread emails.
46 | }
47 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/en-US.all.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "one": "{{.Count}} day",
6 | "other": "{{.Count}} days"
7 | }
8 | },
9 | {
10 | "id": "my_height_in_meters",
11 | "translation": {
12 | "one": "I am {{.Count}} meter tall.",
13 | "other": "I am {{.Count}} meters tall."
14 | }
15 | },
16 | {
17 | "id": "person_greeting",
18 | "translation": "Hello {{.Person}}"
19 | },
20 | {
21 | "id": "person_unread_email_count",
22 | "translation": {
23 | "one": "{{.Person}} has {{.Count}} unread email.",
24 | "other": "{{.Person}} has {{.Count}} unread emails."
25 | }
26 | },
27 | {
28 | "id": "person_unread_email_count_timeframe",
29 | "translation": {
30 | "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}.",
31 | "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
32 | }
33 | },
34 | {
35 | "id": "program_greeting",
36 | "translation": "Hello world"
37 | },
38 | {
39 | "id": "your_unread_email_count",
40 | "translation": {
41 | "one": "You have {{.Count}} unread email.",
42 | "other": "You have {{.Count}} unread emails."
43 | }
44 | }
45 | ]
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/fr-FR.untranslated.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "one": "{{.Count}} days",
6 | "other": "{{.Count}} days"
7 | }
8 | },
9 | {
10 | "id": "my_height_in_meters",
11 | "translation": {
12 | "one": "I am {{.Count}} meters tall.",
13 | "other": "I am {{.Count}} meters tall."
14 | }
15 | },
16 | {
17 | "id": "person_greeting",
18 | "translation": "Hello {{.Person}}"
19 | },
20 | {
21 | "id": "person_unread_email_count",
22 | "translation": {
23 | "one": "{{.Person}} has {{.Count}} unread emails.",
24 | "other": "{{.Person}} has {{.Count}} unread emails."
25 | }
26 | },
27 | {
28 | "id": "person_unread_email_count_timeframe",
29 | "translation": {
30 | "one": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
31 | "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
32 | }
33 | },
34 | {
35 | "id": "program_greeting",
36 | "translation": "Hello world"
37 | },
38 | {
39 | "id": "your_unread_email_count",
40 | "translation": {
41 | "one": "You have {{.Count}} unread emails.",
42 | "other": "You have {{.Count}} unread emails."
43 | }
44 | }
45 | ]
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/input/ar-AR.one.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "few": "arabic few translation of d_days",
6 | "many": "arabic many translation of d_days",
7 | "one": "",
8 | "other": "",
9 | "two": "",
10 | "zero": ""
11 | }
12 | },
13 | {
14 | "id": "person_greeting",
15 | "translation": "arabic translation of person_greeting"
16 | },
17 | {
18 | "id": "person_unread_email_count",
19 | "translation": {
20 | "few": "arabic few translation of person_unread_email_count",
21 | "many": "arabic many translation of person_unread_email_count",
22 | "one": "arabic one translation of person_unread_email_count",
23 | "other": "",
24 | "two": "",
25 | "zero": ""
26 | }
27 | },
28 | {
29 | "id": "person_unread_email_count_timeframe",
30 | "translation": {
31 | "few": "",
32 | "many": "",
33 | "one": "",
34 | "other": "",
35 | "two": "",
36 | "zero": ""
37 | }
38 | },
39 | {
40 | "id": "program_greeting",
41 | "translation": ""
42 | },
43 | {
44 | "id": "your_unread_email_count",
45 | "translation": {
46 | "few": "",
47 | "many": "",
48 | "one": "",
49 | "other": "",
50 | "two": "",
51 | "zero": ""
52 | }
53 | }
54 | ]
55 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/input/ar-AR.two.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "few": "new arabic few translation of d_days",
6 | "many": "",
7 | "one": "arabic one translation of d_days",
8 | "other": "",
9 | "two": "",
10 | "zero": ""
11 | }
12 | },
13 | {
14 | "id": "person_greeting",
15 | "translation": "new arabic translation of person_greeting"
16 | },
17 | {
18 | "id": "person_unread_email_count",
19 | "translation": {
20 | "few": "",
21 | "many": "",
22 | "one": "",
23 | "other": "arabic other translation of person_unread_email_count",
24 | "two": "arabic two translation of person_unread_email_count",
25 | "zero": "arabic zero translation of person_unread_email_count"
26 | }
27 | },
28 | {
29 | "id": "person_unread_email_count_timeframe",
30 | "translation": {
31 | "few": "",
32 | "many": "",
33 | "one": "",
34 | "other": "",
35 | "two": "",
36 | "zero": ""
37 | }
38 | },
39 | {
40 | "id": "program_greeting",
41 | "translation": ""
42 | },
43 | {
44 | "id": "your_unread_email_count",
45 | "translation": {
46 | "few": "",
47 | "many": "",
48 | "one": "",
49 | "other": "",
50 | "two": "",
51 | "zero": ""
52 | }
53 | }
54 | ]
55 |
--------------------------------------------------------------------------------
/go-i18n/i18n/plural/operands_test.go:
--------------------------------------------------------------------------------
1 | package plural
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestNewOperands(t *testing.T) {
9 | tests := []struct {
10 | input interface{}
11 | ops *Operands
12 | err bool
13 | }{
14 | {int64(0), &Operands{0.0, 0, 0, 0, 0, 0}, false},
15 | {int64(1), &Operands{1.0, 1, 0, 0, 0, 0}, false},
16 | {"0", &Operands{0.0, 0, 0, 0, 0, 0}, false},
17 | {"1", &Operands{1.0, 1, 0, 0, 0, 0}, false},
18 | {"1.0", &Operands{1.0, 1, 1, 0, 0, 0}, false},
19 | {"1.00", &Operands{1.0, 1, 2, 0, 0, 0}, false},
20 | {"1.3", &Operands{1.3, 1, 1, 1, 3, 3}, false},
21 | {"1.30", &Operands{1.3, 1, 2, 1, 30, 3}, false},
22 | {"1.03", &Operands{1.03, 1, 2, 2, 3, 3}, false},
23 | {"1.230", &Operands{1.23, 1, 3, 2, 230, 23}, false},
24 | {"20.0230", &Operands{20.023, 20, 4, 3, 230, 23}, false},
25 | {20.0230, nil, true},
26 | }
27 | for _, test := range tests {
28 | ops, err := NewOperands(test.input)
29 | if err != nil && !test.err {
30 | t.Errorf("NewOperands(%#v) unexpected error: %s", test.input, err)
31 | } else if err == nil && test.err {
32 | t.Errorf("NewOperands(%#v) returned %#v; expected error", test.input, ops)
33 | } else if !reflect.DeepEqual(ops, test.ops) {
34 | t.Errorf("NewOperands(%#v) returned %#v; expected %#v", test.input, ops, test.ops)
35 | }
36 | }
37 | }
38 |
39 | func BenchmarkNewOperand(b *testing.B) {
40 | for i := 0; i < b.N; i++ {
41 | if _, err := NewOperands("1234.56780000"); err != nil {
42 | b.Fatal(err)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/static/css/style.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/serviceTests/buoyService_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package serviceTests implements tests for the buoy services.
6 | package serviceTests
7 |
8 | import (
9 | "testing"
10 |
11 | "github.com/goinggo/beego-mgo/services/buoyService"
12 | . "github.com/smartystreets/goconvey/convey"
13 | )
14 |
15 | // Test_Station checks the station service call is working
16 | func Test_Station(t *testing.T) {
17 | service := Prepare()
18 | defer Finish(service)
19 |
20 | stationID := "42002"
21 |
22 | buoyStation, err := buoyService.FindStation(service, stationID)
23 |
24 | Convey("Subject: Test Station Service", t, func() {
25 | Convey("Should Be Able To Perform A Search", func() {
26 | So(err, ShouldEqual, nil)
27 | })
28 | Convey("Should Have Station Data", func() {
29 | So(buoyStation.StationID, ShouldEqual, stationID)
30 | })
31 | })
32 | }
33 |
34 | // Test_Region checks the region service call is working
35 | func Test_Region(t *testing.T) {
36 | service := Prepare()
37 | defer Finish(service)
38 |
39 | region := "Gulf Of Mexico"
40 |
41 | buoyStations, err := buoyService.FindRegion(service, region)
42 |
43 | Convey("Subject: Test Region Service", t, func() {
44 | Convey("Should Be Able To Perform A Search", func() {
45 | So(err, ShouldEqual, nil)
46 | })
47 | Convey("Should Have Region Data", func() {
48 | So(len(buoyStations), ShouldBeGreaterThan, 0)
49 | })
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/single_translation.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n/language"
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
6 | )
7 |
8 | type singleTranslation struct {
9 | id string
10 | template *template
11 | }
12 |
13 | func (st *singleTranslation) MarshalInterface() interface{} {
14 | return map[string]interface{}{
15 | "id": st.id,
16 | "translation": st.template,
17 | }
18 | }
19 |
20 | func (st *singleTranslation) ID() string {
21 | return st.id
22 | }
23 |
24 | func (st *singleTranslation) Template(pc plural.Category) *template {
25 | return st.template
26 | }
27 |
28 | func (st *singleTranslation) UntranslatedCopy() Translation {
29 | return &singleTranslation{st.id, mustNewTemplate("")}
30 | }
31 |
32 | func (st *singleTranslation) Normalize(language *language.Language) Translation {
33 | return st
34 | }
35 |
36 | func (st *singleTranslation) Backfill(src Translation) Translation {
37 | if st.template == nil || st.template.src == "" {
38 | st.template = src.Template(plural.Other)
39 | }
40 | return st
41 | }
42 |
43 | func (st *singleTranslation) Merge(t Translation) Translation {
44 | other, ok := t.(*singleTranslation)
45 | if !ok || st.ID() != t.ID() {
46 | return t
47 | }
48 | if other.template != nil && other.template.src != "" {
49 | st.template = other.template
50 | }
51 | return st
52 | }
53 |
54 | func (st *singleTranslation) Incomplete(l *language.Language) bool {
55 | return st.template == nil || st.template.src == ""
56 | }
57 |
58 | var _ = Translation(&singleTranslation{})
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Simplified BSD License
2 |
3 | Copyright (c) 2013, Ardan Studios
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
26 | The views and conclusions contained in the software and documentation are those
27 | of the authors and should not be interpreted as representing official policies,
28 | either expressed or implied, of the FreeBSD Project.
--------------------------------------------------------------------------------
/services/services.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of service source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package services implements boilerplate code for all services.
6 | package services
7 |
8 | import (
9 | "github.com/goinggo/beego-mgo/utilities/helper"
10 | "github.com/goinggo/beego-mgo/utilities/mongo"
11 | log "github.com/goinggo/tracelog"
12 | "gopkg.in/mgo.v2"
13 | )
14 |
15 | //** TYPES
16 |
17 | type (
18 | // Service contains common properties for all services.
19 | Service struct {
20 | MongoSession *mgo.Session
21 | UserID string
22 | }
23 | )
24 |
25 | //** PUBLIC FUNCTIONS
26 |
27 | // Prepare is called before any controller.
28 | func (service *Service) Prepare() (err error) {
29 | service.MongoSession, err = mongo.CopyMonotonicSession(service.UserID)
30 | if err != nil {
31 | log.Error(err, service.UserID, "Service.Prepare")
32 | return err
33 | }
34 |
35 | return err
36 | }
37 |
38 | // Finish is called after the controller.
39 | func (service *Service) Finish() (err error) {
40 | defer helper.CatchPanic(&err, service.UserID, "Service.Finish")
41 |
42 | if service.MongoSession != nil {
43 | mongo.CloseSession(service.UserID, service.MongoSession)
44 | service.MongoSession = nil
45 | }
46 |
47 | return err
48 | }
49 |
50 | // DBAction executes the MongoDB literal function
51 | func (service *Service) DBAction(databaseName string, collectionName string, dbCall mongo.DBCall) (err error) {
52 | return mongo.Execute(service.UserID, service.MongoSession, databaseName, collectionName, dbCall)
53 | }
54 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/ar-AR.all.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "few": "new arabic few translation of d_days",
6 | "many": "arabic many translation of d_days",
7 | "one": "arabic one translation of d_days",
8 | "other": "",
9 | "two": "",
10 | "zero": ""
11 | }
12 | },
13 | {
14 | "id": "my_height_in_meters",
15 | "translation": {
16 | "few": "",
17 | "many": "",
18 | "one": "",
19 | "other": "",
20 | "two": "",
21 | "zero": ""
22 | }
23 | },
24 | {
25 | "id": "person_greeting",
26 | "translation": "new arabic translation of person_greeting"
27 | },
28 | {
29 | "id": "person_unread_email_count",
30 | "translation": {
31 | "few": "arabic few translation of person_unread_email_count",
32 | "many": "arabic many translation of person_unread_email_count",
33 | "one": "arabic one translation of person_unread_email_count",
34 | "other": "arabic other translation of person_unread_email_count",
35 | "two": "arabic two translation of person_unread_email_count",
36 | "zero": "arabic zero translation of person_unread_email_count"
37 | }
38 | },
39 | {
40 | "id": "person_unread_email_count_timeframe",
41 | "translation": {
42 | "few": "",
43 | "many": "",
44 | "one": "",
45 | "other": "",
46 | "two": "",
47 | "zero": ""
48 | }
49 | },
50 | {
51 | "id": "program_greeting",
52 | "translation": ""
53 | },
54 | {
55 | "id": "your_unread_email_count",
56 | "translation": {
57 | "few": "",
58 | "many": "",
59 | "one": "",
60 | "other": "",
61 | "two": "",
62 | "zero": ""
63 | }
64 | }
65 | ]
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/template.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "bytes"
5 | "encoding"
6 | "strings"
7 | //"launchpad.net/goyaml"
8 | gotemplate "text/template"
9 | )
10 |
11 | type template struct {
12 | tmpl *gotemplate.Template
13 | src string
14 | }
15 |
16 | func newTemplate(src string) (*template, error) {
17 | var tmpl template
18 | err := tmpl.parseTemplate(src)
19 | return &tmpl, err
20 | }
21 |
22 | func mustNewTemplate(src string) *template {
23 | t, err := newTemplate(src)
24 | if err != nil {
25 | panic(err)
26 | }
27 | return t
28 | }
29 |
30 | func (t *template) String() string {
31 | return t.src
32 | }
33 |
34 | func (t *template) Execute(args interface{}) string {
35 | if t.tmpl == nil {
36 | return t.src
37 | }
38 | var buf bytes.Buffer
39 | if err := t.tmpl.Execute(&buf, args); err != nil {
40 | return err.Error()
41 | }
42 | return buf.String()
43 | }
44 |
45 | func (t *template) MarshalText() ([]byte, error) {
46 | return []byte(t.src), nil
47 | }
48 |
49 | func (t *template) UnmarshalText(src []byte) error {
50 | return t.parseTemplate(string(src))
51 | }
52 |
53 | func (t *template) parseTemplate(src string) (err error) {
54 | t.src = src
55 | if strings.Contains(src, "{{") {
56 | t.tmpl, err = gotemplate.New(src).Parse(src)
57 | }
58 | return
59 | }
60 |
61 | var _ = encoding.TextMarshaler(&template{})
62 | var _ = encoding.TextUnmarshaler(&template{})
63 |
64 | /*
65 | func (t *template) GetYAML() (tag string, value interface{}) {
66 | return "", t.src
67 | }
68 |
69 | func (t *template) SetYAML(tag string, value interface{}) bool {
70 | panic(tag)
71 | src, ok := value.(string)
72 | if !ok {
73 | return false
74 | }
75 | return t.parseTemplate(src) == nil
76 | }
77 |
78 | var _ = goyaml.Getter(&template{})
79 | var _ = goyaml.Setter(&template{})
80 | */
81 |
--------------------------------------------------------------------------------
/models/buoyModels/buoyModels.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package buoyModels contains the models for the buoy service.
6 | package buoyModels
7 |
8 | import (
9 | "fmt"
10 |
11 | "gopkg.in/mgo.v2/bson"
12 | )
13 |
14 | //** TYPES
15 |
16 | type (
17 | // BuoyCondition contains information for an individual station.
18 | BuoyCondition struct {
19 | WindSpeed float64 `bson:"wind_speed_milehour" json:"wind_speed_milehour"`
20 | WindDirection int `bson:"wind_direction_degnorth" json:"wind_direction_degnorth"`
21 | WindGust float64 `bson:"gust_wind_speed_milehour" json:"gust_wind_speed_milehour"`
22 | }
23 |
24 | // BuoyLocation contains the buoys location.
25 | BuoyLocation struct {
26 | Type string `bson:"type" json:"type"`
27 | Coordinates []float64 `bson:"coordinates" json:"coordinates"`
28 | }
29 |
30 | // BuoyStation contains information for an individual station.
31 | BuoyStation struct {
32 | ID bson.ObjectId `bson:"_id,omitempty"`
33 | StationID string `bson:"station_id" json:"station_id"`
34 | Name string `bson:"name" json:"name"`
35 | LocDesc string `bson:"location_desc" json:"location_desc"`
36 | Condition BuoyCondition `bson:"condition" json:"condition"`
37 | Location BuoyLocation `bson:"location" json:"location"`
38 | }
39 | )
40 |
41 | // DisplayWindSpeed pretty prints wind speed.
42 | func (buoyCondition *BuoyCondition) DisplayWindSpeed() string {
43 | return fmt.Sprintf("%.2f", buoyCondition.WindSpeed)
44 | }
45 |
46 | // DisplayWindGust pretty prints wind gust.
47 | func (buoyCondition *BuoyCondition) DisplayWindGust() string {
48 | return fmt.Sprintf("%.2f", buoyCondition.WindGust)
49 | }
50 |
--------------------------------------------------------------------------------
/views/shared/basic-layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Sample Beego App - {{.Title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{.PageHead}}
18 |
19 |
20 |
21 |
25 |
26 |
36 |
37 |
38 | {{.Header}}
39 |
40 | {{.LayoutContent}}
41 | {{.Modal}}
42 |
43 |
--------------------------------------------------------------------------------
/go-i18n/i18n/example_test.go:
--------------------------------------------------------------------------------
1 | package i18n_test
2 |
3 | import (
4 | "fmt"
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n"
6 | )
7 |
8 | func Example() {
9 | i18n.MustLoadTranslationFile("../goi18n/testdata/expected/en-US.all.json")
10 |
11 | T, _ := i18n.Tfunc("en-US")
12 |
13 | fmt.Println(T("program_greeting"))
14 | fmt.Println(T("person_greeting", map[string]interface{}{
15 | "Person": "Bob",
16 | }))
17 |
18 | fmt.Println(T("your_unread_email_count", 0))
19 | fmt.Println(T("your_unread_email_count", 1))
20 | fmt.Println(T("your_unread_email_count", 2))
21 | fmt.Println(T("my_height_in_meters", "1.7"))
22 |
23 | fmt.Println(T("person_unread_email_count", 0, map[string]interface{}{
24 | "Person": "Bob",
25 | }))
26 | fmt.Println(T("person_unread_email_count", 1, map[string]interface{}{
27 | "Person": "Bob",
28 | }))
29 | fmt.Println(T("person_unread_email_count", 2, map[string]interface{}{
30 | "Person": "Bob",
31 | }))
32 |
33 | fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
34 | "Person": "Bob",
35 | "Timeframe": T("d_days", 0),
36 | }))
37 | fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
38 | "Person": "Bob",
39 | "Timeframe": T("d_days", 1),
40 | }))
41 | fmt.Println(T("person_unread_email_count_timeframe", 3, map[string]interface{}{
42 | "Person": "Bob",
43 | "Timeframe": T("d_days", 2),
44 | }))
45 |
46 | // Output:
47 | // Hello world
48 | // Hello Bob
49 | // You have 0 unread emails.
50 | // You have 1 unread email.
51 | // You have 2 unread emails.
52 | // I am 1.7 meters tall.
53 | // Bob has 0 unread emails.
54 | // Bob has 1 unread email.
55 | // Bob has 2 unread emails.
56 | // Bob has 3 unread emails in the past 0 days.
57 | // Bob has 3 unread emails in the past 1 day.
58 | // Bob has 3 unread emails in the past 2 days.
59 | }
60 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/merge_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "io/ioutil"
6 | "os"
7 | "testing"
8 | )
9 |
10 | func TestMergeExecute(t *testing.T) {
11 | resetDir(t, "testdata/output")
12 | files := []string{
13 | "testdata/input/en-US.one.json",
14 | "testdata/input/en-US.two.json",
15 | "testdata/input/fr-FR.json",
16 | "testdata/input/ar-AR.one.json",
17 | "testdata/input/ar-AR.two.json",
18 | }
19 |
20 | mc := &mergeCommand{
21 | translationFiles: files,
22 | sourceLocaleID: "en-US",
23 | outdir: "testdata/output",
24 | format: "json",
25 | }
26 | if err := mc.execute(); err != nil {
27 | t.Fatal(err)
28 | }
29 |
30 | expectEqualFiles(t, "testdata/output/en-US.all.json", "testdata/expected/en-US.all.json")
31 | expectEqualFiles(t, "testdata/output/ar-AR.all.json", "testdata/expected/ar-AR.all.json")
32 | expectEqualFiles(t, "testdata/output/fr-FR.all.json", "testdata/expected/fr-FR.all.json")
33 | expectEqualFiles(t, "testdata/output/en-US.untranslated.json", "testdata/expected/en-US.untranslated.json")
34 | expectEqualFiles(t, "testdata/output/ar-AR.untranslated.json", "testdata/expected/ar-AR.untranslated.json")
35 | expectEqualFiles(t, "testdata/output/fr-FR.untranslated.json", "testdata/expected/fr-FR.untranslated.json")
36 | }
37 |
38 | func resetDir(t *testing.T, dir string) {
39 | if err := os.RemoveAll(dir); err != nil {
40 | t.Fatal(err)
41 | }
42 | if err := os.Mkdir(dir, 0777); err != nil {
43 | t.Fatal(err)
44 | }
45 | }
46 |
47 | func expectEqualFiles(t *testing.T, expectedName, actualName string) {
48 | actual, err := ioutil.ReadFile(actualName)
49 | if err != nil {
50 | t.Fatal(err)
51 | }
52 | expected, err := ioutil.ReadFile(expectedName)
53 | if err != nil {
54 | t.Fatal(err)
55 | }
56 | if !bytes.Equal(actual, expected) {
57 | t.Fatalf("contents of files did not match: %s, %s", expectedName, actualName)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/go-i18n/i18n/locale/locale.go:
--------------------------------------------------------------------------------
1 | // Package locale parses locale strings.
2 | package locale
3 |
4 | import (
5 | "fmt"
6 | "github.com/goinggo/beego-mgo/go-i18n/i18n/language"
7 | "regexp"
8 | "strings"
9 | )
10 |
11 | // Locale is a language and a geographic region (e.g. en-US, en-GB).
12 | type Locale struct {
13 | ID string
14 | Language *language.Language
15 | }
16 |
17 | // tagMatcher matches language tags (e.g. zh-CN).
18 | var tagMatcher = regexp.MustCompile(`^([a-z]{2})[_\-]([A-Z]{2})$`)
19 |
20 | // tagSplitter matches characters not found in language tags.
21 | var tagSplitter = regexp.MustCompile(`[^a-zA-Z_\-]+`)
22 |
23 | // New searches s for a valid language tag (RFC 5646)
24 | // of the form xx-YY or xx_YY where
25 | // xx is a 2 character language code and
26 | // YY is a 2 character country code.
27 | //
28 | // It returns an error if s doesn't contain exactly one language tag or
29 | // if the language represented by the tag is not supported by this package.
30 | func New(s string) (*Locale, error) {
31 | parts := tagSplitter.Split(s, -1)
32 | var id, lc string
33 | count := 0
34 | for _, part := range parts {
35 | if tag := tagMatcher.FindStringSubmatch(part); tag != nil {
36 | count += 1
37 | id, lc = tag[0], tag[1]
38 | }
39 | }
40 | if count != 1 {
41 | return nil, fmt.Errorf("%d locales found in string %s", count, s)
42 | }
43 | id = strings.Replace(id, "_", "-", -1)
44 | lang := language.LanguageWithID(id)
45 | if lang == nil {
46 | lang = language.LanguageWithID(lc)
47 | }
48 | if lang == nil {
49 | return nil, fmt.Errorf("unknown language %s", id)
50 | }
51 | return &Locale{id, lang}, nil
52 | }
53 |
54 | // MustNew is similar to New except that it panics if an error happens.
55 | func MustNew(s string) *Locale {
56 | locale, err := New(s)
57 | if err != nil {
58 | panic(err)
59 | }
60 | return locale
61 | }
62 |
63 | func (l *Locale) String() string {
64 | return l.ID
65 | }
66 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/testdata/expected/ar-AR.untranslated.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "d_days",
4 | "translation": {
5 | "few": "new arabic few translation of d_days",
6 | "many": "arabic many translation of d_days",
7 | "one": "arabic one translation of d_days",
8 | "other": "{{.Count}} days",
9 | "two": "{{.Count}} days",
10 | "zero": "{{.Count}} days"
11 | }
12 | },
13 | {
14 | "id": "my_height_in_meters",
15 | "translation": {
16 | "few": "I am {{.Count}} meters tall.",
17 | "many": "I am {{.Count}} meters tall.",
18 | "one": "I am {{.Count}} meters tall.",
19 | "other": "I am {{.Count}} meters tall.",
20 | "two": "I am {{.Count}} meters tall.",
21 | "zero": "I am {{.Count}} meters tall."
22 | }
23 | },
24 | {
25 | "id": "person_unread_email_count_timeframe",
26 | "translation": {
27 | "few": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
28 | "many": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
29 | "one": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
30 | "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
31 | "two": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.",
32 | "zero": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
33 | }
34 | },
35 | {
36 | "id": "program_greeting",
37 | "translation": "Hello world"
38 | },
39 | {
40 | "id": "your_unread_email_count",
41 | "translation": {
42 | "few": "You have {{.Count}} unread emails.",
43 | "many": "You have {{.Count}} unread emails.",
44 | "one": "You have {{.Count}} unread emails.",
45 | "other": "You have {{.Count}} unread emails.",
46 | "two": "You have {{.Count}} unread emails.",
47 | "zero": "You have {{.Count}} unread emails."
48 | }
49 | }
50 | ]
--------------------------------------------------------------------------------
/test/serviceTests/serviceTests.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package serviceTests implements boilerplate code for all testing
6 | package serviceTests
7 |
8 | import (
9 | "github.com/goinggo/beego-mgo/localize"
10 | "github.com/goinggo/beego-mgo/services"
11 | "github.com/goinggo/beego-mgo/utilities/helper"
12 | "github.com/goinggo/beego-mgo/utilities/mongo"
13 | log "github.com/goinggo/tracelog"
14 | )
15 |
16 | //** CONSTANTS
17 |
18 | const (
19 | // SessionID is just mocking the id for testing.
20 | SessionID = "testing"
21 | )
22 |
23 | //** TYPES
24 |
25 | type (
26 | // testController contains state and behavior for testing
27 | testController struct {
28 | services.Service
29 | }
30 | )
31 |
32 | //** INIT
33 |
34 | // init initializes all required packages and systems
35 | func init() {
36 | log.Start(log.LEVEL_TRACE)
37 |
38 | // Init mongo
39 | log.Started("main", "Initializing Mongo")
40 | err := mongo.Startup(helper.MainGoRoutine)
41 | if err != nil {
42 | log.CompletedError(err, helper.MainGoRoutine, "initTesting")
43 | return
44 | }
45 |
46 | // Load message strings
47 | localize.Init("en-US")
48 | }
49 |
50 | //** INTERCEPT FUNCTIONS
51 |
52 | // Prepare is called before controllers are called.
53 | func Prepare() *services.Service {
54 | var service services.Service
55 |
56 | // TODO: Add Test User To Environment
57 | service.UserID = "testing"
58 |
59 | err := service.Prepare()
60 | if err != nil {
61 | log.Error(err, service.UserID, "Prepare")
62 | return nil
63 | }
64 |
65 | log.Trace(service.UserID, "Before", "UserID[%s]", service.UserID)
66 | return &service
67 | }
68 |
69 | // Finish is called after controllers are called.
70 | func Finish(service *services.Service) {
71 | service.Finish()
72 |
73 | log.Completed(service.UserID, "Finish")
74 | }
75 |
--------------------------------------------------------------------------------
/views/buoy/content.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
This examples shows how to load a view, use a partial view and a modal dialog.
19 |
20 |
21 |
22 | | Station ID |
23 | Name |
24 | LocDesc |
25 | Wind Speed |
26 | Wind Direction |
27 | Wind Gust |
28 |
29 | {{range $index, $val := .Stations}}
30 |
31 | | {{$val.StationID}} |
32 | {{$val.Name}} |
33 | {{$val.LocDesc}} |
34 | {{$val.Condition.DisplayWindSpeed}} |
35 | {{$val.Condition.WindDirection}} |
36 | {{$val.Condition.DisplayWindGust}} |
37 |
38 | {{end}}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
This example shows how to return a JSON document.
48 |
49 |
54 |
55 |
56 |
57 | Loading View, Please Wait...
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/plural_translation.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n/language"
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
6 | )
7 |
8 | type pluralTranslation struct {
9 | id string
10 | templates map[plural.Category]*template
11 | }
12 |
13 | func (pt *pluralTranslation) MarshalInterface() interface{} {
14 | return map[string]interface{}{
15 | "id": pt.id,
16 | "translation": pt.templates,
17 | }
18 | }
19 |
20 | func (pt *pluralTranslation) ID() string {
21 | return pt.id
22 | }
23 |
24 | func (pt *pluralTranslation) Template(pc plural.Category) *template {
25 | return pt.templates[pc]
26 | }
27 |
28 | func (pt *pluralTranslation) UntranslatedCopy() Translation {
29 | return &pluralTranslation{pt.id, make(map[plural.Category]*template)}
30 | }
31 |
32 | func (pt *pluralTranslation) Normalize(l *language.Language) Translation {
33 | // Delete plural categories that don't belong to this language.
34 | for pc := range pt.templates {
35 | if _, ok := l.PluralCategories[pc]; !ok {
36 | delete(pt.templates, pc)
37 | }
38 | }
39 | // Create map entries for missing valid categories.
40 | for pc := range l.PluralCategories {
41 | if _, ok := pt.templates[pc]; !ok {
42 | pt.templates[pc] = mustNewTemplate("")
43 | }
44 | }
45 | return pt
46 | }
47 |
48 | func (pt *pluralTranslation) Backfill(src Translation) Translation {
49 | for pc, t := range pt.templates {
50 | if t == nil || t.src == "" {
51 | pt.templates[pc] = src.Template(plural.Other)
52 | }
53 | }
54 | return pt
55 | }
56 |
57 | func (pt *pluralTranslation) Merge(t Translation) Translation {
58 | other, ok := t.(*pluralTranslation)
59 | if !ok || pt.ID() != t.ID() {
60 | return t
61 | }
62 | for pluralCategory, template := range other.templates {
63 | if template != nil && template.src != "" {
64 | pt.templates[pluralCategory] = template
65 | }
66 | }
67 | return pt
68 | }
69 |
70 | func (pt *pluralTranslation) Incomplete(l *language.Language) bool {
71 | for pc := range l.PluralCategories {
72 | if t := pt.templates[pc]; t == nil || t.src == "" {
73 | return true
74 | }
75 | }
76 | return false
77 | }
78 |
79 | var _ = Translation(&pluralTranslation{})
80 |
--------------------------------------------------------------------------------
/go-i18n/i18n/plural/operands.go:
--------------------------------------------------------------------------------
1 | package plural
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 | )
8 |
9 | // http://unicode.org/reports/tr35/tr35-numbers.html#Operands
10 | type Operands struct {
11 | N float64 // absolute value of the source number (integer and decimals)
12 | I int64 // integer digits of n
13 | V int // number of visible fraction digits in n, with trailing zeros
14 | W int // number of visible fraction digits in n, without trailing zeros
15 | F int // visible fractional digits in n, with trailing zeros
16 | T int // visible fractional digits in n, without trailing zeros
17 | }
18 |
19 | func NewOperands(v interface{}) (*Operands, error) {
20 | switch v := v.(type) {
21 | case int:
22 | return newOperandsInt64(int64(v)), nil
23 | case int8:
24 | return newOperandsInt64(int64(v)), nil
25 | case int16:
26 | return newOperandsInt64(int64(v)), nil
27 | case int32:
28 | return newOperandsInt64(int64(v)), nil
29 | case int64:
30 | return newOperandsInt64(v), nil
31 | case string:
32 | return newOperandsString(v)
33 | case float32, float64:
34 | return nil, fmt.Errorf("floats should be formatted into a string")
35 | default:
36 | return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
37 | }
38 | }
39 |
40 | func newOperandsInt64(i int64) *Operands {
41 | if i < 0 {
42 | i = -i
43 | }
44 | return &Operands{float64(i), i, 0, 0, 0, 0}
45 | }
46 |
47 | func newOperandsString(s string) (*Operands, error) {
48 | if s[0] == '-' {
49 | s = s[1:]
50 | }
51 | n, err := strconv.ParseFloat(s, 64)
52 | if err != nil {
53 | return nil, err
54 | }
55 | ops := &Operands{N: n}
56 | parts := strings.SplitN(s, ".", 2)
57 | ops.I, err = strconv.ParseInt(parts[0], 10, 64)
58 | if err != nil {
59 | return nil, err
60 | }
61 | if len(parts) == 1 {
62 | return ops, nil
63 | }
64 | fraction := parts[1]
65 | ops.V = len(fraction)
66 | for i := ops.V - 1; i >= 0; i-- {
67 | if fraction[i] != '0' {
68 | ops.W = i + 1
69 | break
70 | }
71 | }
72 | if ops.V > 0 {
73 | f, err := strconv.ParseInt(fraction, 10, 0)
74 | if err != nil {
75 | return nil, err
76 | }
77 | ops.F = int(f)
78 | }
79 | if ops.W > 0 {
80 | t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
81 | if err != nil {
82 | return nil, err
83 | }
84 | ops.T = int(t)
85 | }
86 | return ops, nil
87 | }
88 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/doc.go:
--------------------------------------------------------------------------------
1 | // The goi18n command formats and merges translation files.
2 | //
3 | // go get -u github.com/finapps/go-i18n/goi18n
4 | // goi18n -help
5 | //
6 | // Help documentation:
7 | //
8 | // goi18n formats and merges translation files.
9 | //
10 | // Usage:
11 | //
12 | // goi18n [options] [files...]
13 | //
14 | // Translation files:
15 | //
16 | // A translation file contains the strings and translations for a single locale (language + country).
17 | //
18 | // Translation file names must have a suffix of a supported format (e.g. .json) and
19 | // contain a valid locale identifier (e.g. ar-EG, en-US, fr-FR, etc.).
20 | //
21 | // For each locale represented by at least one input translation file, goi18n will produce 2 output files:
22 | //
23 | // xx-XX.all.format
24 | // This file contains all strings for the locale (translated and untranslated).
25 | //
26 | // xx-XX.untranslated.format
27 | // This file contains the strings that have not been translated for this locale.
28 | // The translations for the strings in this file will be extracted from the source locale.
29 | // Get these strings translated! After they are translated, merge them back into
30 | // xx-XX.all.format using goi18n.
31 | //
32 | // goi18n will merge multiple translation files for the same locale.
33 | // Duplicate translations will be merged into the existing translation.
34 | // Non-empty fields in the duplicate translation will overwrite those fields in the existing translation.
35 | // Empty fields in the duplicate translation are ignored.
36 | //
37 | // To produce translation files for a new locale, create an empty translation file with the
38 | // appropriate name and pass it in to goi18n.
39 | //
40 | // Options:
41 | //
42 | // -sourceLocale localeId
43 | // The id of the locale that strings are initially written in (e.g. xx-XX)
44 | // Default: en-US
45 | //
46 | // -outdir directory
47 | // goi18n will write the output translation files to this directory.
48 | // Default: .
49 | //
50 | // -format format
51 | // goi18n will encode the output translation files in this format.
52 | // Supported formats: json
53 | // Default: json
54 | //
55 | package main
56 |
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/translation.go:
--------------------------------------------------------------------------------
1 | // Package translation defines the interface for a translation.
2 | package translation
3 |
4 | import (
5 | "fmt"
6 | "github.com/goinggo/beego-mgo/go-i18n/i18n/language"
7 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
8 | )
9 |
10 | // Translation is the interface that represents a translated string.
11 | type Translation interface {
12 | // MarshalInterface returns the object that should be used
13 | // to serialize the translation.
14 | MarshalInterface() interface{}
15 | ID() string
16 | Template(plural.Category) *template
17 | UntranslatedCopy() Translation
18 | Normalize(language *language.Language) Translation
19 | Backfill(src Translation) Translation
20 | Merge(Translation) Translation
21 | Incomplete(l *language.Language) bool
22 | }
23 |
24 | // SortableByID implements sort.Interface for a slice of translations.
25 | type SortableByID []Translation
26 |
27 | func (a SortableByID) Len() int { return len(a) }
28 | func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
29 | func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() }
30 |
31 | // NewTranslation reflects on data to create a new Translation.
32 | //
33 | // data["id"] must be a string and data["translation"] must be either a string
34 | // for a non-plural translation or a map[string]interface{} for a plural translation.
35 | func NewTranslation(data map[string]interface{}) (Translation, error) {
36 | id, ok := data["id"].(string)
37 | if !ok {
38 | return nil, fmt.Errorf(`missing "id" key`)
39 | }
40 | switch translation := data["translation"].(type) {
41 | case string:
42 | tmpl, err := newTemplate(translation)
43 | if err != nil {
44 | return nil, err
45 | }
46 | return &singleTranslation{id, tmpl}, nil
47 | case map[string]interface{}:
48 | templates := make(map[plural.Category]*template, len(translation))
49 | for k, v := range translation {
50 | pc, err := plural.NewCategory(k)
51 | if err != nil {
52 | return nil, err
53 | }
54 | str, ok := v.(string)
55 | if !ok {
56 | return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v)
57 | }
58 | tmpl, err := newTemplate(str)
59 | if err != nil {
60 | return nil, err
61 | }
62 | templates[pc] = tmpl
63 | }
64 | return &pluralTranslation{id, templates}, nil
65 | case nil:
66 | return nil, fmt.Errorf(`missing "translation" key`)
67 | default:
68 | return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Beego Mgo Example
2 |
3 | Copyright 2013 Ardan Studios. All rights reserved.
4 | Use of this source code is governed by a BSD-style license that can be found in the LICENSE handle.
5 |
6 | This application provides a sample to use the beego web framework and the Go MongoDB driver mgo. This program connects to a public MongoDB at MongoLab. A single collection is available for testing.
7 |
8 | The project includes several shell scripts in the zscripts folder to make building, running and testing the web application easier.
9 |
10 | GoingGo.net Post:
11 | http://www.goinggo.net/2013/12/sample-web-application-using-beego-and.html
12 |
13 | Ardan Studios
14 | 12973 SW 112 ST, Suite 153
15 | Miami, FL 33186
16 | bill@ardanstudios.com
17 |
18 | ### Installation
19 |
20 | -- YOU MUST HAVE BAZAAR INSTALLED
21 | http://wiki.bazaar.canonical.com/Download
22 |
23 | -- Get, build and install the code
24 | go get github.com/goinggo/beego-mgo
25 |
26 | -- Run the web service
27 | cd $GOPATH/src/github.com/goinggo/beego-mgo/zscripts
28 | ./runbuild.sh
29 |
30 | -- Run the tests
31 | cd $GOPATH/src/github.com/goinggo/beego-mgo/zscripts
32 | ./runtests.sh
33 |
34 | -- Test Web Service API's
35 | Run the home page and go through the tabs
36 | http://localhost:9003
37 |
38 | ### Notes About Architecture
39 |
40 | I have been asked why I have organized the code in this way?
41 |
42 | The models folder contains the data structures for the individual services. Each service places their models in a separate folder.
43 |
44 | The services folder contain the raw service calls that the business layer would use to implement higher level functionality.
45 |
46 | The controller methods handle and process the requests.
47 |
48 | The more that can be abstracted into the base controller and base service the better. This way, adding a new functionality is simple and you don't need to worry about forgetting to do something important. Authentication always comes to mind.
49 |
50 | The utilities folder is just that, support for the web application, mostly used by the services. You have exception handling support, extended logging support and the mongo support.
51 |
52 | The abstraction layer for executing MongoDB queries and commands help hide the boilerplate code away into the base service and mongo utility code.
53 |
54 | Using environmental variables for the configuration parameters provides a best practice for minimizing security risks. The scripts in the zscripts folder contains the environment variables required to run the web application. In a real project these settings would never be saved in source control.
55 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/goi18n.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | func usage() {
10 | fmt.Printf(`goi18n formats and merges translation files.
11 |
12 | Usage:
13 |
14 | goi18n [options] [files...]
15 |
16 | Translation files:
17 |
18 | A translation file contains the strings and translations for a single locale (language + country).
19 |
20 | Translation file names must have a suffix of a supported format (e.g. .json) and
21 | contain a valid locale identifier (e.g. ar-EG, en-US, fr-FR, etc.).
22 |
23 | For each locale represented by at least one input translation file, goi18n will produce 2 output files:
24 |
25 | xx-XX.all.format
26 | This file contains all strings for the locale (translated and untranslated).
27 |
28 | xx-XX.untranslated.format
29 | This file contains the strings that have not been translated for this locale.
30 | The translations for the strings in this file will be extracted from the source locale.
31 | Get these strings translated! After they are translated, merge them back into
32 | xx-XX.all.format using goi18n.
33 |
34 | goi18n will merge multiple translation files for the same locale.
35 | Duplicate translations will be merged into the existing translation.
36 | Non-empty fields in the duplicate translation will overwrite those fields in the existing translation.
37 | Empty fields in the duplicate translation are ignored.
38 |
39 | To produce translation files for a new locale, create an empty translation file with the
40 | appropriate name and pass it in to goi18n.
41 |
42 | Options:
43 |
44 | -sourceLocale localeId
45 | The id of the locale that strings are initially written in (e.g. xx-XX)
46 | Default: en-US
47 |
48 | -outdir directory
49 | goi18n will write the output translation files to this directory.
50 | Default: .
51 |
52 | -format format
53 | goi18n will encode the output translation files in this format.
54 | Supported formats: json
55 | Default: json
56 |
57 | `)
58 | os.Exit(1)
59 | }
60 |
61 | func main() {
62 | flag.Usage = usage
63 | sourceLocale := flag.String("sourceLocale", "en-US", "")
64 | outdir := flag.String("outdir", ".", "")
65 | format := flag.String("format", "json", "")
66 | flag.Parse()
67 |
68 | mc := &mergeCommand{
69 | translationFiles: flag.Args(),
70 | sourceLocaleID: *sourceLocale,
71 | outdir: *outdir,
72 | format: *format,
73 | }
74 | if err := mc.execute(); err != nil {
75 | fmt.Println(err.Error())
76 | os.Exit(1)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/static/js/buoy/buoy.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('.detail').click(function(e) {
3 | e.preventDefault();
4 | ShowDetail(this);
5 | });
6 |
7 | $('#station-names-json').change(function() {
8 | LoadStationJson();
9 | });
10 |
11 | $('#load-station-button-json').click(function() {
12 | LoadStationJsonOwnTab();
13 | });
14 |
15 | LoadStationJson();
16 | });
17 |
18 | function Standard_Callback() {
19 | try {
20 | alert(this.ResultString);
21 | }
22 |
23 | catch (e) {
24 | alert(e);
25 | }
26 | }
27 |
28 | function Standard_ValidationCallback() {
29 | try {
30 | alert(this.ResultString);
31 | }
32 |
33 | catch (e) {
34 | alert(e);
35 | }
36 | }
37 |
38 | function Standard_ErrorCallback() {
39 | try {
40 | alert(this.ResultString);
41 | }
42 |
43 | catch (e) {
44 | alert(e);
45 | }
46 | }
47 |
48 | function ShowDetail(result) {
49 | try {
50 | var postData = {};
51 | postData["stationId"] = $(result).attr('data');
52 |
53 | var service = new ServiceResult();
54 | service.getJSONData("/buoy/retrievestation",
55 | postData,
56 | ShowDetail_Callback,
57 | Standard_ValidationCallback,
58 | Standard_ErrorCallback
59 | );
60 | }
61 |
62 | catch (e) {
63 | alert(e);
64 | }
65 | }
66 |
67 | function ShowDetail_Callback() {
68 | try {
69 | $('#system-modal-title').html("Buoy Details");
70 | $('#system-modal-content').html(this.ResultObject);
71 | $("#systemModal").modal('show');
72 | }
73 |
74 | catch (e) {
75 | alert(e);
76 | }
77 | }
78 |
79 | function LoadStationJson() {
80 | try {
81 | $('#stations-view').html('Loading View, Please Wait...');
82 |
83 | url = "/buoy/station/" + $('#station-names-json').val();
84 |
85 | var postData = {};
86 |
87 | var service = new ServiceResult();
88 | service.getJSONDataRaw(url,
89 | postData,
90 | LoadStationJson_Callback
91 | );
92 | }
93 |
94 | catch (e) {
95 | alert(e);
96 | }
97 | }
98 |
99 | function LoadStationJson_Callback() {
100 | try {
101 | $('#stations-view-json').html(JSON.stringify(this.Data));
102 | }
103 |
104 | catch (e) {
105 | alert(e);
106 | }
107 | }
108 |
109 | function LoadStationJsonOwnTab() {
110 | url = "/buoy/station/" + $('#station-names-json').val();
111 | window.open(url);
112 | }
113 |
--------------------------------------------------------------------------------
/services/buoyService/buoyService.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package buoyService implements the service for the buoy functionality.
6 | package buoyService
7 |
8 | import (
9 | "github.com/goinggo/beego-mgo/models/buoyModels"
10 | "github.com/goinggo/beego-mgo/services"
11 | "github.com/goinggo/beego-mgo/utilities/helper"
12 | "github.com/goinggo/beego-mgo/utilities/mongo"
13 | log "github.com/goinggo/tracelog"
14 | "github.com/kelseyhightower/envconfig"
15 | "gopkg.in/mgo.v2"
16 | "gopkg.in/mgo.v2/bson"
17 | )
18 |
19 | //** TYPES
20 |
21 | type (
22 | // buoyConfiguration contains settings for running the buoy service.
23 | buoyConfiguration struct {
24 | Database string
25 | }
26 | )
27 |
28 | //** PACKAGE VARIABLES
29 |
30 | // Config provides buoy configuration.
31 | var Config buoyConfiguration
32 |
33 | //** INIT
34 |
35 | func init() {
36 | // Pull in the configuration.
37 | if err := envconfig.Process("buoy", &Config); err != nil {
38 | log.CompletedError(err, helper.MainGoRoutine, "Init")
39 | }
40 | }
41 |
42 | //** PUBLIC FUNCTIONS
43 |
44 | // FindStation retrieves the specified station
45 | func FindStation(service *services.Service, stationID string) (*buoyModels.BuoyStation, error) {
46 | log.Startedf(service.UserID, "FindStation", "stationID[%s]", stationID)
47 |
48 | var buoyStation buoyModels.BuoyStation
49 | f := func(collection *mgo.Collection) error {
50 | queryMap := bson.M{"station_id": stationID}
51 |
52 | log.Trace(service.UserID, "FindStation", "MGO : db.buoy_stations.find(%s).limit(1)", mongo.ToString(queryMap))
53 | return collection.Find(queryMap).One(&buoyStation)
54 | }
55 |
56 | if err := service.DBAction(Config.Database, "buoy_stations", f); err != nil {
57 | if err != mgo.ErrNotFound {
58 | log.CompletedError(err, service.UserID, "FindStation")
59 | return nil, err
60 | }
61 | }
62 |
63 | log.Completedf(service.UserID, "FindStation", "buoyStation%+v", &buoyStation)
64 | return &buoyStation, nil
65 | }
66 |
67 | // FindRegion retrieves the stations for the specified region
68 | func FindRegion(service *services.Service, region string) ([]buoyModels.BuoyStation, error) {
69 | log.Startedf(service.UserID, "FindRegion", "region[%s]", region)
70 |
71 | var buoyStations []buoyModels.BuoyStation
72 | f := func(collection *mgo.Collection) error {
73 | queryMap := bson.M{"region": region}
74 |
75 | log.Trace(service.UserID, "FindRegion", "Query : db.buoy_stations.find(%s)", mongo.ToString(queryMap))
76 | return collection.Find(queryMap).All(&buoyStations)
77 | }
78 |
79 | if err := service.DBAction(Config.Database, "buoy_stations", f); err != nil {
80 | log.CompletedError(err, service.UserID, "FindRegion")
81 | return nil, err
82 | }
83 |
84 | log.Completedf(service.UserID, "FindRegion", "buoyStations%+v", buoyStations)
85 | return buoyStations, nil
86 | }
87 |
--------------------------------------------------------------------------------
/controllers/buoyController.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of controller source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package controllers implements the controller layer for the buoy API.
6 | package controllers
7 |
8 | import (
9 | bc "github.com/goinggo/beego-mgo/controllers/baseController"
10 | "github.com/goinggo/beego-mgo/services/buoyService"
11 | log "github.com/goinggo/tracelog"
12 | )
13 |
14 | //** TYPES
15 |
16 | // BuoyController manages the API for buoy related functionality.
17 | type BuoyController struct {
18 | bc.BaseController
19 | }
20 |
21 | //** WEB FUNCTIONS
22 |
23 | // Index is the initial view for the buoy system.
24 | func (controller *BuoyController) Index() {
25 | region := "Gulf Of Mexico"
26 | log.Startedf(controller.UserID, "BuoyController.Index", "Region[%s]", region)
27 |
28 | buoyStations, err := buoyService.FindRegion(&controller.Service, region)
29 | if err != nil {
30 | log.CompletedErrorf(err, controller.UserID, "BuoyController.Index", "Region[%s]", region)
31 | controller.ServeError(err)
32 | return
33 | }
34 |
35 | controller.Data["Stations"] = buoyStations
36 | controller.Layout = "shared/basic-layout.html"
37 | controller.TplNames = "buoy/content.html"
38 | controller.LayoutSections = map[string]string{}
39 | controller.LayoutSections["PageHead"] = "buoy/page-head.html"
40 | controller.LayoutSections["Header"] = "shared/header.html"
41 | controller.LayoutSections["Modal"] = "shared/modal.html"
42 | }
43 |
44 | //** AJAX FUNCTIONS
45 |
46 | // RetrieveStation handles the example 2 tab.
47 | func (controller *BuoyController) RetrieveStation() {
48 | var params struct {
49 | StationID string `form:"stationID" valid:"Required; MinSize(4)" error:"invalid_station_id"`
50 | }
51 |
52 | if controller.ParseAndValidate(¶ms) == false {
53 | return
54 | }
55 |
56 | buoyStation, err := buoyService.FindStation(&controller.Service, params.StationID)
57 | if err != nil {
58 | log.CompletedErrorf(err, controller.UserID, "BuoyController.RetrieveStation", "StationID[%s]", params.StationID)
59 | controller.ServeError(err)
60 | return
61 | }
62 |
63 | controller.Data["Station"] = buoyStation
64 | controller.Layout = ""
65 | controller.TplNames = "buoy/modal/pv_station-detail.html"
66 | view, _ := controller.RenderString()
67 |
68 | controller.AjaxResponse(0, "SUCCESS", view)
69 | }
70 |
71 | // RetrieveStationJSON handles the example 3 tab.
72 | // http://localhost:9003/buoy/station/42002
73 | func (controller *BuoyController) RetrieveStationJSON() {
74 | // The call to ParseForm inside of ParseAndValidate is failing. This is a BAD FIX
75 | params := struct {
76 | StationID string `form:":stationId" valid:"Required; MinSize(4)" error:"invalid_station_id"`
77 | }{controller.GetString(":stationId")}
78 |
79 | if controller.ParseAndValidate(¶ms) == false {
80 | return
81 | }
82 |
83 | buoyStation, err := buoyService.FindStation(&controller.Service, params.StationID)
84 | if err != nil {
85 | log.CompletedErrorf(err, controller.UserID, "Station", "StationID[%s]", params.StationID)
86 | controller.ServeError(err)
87 | return
88 | }
89 |
90 | controller.Data["json"] = buoyStation
91 | controller.ServeJson()
92 | }
93 |
--------------------------------------------------------------------------------
/go-i18n/i18n/bundle/bundle_test.go:
--------------------------------------------------------------------------------
1 | package bundle
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n/locale"
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n/translation"
6 | "testing"
7 | )
8 |
9 | func TestMustLoadTranslationFile(t *testing.T) {
10 | t.Skipf("not implemented")
11 | }
12 |
13 | func TestLoadTranslationFile(t *testing.T) {
14 | t.Skipf("not implemented")
15 | }
16 |
17 | func TestAddTranslation(t *testing.T) {
18 | t.Skipf("not implemented")
19 | }
20 |
21 | func TestMustTfunc(t *testing.T) {
22 | defer func() {
23 | if r := recover(); r == nil {
24 | t.Errorf("expected MustTfunc to panic")
25 | }
26 | }()
27 | New().MustTfunc("invalid")
28 | }
29 |
30 | func TestTfunc(t *testing.T) {
31 | b := New()
32 | translationID := "translation_id"
33 | englishTranslation := "en-US(translation_id)"
34 | b.AddTranslation(locale.MustNew("en-US"), testNewTranslation(t, map[string]interface{}{
35 | "id": translationID,
36 | "translation": englishTranslation,
37 | }))
38 | frenchTranslation := "fr-FR(translation_id)"
39 | b.AddTranslation(locale.MustNew("fr-FR"), testNewTranslation(t, map[string]interface{}{
40 | "id": translationID,
41 | "translation": frenchTranslation,
42 | }))
43 |
44 | tests := []struct {
45 | localeIDs []string
46 | valid bool
47 | result string
48 | }{
49 | {
50 | []string{"invalid"},
51 | false,
52 | translationID,
53 | },
54 | {
55 | []string{"invalid", "invalid2"},
56 | false,
57 | translationID,
58 | },
59 | {
60 | []string{"invalid", "en-US"},
61 | true,
62 | englishTranslation,
63 | },
64 | {
65 | []string{"en-US", "invalid"},
66 | true,
67 | englishTranslation,
68 | },
69 | {
70 | []string{"en-US", "fr-FR"},
71 | true,
72 | englishTranslation,
73 | },
74 | }
75 |
76 | for _, test := range tests {
77 | tf, err := b.Tfunc(test.localeIDs[0], test.localeIDs[1:]...)
78 | if err != nil && test.valid {
79 | t.Errorf("Tfunc for %v returned error %s", test.localeIDs, err)
80 | }
81 | if err == nil && !test.valid {
82 | t.Errorf("Tfunc for %v returned nil error", test.localeIDs)
83 | }
84 | if result := tf(translationID); result != test.result {
85 | t.Errorf("translation was %s; expected %s", result, test.result)
86 | }
87 | }
88 | }
89 |
90 | func testNewTranslation(t *testing.T, data map[string]interface{}) translation.Translation {
91 | translation, err := translation.NewTranslation(data)
92 | if err != nil {
93 | t.Fatal(err)
94 | }
95 | return translation
96 | }
97 |
98 | /*
99 |
100 | func bundleFixture(t *testing.T) *Bundle {
101 | l, err := NewLocaleFromString("ar-EG")
102 | if err != nil {
103 | t.Errorf(err.Error())
104 | }
105 | return &Bundle{
106 | Locale: l,
107 | localizedStrings: map[string]*LocalizedString{
108 | "a": &LocalizedString{
109 | ID: "a",
110 | },
111 | "b": &LocalizedString{
112 | ID: "b",
113 | Translation: "translation(b)",
114 | },
115 | "c": &LocalizedString{
116 | ID: "c",
117 | Translations: map[PluralCategory]*PluralTranslation{
118 | Zero: NewPluralTranslation("zero(c)"),
119 | One: NewPluralTranslation("one(c)"),
120 | Two: NewPluralTranslation("two(c)"),
121 | Few: NewPluralTranslation("few(c)"),
122 | Many: NewPluralTranslation("many(c)"),
123 | Other: NewPluralTranslation("other(c)"),
124 | },
125 | },
126 | "d": &LocalizedString{
127 | ID: "d",
128 | Translations: map[PluralCategory]*PluralTranslation{
129 | Zero: NewPluralTranslation("zero(d)"),
130 | One: NewPluralTranslation("one(d)"),
131 | },
132 | },
133 | },
134 | }
135 | }
136 | */
137 |
--------------------------------------------------------------------------------
/static/js/service.js:
--------------------------------------------------------------------------------
1 | function ServiceResult() {
2 | var processJSONDataRaw = function (data) {
3 | try {
4 | this.Data = data;
5 | this.SuccessCallback.call(this);
6 | }
7 |
8 | catch (e) {
9 | // TODO: Add Err Handler. Localize Message.
10 | alert("Error Processing Request : " + e);
11 | }
12 | };
13 |
14 | var processJSONData = function (data) {
15 | try {
16 | this.Result = data.Result;
17 | this.ResultString = data.ResultString;
18 | this.ResultObject = data.ResultObject;
19 |
20 | switch (this.Result)
21 | {
22 | case 0: // Success
23 | this.SuccessCallback.call(this);
24 | break;
25 |
26 | case 100: // Validation Error
27 | if (this.ValidationCallback !== undefined)
28 | {
29 | this.ValidationCallback.call(this);
30 | }
31 |
32 | break;
33 |
34 | case 200: // Session Timeout Error
35 | alert(ResultString);
36 | if (locationUrls.Logout === undefined)
37 | {
38 | window.location.href = '/';
39 | return;
40 | }
41 |
42 | window.location.href = locationUrls.Logout;
43 | break;
44 |
45 | default: // Other Error
46 | alert('Error: ' + this.ResultString);
47 | if (this.ErrorCallback !== undefined)
48 | {
49 | this.ErrorCallback.call(this);
50 | }
51 | break;
52 | }
53 | }
54 |
55 | catch (e) {
56 | // TODO: Add Err Handler. Localize Message.
57 | alert("Error Processing Request : " + e);
58 | }
59 | };
60 |
61 | // Handles Ajax Error
62 | var processError = function (objXHR, textStatus, error) {
63 | try {
64 | alert('Error: ' + textStatus);
65 | this.ErrorCallback.call(this);
66 | }
67 |
68 | catch (e) {
69 | }
70 | };
71 |
72 | // Object Definition
73 | return {
74 | Data: '',
75 | Result: '',
76 | ResultString: '',
77 | ResultObject: '',
78 | SuccessCallback: function () {},
79 | ValidationCallback: '',
80 | ErrorCallback: '',
81 | Baggage: '',
82 |
83 | getJSONDataBasic: function (url, data, callBack, baggage) {
84 | //Calls Base Method
85 | this.getJSONData(url, data, callback, callback, callback, baggage);
86 | },
87 |
88 | // Method to Post Data via JSON
89 | getJSONData: function (url, data, callback, validationCallBack, errorCallBack, baggage) {
90 | this.SuccessCallback = callback;
91 | this.ValidationCallback = validationCallBack;
92 | this.ErrorCallback = errorCallBack;
93 | this.Baggage = baggage;
94 |
95 | $.ajax({
96 | url: url,
97 | data: data,
98 | dataType: 'json',
99 | error: processError,
100 | success: processJSONData,
101 | context: this,
102 | type: 'POST'
103 | });
104 | },
105 |
106 | // Method to Post Data via JSON
107 | getJSONDataRaw: function (url, data, callback) {
108 | this.SuccessCallback = callback;
109 | $.ajax({
110 | url: url,
111 | data: data,
112 | dataType: 'json',
113 | error: processJSONDataRaw,
114 | success: processJSONDataRaw,
115 | context: this,
116 | type: 'POST'
117 | });
118 | }
119 | };
120 | }
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/template_test.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | //"launchpad.net/goyaml"
7 | "testing"
8 | gotemplate "text/template"
9 | )
10 |
11 | func TestNilTemplate(t *testing.T) {
12 | expected := "hello"
13 | tmpl := &template{
14 | tmpl: nil,
15 | src: expected,
16 | }
17 | if actual := tmpl.Execute(nil); actual != expected {
18 | t.Errorf("Execute(nil) returned %s; expected %s", actual, expected)
19 | }
20 | }
21 |
22 | func TestMarshalText(t *testing.T) {
23 | tmpl := &template{
24 | tmpl: gotemplate.Must(gotemplate.New("id").Parse("this is a {{.foo}} template")),
25 | src: "boom",
26 | }
27 | expectedBuf := []byte(tmpl.src)
28 | if buf, err := tmpl.MarshalText(); !bytes.Equal(buf, expectedBuf) || err != nil {
29 | t.Errorf("MarshalText() returned %#v, %#v; expected %#v, nil", buf, err, expectedBuf)
30 | }
31 | }
32 |
33 | func TestUnmarshalText(t *testing.T) {
34 | tmpl := &template{}
35 | tmpl.UnmarshalText([]byte("hello {{.World}}"))
36 | result := tmpl.Execute(map[string]string{
37 | "World": "world!",
38 | })
39 | expected := "hello world!"
40 | if result != expected {
41 | t.Errorf("expected %#v; got %#v", expected, result)
42 | }
43 | }
44 |
45 | /*
46 | func TestYAMLMarshal(t *testing.T) {
47 | src := "hello {{.World}}"
48 | tmpl, err := newTemplate(src)
49 | if err != nil {
50 | t.Fatal(err)
51 | }
52 | buf, err := goyaml.Marshal(tmpl)
53 | if err != nil {
54 | t.Fatal(err)
55 | }
56 | if !bytes.Equal(buf, []byte(src)) {
57 | t.Fatalf(`expected "%s"; got "%s"`, src, buf)
58 | }
59 | }
60 |
61 | func TestYAMLUnmarshal(t *testing.T) {
62 | buf := []byte(`Tmpl: "hello"`)
63 |
64 | var out struct {
65 | Tmpl *template
66 | }
67 | var foo map[string]string
68 | if err := goyaml.Unmarshal(buf, &foo); err != nil {
69 | t.Fatal(err)
70 | }
71 | if out.Tmpl == nil {
72 | t.Fatalf("out.Tmpl was nil")
73 | }
74 | if out.Tmpl.tmpl == nil {
75 | t.Fatalf("out.Tmpl.tmpl was nil")
76 | }
77 | if expected := "hello {{.World}}"; out.Tmpl.src != expected {
78 | t.Fatalf("expected %s; got %s", expected, out.Tmpl.src)
79 | }
80 | }
81 |
82 | func TestGetYAML(t *testing.T) {
83 | src := "hello"
84 | tmpl := &template{
85 | tmpl: nil,
86 | src: src,
87 | }
88 | if tag, value := tmpl.GetYAML(); tag != "" || value != src {
89 | t.Errorf("GetYAML() returned (%#v, %#v); expected (%#v, %#v)", tag, value, "", src)
90 | }
91 | }
92 |
93 | func TestSetYAML(t *testing.T) {
94 | tmpl := &template{}
95 | tmpl.SetYAML("tagDoesntMatter", "hello {{.World}}")
96 | result := tmpl.Execute(map[string]string{
97 | "World": "world!",
98 | })
99 | expected := "hello world!"
100 | if result != expected {
101 | t.Errorf("expected %#v; got %#v", expected, result)
102 | }
103 | }
104 | */
105 |
106 | func BenchmarkExecuteNilTemplate(b *testing.B) {
107 | template := &template{src: "hello world"}
108 | b.ResetTimer()
109 | for i := 0; i < b.N; i++ {
110 | template.Execute(nil)
111 | }
112 | }
113 |
114 | func BenchmarkExecuteHelloWorldTemplate(b *testing.B) {
115 | template, err := newTemplate("hello world")
116 | if err != nil {
117 | b.Fatal(err)
118 | }
119 | b.ResetTimer()
120 | for i := 0; i < b.N; i++ {
121 | template.Execute(nil)
122 | }
123 | }
124 |
125 | // Executing a simple template like this is ~6x slower than Sprintf
126 | // but it is still only a few microseconds which should be sufficiently fast.
127 | // The benefit is that we have nice semantic tags in the translation.
128 | func BenchmarkExecuteHelloNameTemplate(b *testing.B) {
129 | template, err := newTemplate("hello {{.Name}}")
130 | if err != nil {
131 | b.Fatal(err)
132 | }
133 | b.ResetTimer()
134 | for i := 0; i < b.N; i++ {
135 | template.Execute(map[string]string{
136 | "Name": "Nick",
137 | })
138 | }
139 | }
140 |
141 | func BenchmarkSprintf(b *testing.B) {
142 | b.ResetTimer()
143 | for i := 0; i < b.N; i++ {
144 | fmt.Sprintf("hello %s", "nick")
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/test/endpointTests/buoyEndpoints_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package endpointTests implements tests for the buoy endpoints.
6 | package endpointTests
7 |
8 | import (
9 | "net/http"
10 | "net/http/httptest"
11 | "testing"
12 |
13 | "encoding/json"
14 |
15 | "github.com/astaxie/beego"
16 | log "github.com/goinggo/tracelog"
17 | . "github.com/smartystreets/goconvey/convey"
18 | )
19 |
20 | // TestStation is a sample to run an endpoint test
21 | func TestStation(t *testing.T) {
22 | r, _ := http.NewRequest("GET", "/buoy/station/42002", nil)
23 | w := httptest.NewRecorder()
24 | beego.BeeApp.Handlers.ServeHTTP(w, r)
25 |
26 | log.Trace("testing", "TestStation", "Code[%d]\n%s", w.Code, w.Body.String())
27 |
28 | var response struct {
29 | StationID string `json:"station_id"`
30 | Name string `json:"name"`
31 | LocDesc string `json:"location_desc"`
32 | Condition struct {
33 | Type string `json:"type"`
34 | Coordinates []float64 `json:"coordinates"`
35 | } `json:"condition"`
36 | Location struct {
37 | WindSpeed float64 `json:"wind_speed_milehour"`
38 | WindDirection int `json:"wind_direction_degnorth"`
39 | WindGust float64 `json:"gust_wind_speed_milehour"`
40 | } `json:"location"`
41 | }
42 | json.Unmarshal(w.Body.Bytes(), &response)
43 |
44 | Convey("Subject: Test Station Endpoint\n", t, func() {
45 | Convey("Status Code Should Be 200", func() {
46 | So(w.Code, ShouldEqual, 200)
47 | })
48 | Convey("The Result Should Not Be Empty", func() {
49 | So(w.Body.Len(), ShouldBeGreaterThan, 0)
50 | })
51 | Convey("There Should Be A Result For Station 42002", func() {
52 | So(response.StationID, ShouldEqual, "42002")
53 | })
54 | })
55 | }
56 |
57 | // TestInvalidStation is a sample to run an endpoint test that returns
58 | // an empty result set
59 | func TestInvalidStation(t *testing.T) {
60 | r, _ := http.NewRequest("GET", "/buoy/station/000000", nil)
61 | w := httptest.NewRecorder()
62 | beego.BeeApp.Handlers.ServeHTTP(w, r)
63 |
64 | log.Trace("testing", "TestStation", "Code[%d]\n%s", w.Code, w.Body.String())
65 |
66 | var response struct {
67 | StationID string `json:"station_id"`
68 | Name string `json:"name"`
69 | LocDesc string `json:"location_desc"`
70 | Condition struct {
71 | Type string `json:"type"`
72 | Coordinates []float64 `json:"coordinates"`
73 | } `json:"condition"`
74 | Location struct {
75 | WindSpeed float64 `json:"wind_speed_milehour"`
76 | WindDirection int `json:"wind_direction_degnorth"`
77 | WindGust float64 `json:"gust_wind_speed_milehour"`
78 | } `json:"location"`
79 | }
80 | json.Unmarshal(w.Body.Bytes(), &response)
81 |
82 | Convey("Subject: Test Station Endpoint\n", t, func() {
83 | Convey("Status Code Should Be 200", func() {
84 | So(w.Code, ShouldEqual, 200)
85 | })
86 | Convey("The Result Should Not Be Empty", func() {
87 | So(w.Body.Len(), ShouldBeGreaterThan, 0)
88 | })
89 | Convey("The Result Should Be Empty For Station 00000", func() {
90 | So(response.StationID, ShouldBeBlank)
91 | })
92 | })
93 | }
94 |
95 | // TestInvalidStation is a sample to run an endpoint test that returns
96 | // an empty result set
97 | func TestMissingStation(t *testing.T) {
98 | r, _ := http.NewRequest("GET", "/buoy/station/420", nil)
99 | w := httptest.NewRecorder()
100 | beego.BeeApp.Handlers.ServeHTTP(w, r)
101 |
102 | log.Trace("testing", "TestStation", "Code[%d]\n%s", w.Code, w.Body.String())
103 |
104 | var err struct {
105 | Errors []string `json:"errors"`
106 | }
107 | json.Unmarshal(w.Body.Bytes(), &err)
108 |
109 | Convey("Subject: Test Station Endpoint\n", t, func() {
110 | Convey("Status Code Should Be 409", func() {
111 | So(w.Code, ShouldEqual, 409)
112 | })
113 | Convey("The Result Should Not Be Empty", func() {
114 | So(w.Body.Len(), ShouldBeGreaterThan, 0)
115 | })
116 | Convey("The Should Be An Error In The Result", func() {
117 | So(len(err.Errors), ShouldEqual, 1)
118 | })
119 | })
120 | }
121 |
--------------------------------------------------------------------------------
/go-i18n/goi18n/merge.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | //"launchpad.net/goyaml"
8 | "github.com/goinggo/beego-mgo/go-i18n/i18n/bundle"
9 | "github.com/goinggo/beego-mgo/go-i18n/i18n/locale"
10 | "github.com/goinggo/beego-mgo/go-i18n/i18n/translation"
11 | "path/filepath"
12 | "reflect"
13 | "sort"
14 | )
15 |
16 | type mergeCommand struct {
17 | translationFiles []string
18 | sourceLocaleID string
19 | outdir string
20 | format string
21 | }
22 |
23 | func (mc *mergeCommand) execute() error {
24 | if len(mc.translationFiles) < 1 {
25 | return fmt.Errorf("need at least one translation file to parse")
26 | }
27 |
28 | if _, err := locale.New(mc.sourceLocaleID); err != nil {
29 | return fmt.Errorf("invalid source locale %s: %s", mc.sourceLocaleID, err)
30 | }
31 |
32 | marshal, err := newMarshalFunc(mc.format)
33 | if err != nil {
34 | return err
35 | }
36 |
37 | bundle := bundle.New()
38 | for _, tf := range mc.translationFiles {
39 | if err := bundle.LoadTranslationFile(tf); err != nil {
40 | return fmt.Errorf("failed to load translation file %s because %s\n", tf, err)
41 | }
42 | }
43 |
44 | translations := bundle.Translations()
45 | sourceTranslations := translations[mc.sourceLocaleID]
46 | for translationID, src := range sourceTranslations {
47 | for _, localeTranslations := range translations {
48 | if dst := localeTranslations[translationID]; dst == nil || reflect.TypeOf(src) != reflect.TypeOf(dst) {
49 | localeTranslations[translationID] = src.UntranslatedCopy()
50 | }
51 | }
52 | }
53 |
54 | for localeID, localeTranslations := range translations {
55 | locale := locale.MustNew(localeID)
56 | all := filter(localeTranslations, func(t translation.Translation) translation.Translation {
57 | return t.Normalize(locale.Language)
58 | })
59 | if err := mc.writeFile("all", all, localeID, marshal); err != nil {
60 | return err
61 | }
62 |
63 | untranslated := filter(localeTranslations, func(t translation.Translation) translation.Translation {
64 | if t.Incomplete(locale.Language) {
65 | return t.Normalize(locale.Language).Backfill(sourceTranslations[t.ID()])
66 | }
67 | return nil
68 | })
69 | if err := mc.writeFile("untranslated", untranslated, localeID, marshal); err != nil {
70 | return err
71 | }
72 | }
73 | return nil
74 | }
75 |
76 | type marshalFunc func(interface{}) ([]byte, error)
77 |
78 | func (mc *mergeCommand) writeFile(label string, translations []translation.Translation, localeID string, marshal marshalFunc) error {
79 | sort.Sort(translation.SortableByID(translations))
80 | buf, err := marshal(marshalInterface(translations))
81 | if err != nil {
82 | return fmt.Errorf("failed to marshal %s strings to %s because %s", localeID, mc.format, err)
83 | }
84 | filename := filepath.Join(mc.outdir, fmt.Sprintf("%s.%s.%s", localeID, label, mc.format))
85 | if err := ioutil.WriteFile(filename, buf, 0666); err != nil {
86 | return fmt.Errorf("failed to write %s because %s", filename, err)
87 | }
88 | return nil
89 | }
90 |
91 | func filter(translations map[string]translation.Translation, filter func(translation.Translation) translation.Translation) []translation.Translation {
92 | filtered := make([]translation.Translation, 0, len(translations))
93 | for _, translation := range translations {
94 | if t := filter(translation); t != nil {
95 | filtered = append(filtered, t)
96 | }
97 | }
98 | return filtered
99 |
100 | }
101 |
102 | func newMarshalFunc(format string) (marshalFunc, error) {
103 | switch format {
104 | case "json":
105 | return func(v interface{}) ([]byte, error) {
106 | return json.MarshalIndent(v, "", " ")
107 | }, nil
108 | /*
109 | case "yaml":
110 | return func(v interface{}) ([]byte, error) {
111 | return goyaml.Marshal(v)
112 | }, nil
113 | */
114 | }
115 | return nil, fmt.Errorf("unsupported format: %s\n", format)
116 | }
117 |
118 | func marshalInterface(translations []translation.Translation) []interface{} {
119 | mi := make([]interface{}, len(translations))
120 | for i, translation := range translations {
121 | mi[i] = translation.MarshalInterface()
122 | }
123 | return mi
124 | }
125 |
--------------------------------------------------------------------------------
/static/css/main.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /* CSS Document */
3 |
4 | body {
5 | background: rgba(232,234,242,1);
6 | font-family: 'Rambla', sans-serif;
7 | }
8 | a {
9 | color: #00aeff;
10 | }
11 | a, a:hover, a:active {
12 | outline: 0 none !important;
13 | }
14 | h2 {
15 | margin-top: 1px;
16 | }
17 | input[type="button"] {
18 | font-size: 16px;
19 | }
20 | .bootstrap-select:not([class*="span"]):not([class*="col-"]):not([class*="form-control"]) {
21 | width: 240px;
22 | }
23 | .navbar {
24 | border-radius: 0px;
25 | border: none;
26 | }
27 | .navbar-inverse {
28 | background: rgba(52,50,61,0.95);
29 | border-color: #080808;
30 | }
31 | .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus {
32 | background-color: rgba(0,0,0,0.5);
33 | color: #FFFFFF;
34 | }
35 | .navbar-inverse .navbar-brand {
36 | color: #FFFFFF;
37 | }
38 | hr {
39 | border-color: rgba(52, 50, 61, 0.3);
40 | box-shadow: 0px 1px 0px rgba(255,255,255,1);
41 | margin-top: 1px;
42 | margin-bottom: 10px;
43 | }
44 | .alert {
45 | font-size: 18px;
46 | }
47 | /*-- Scrolling Background --*/
48 | #top {
49 | background: url(../img/posters.jpg);
50 | box-shadow: 0px 0px 180px rgba(0,0,0,1) inset;
51 | }
52 |
53 | /*-- Overide for Responsive Styles --*/
54 | .row {
55 | margin-left: 0px !important;
56 | margin-right: 0px !important;
57 | }
58 |
59 | /*-- Code Section Styles --*/
60 | #code-section {
61 | background: rgba(255,255,255,0.5);
62 | border-bottom: 1px solid rgba(68,67,80,0.2);
63 | box-shadow: 0px 1px 0px rgba(255,255,255,1.0);
64 | padding: 0 0 30px;
65 | }
66 | #code-section textarea {
67 | width: 90%;
68 | min-height: 600px;
69 | font-size: 20px;
70 | float: left;
71 | white-space: pre;
72 | word-wrap: normal;
73 | overflow-x: scroll;
74 | }
75 | #code-section {
76 | width: 90%;
77 | font-size: 20px;
78 | float: left;
79 | }
80 |
81 | #code-section {
82 | margin: 10px;
83 | }
84 |
85 | /*-- Input Section Styles --*/
86 | #input-section {
87 | }
88 | #input-section textarea {
89 | min-height: 500px;
90 | }
91 | textarea.pasted-textarea {
92 | background: none repeat scroll 0 0 #FAFBFF;
93 | font-size: 20px;
94 | }
95 | textarea.result-textarea {
96 | color: #090;
97 | font-size: 18px;
98 | }
99 | textarea.form-control {
100 | font-size: 18px;
101 | }
102 | /*-- Tab Section Styles --*/
103 | p.nav-text {
104 | border-left: 1px solid rgba(255, 255, 255, 0.2);
105 | box-shadow: -1px 0 0 rgba(0, 0, 0, 0.5);
106 | color: rgba(255, 255, 255, 0.8);
107 | float: left;
108 | font-size: 15px;
109 | margin: 0;
110 | padding: 15px 0 15px 15px;
111 | }
112 | .tab-pane {
113 | background: #FFFFFF;
114 | border-right: 1px solid #DDDDDD;
115 | border-bottom: 1px solid #DDDDDD;
116 | border-left: 1px solid #DDDDDD;
117 | border-radius: 0px 0px 4px 4px;
118 | padding: 15px 0;
119 |
120 | }
121 | .tab-row {
122 | padding: 0 5px;
123 | }
124 | .tab-content {
125 | padding: 0px 20px 0px 20px;
126 | }
127 | .button-row {
128 | padding: 10px 0px 0px 0px;
129 | }
130 | .nav > li > a:hover, .nav > li > a:focus {
131 | background-color: rgba(255,255,255,0.5);
132 | text-decoration: none;
133 | }
134 | .nav-tabs > li > a {
135 | font-size: 18px;
136 | text-shadow: 0px 1px 0px #FFFFFF;
137 | }
138 | .nav > li > a {
139 | padding: 15px 20px;
140 | }
141 |
142 | /*-- Overide for select --*/
143 | .bootstrap-select.btn-group .btn .filter-option {
144 | font-size: 16px;
145 | }
146 |
147 | /*-- Modal --*/
148 | .modal-button {
149 | margin-left: 10px;
150 | font-size: 16px;
151 | padding: 5px 12px;
152 | }
153 |
154 | .modal-content {
155 | width: 800px;
156 | }
157 |
158 | .modal-dialog {
159 | width: 800px;
160 | }
161 |
162 | .modal-body {
163 | background-color: #E8EAF2;
164 | padding: 10px 0px 15px 0px;
165 | }
166 |
167 | .modal-row {
168 | padding: 0px 0px 0px 10px;
169 | }
170 |
171 | .modal-footer {
172 | padding: 19px 20px 20px;
173 | margin-top: 0px;
174 | text-align: right;
175 | border-top: 0px solid #e5e5e5;
176 | }
177 |
178 | /*-- Lists --*/
179 | .list-group-item {
180 | font-size: 17px;
181 | }
182 | .list-group-item.name {
183 | background: none repeat scroll 0 0 #FAFBFF;
184 | font-size: 20px;
185 | font-weight: bold;
186 | }
187 | .list-group-item b {
188 | margin: 0px 5px 0px 0px;
189 | }
190 |
191 | /*-- Buoy --*/
192 | #load-station-button, #load-station-button-json {
193 | margin: 0 0 10px !important;
194 | }
--------------------------------------------------------------------------------
/localize/messages.go:
--------------------------------------------------------------------------------
1 | // Package localize : messages.go package provides support for handling different languages and cultures
2 | package localize
3 |
4 | import (
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "os"
9 | "path"
10 |
11 | "github.com/goinggo/beego-mgo/go-i18n/i18n"
12 | "github.com/goinggo/beego-mgo/go-i18n/i18n/locale"
13 | "github.com/goinggo/beego-mgo/go-i18n/i18n/translation"
14 | "github.com/goinggo/tracelog"
15 | )
16 |
17 | var (
18 | // T is the translate function for the default locale
19 | T i18n.TranslateFunc
20 | )
21 |
22 | // Init initializes the local environment
23 | func Init(defaultLocale string) error {
24 | tracelog.Startedf("localize", "Init", "defaultLocal[%s]", defaultLocale)
25 |
26 | switch defaultLocale {
27 | case "en-US":
28 | LoadJSON(defaultLocale, EnUS)
29 | default:
30 | return fmt.Errorf("Unsupported Locale: %s", defaultLocale)
31 | }
32 |
33 | // Obtain the default translation function for use
34 | var err error
35 | if T, err = NewTranslation(defaultLocale, defaultLocale); err != nil {
36 | return err
37 | }
38 |
39 | tracelog.Completed("localize", "Init")
40 | return nil
41 | }
42 |
43 | // NewTranslation obtains a translation function object for the
44 | // specified locales
45 | func NewTranslation(userLocale string, defaultLocale string) (i18n.TranslateFunc, error) {
46 | return i18n.Tfunc(userLocale, userLocale)
47 | }
48 |
49 | // LoadJSON takes a json document of translations and manually
50 | // loads them into the system
51 | func LoadJSON(userLocale string, translationDocument string) error {
52 | tracelog.Startedf("localize", "LoadJSON", "userLocale[%s] length[%d]", userLocale, len(translationDocument))
53 |
54 | var tranDocuments []map[string]interface{}
55 | if err := json.Unmarshal([]byte(translationDocument), &tranDocuments); err != nil {
56 | tracelog.CompletedErrorf(err, "localize", "LoadJSON", "**************>")
57 | return err
58 | }
59 |
60 | for _, tranDocument := range tranDocuments {
61 | tran, err := translation.NewTranslation(tranDocument)
62 | if err != nil {
63 | tracelog.CompletedError(err, "localize", "LoadJSON")
64 | return err
65 | }
66 |
67 | i18n.AddTranslation(locale.MustNew(userLocale), tran)
68 | }
69 |
70 | tracelog.Completed("localize", "LoadJSON")
71 | return nil
72 | }
73 |
74 | // LoadFiles looks for i18n folders inside the current directory and the GOPATH
75 | // to find translation files to load
76 | func LoadFiles(userLocale string, defaultLocal string) error {
77 | gopath := os.Getenv("GOPATH")
78 | pwd, err := os.Getwd()
79 | if err != nil {
80 | tracelog.CompletedError(err, "localize", "LoadFiles")
81 | return err
82 | }
83 |
84 | tracelog.Info("localize", "LoadFiles", "PWD[%s] GOPATH[%s]", pwd, gopath)
85 |
86 | // Load any translation files we can find
87 | searchDirectory(pwd, pwd)
88 | if gopath != "" {
89 | searchDirectory(gopath, pwd)
90 | }
91 |
92 | // Create a translation function for use
93 | T, err = i18n.Tfunc(userLocale, defaultLocal)
94 | if err != nil {
95 | return err
96 | }
97 |
98 | return nil
99 | }
100 |
101 | // searchDirectory recurses through the specified directory looking
102 | // for i18n folders. If found it will load the translations files
103 | func searchDirectory(directory string, pwd string) {
104 | // Read the directory
105 | fileInfos, err := ioutil.ReadDir(directory)
106 | if err != nil {
107 | tracelog.CompletedError(err, "localize", "searchDirectory")
108 | return
109 | }
110 |
111 | // Look for i18n folders
112 | for _, fileInfo := range fileInfos {
113 | if fileInfo.IsDir() == true {
114 | fullPath := fmt.Sprintf("%s/%s", directory, fileInfo.Name())
115 |
116 | // If this directory is the current directory, ignore it
117 | if fullPath == pwd {
118 | continue
119 | }
120 |
121 | // Is this an i18n folder
122 | if fileInfo.Name() == "i18n" {
123 | loadTranslationFiles(fullPath)
124 | continue
125 | }
126 |
127 | // Look for more sub-directories
128 | searchDirectory(fullPath, pwd)
129 | continue
130 | }
131 | }
132 | }
133 |
134 | // loadTranslationFiles loads the found translation files into the i18n
135 | // messaging system for use by the application
136 | func loadTranslationFiles(directory string) {
137 | // Read the directory
138 | fileInfos, err := ioutil.ReadDir(directory)
139 | if err != nil {
140 | tracelog.CompletedError(err, "localize", "loadTranslationFiles")
141 | return
142 | }
143 |
144 | // Look for JSON files
145 | for _, fileInfo := range fileInfos {
146 | if path.Ext(fileInfo.Name()) != ".json" {
147 | continue
148 | }
149 |
150 | fileName := fmt.Sprintf("%s/%s", directory, fileInfo.Name())
151 |
152 | tracelog.Info("localize", "loadTranslationFiles", "Loading %s", fileName)
153 | i18n.MustLoadTranslationFile(fileName)
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/go-i18n/i18n/bundle/bundle.go:
--------------------------------------------------------------------------------
1 | // Package bundle manages translations for multiple locales.
2 | package bundle
3 |
4 | import (
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | // "launchpad.net/goyaml"
9 | "github.com/goinggo/beego-mgo/go-i18n/i18n/locale"
10 | "github.com/goinggo/beego-mgo/go-i18n/i18n/translation"
11 | "path/filepath"
12 | )
13 |
14 | // TranslateFunc is a copy of i18n.TranslateFunc to avoid a circular dependency.
15 | type TranslateFunc func(translationID string, args ...interface{}) string
16 |
17 | type Bundle struct {
18 | translations map[string]map[string]translation.Translation
19 | }
20 |
21 | func New() *Bundle {
22 | return &Bundle{
23 | translations: make(map[string]map[string]translation.Translation),
24 | }
25 | }
26 |
27 | func (b *Bundle) MustLoadTranslationFile(filename string) {
28 | if err := b.LoadTranslationFile(filename); err != nil {
29 | panic(err)
30 | }
31 | }
32 |
33 | func (b *Bundle) LoadTranslationFile(filename string) error {
34 | locale, err := locale.New(filename)
35 | if err != nil {
36 | return err
37 | }
38 |
39 | translations, err := parseTranslationFile(filename)
40 | if err != nil {
41 | return err
42 | }
43 |
44 | b.AddTranslation(locale, translations...)
45 | return nil
46 | }
47 |
48 | func parseTranslationFile(filename string) ([]translation.Translation, error) {
49 | var unmarshalFunc func([]byte, interface{}) error
50 | switch format := filepath.Ext(filename); format {
51 | case ".json":
52 | unmarshalFunc = json.Unmarshal
53 | /*
54 | case ".yaml":
55 | unmarshalFunc = goyaml.Unmarshal
56 | */
57 | default:
58 | return nil, fmt.Errorf("unsupported file extension %s", format)
59 | }
60 | fileBytes, err := ioutil.ReadFile(filename)
61 | if err != nil {
62 | return nil, err
63 | }
64 |
65 | var translationsData []map[string]interface{}
66 | if len(fileBytes) > 0 {
67 | if err := unmarshalFunc(fileBytes, &translationsData); err != nil {
68 | return nil, err
69 | }
70 | }
71 |
72 | translations := make([]translation.Translation, 0, len(translationsData))
73 | for i, translationData := range translationsData {
74 | t, err := translation.NewTranslation(translationData)
75 | if err != nil {
76 | return nil, fmt.Errorf("unable to parse translation #%d in %s because %s\n%v", i, filename, err, translationData)
77 | }
78 | translations = append(translations, t)
79 | }
80 | return translations, nil
81 | }
82 |
83 | func (b *Bundle) AddTranslation(locale *locale.Locale, translations ...translation.Translation) {
84 | if b.translations[locale.ID] == nil {
85 | b.translations[locale.ID] = make(map[string]translation.Translation, len(translations))
86 | }
87 | currentTranslations := b.translations[locale.ID]
88 | for _, newTranslation := range translations {
89 | if currentTranslation := currentTranslations[newTranslation.ID()]; currentTranslation != nil {
90 | currentTranslations[newTranslation.ID()] = currentTranslation.Merge(newTranslation)
91 | } else {
92 | currentTranslations[newTranslation.ID()] = newTranslation
93 | }
94 | }
95 | }
96 |
97 | func (b *Bundle) Translations() map[string]map[string]translation.Translation {
98 | return b.translations
99 | }
100 |
101 | func (b *Bundle) MustTfunc(localeID string, localeIDs ...string) TranslateFunc {
102 | tf, err := b.Tfunc(localeID, localeIDs...)
103 | if err != nil {
104 | panic(err)
105 | }
106 | return tf
107 | }
108 |
109 | func (b *Bundle) Tfunc(localeID string, localeIDs ...string) (tf TranslateFunc, err error) {
110 | var l *locale.Locale
111 | l, err = locale.New(localeID)
112 | if err != nil {
113 | for _, localeID := range localeIDs {
114 | l, err = locale.New(localeID)
115 | if err == nil {
116 | break
117 | }
118 | }
119 | }
120 | return func(translationID string, args ...interface{}) string {
121 | return b.translate(l, translationID, args...)
122 | }, err
123 | }
124 |
125 | func (b *Bundle) translate(locale *locale.Locale, translationID string, args ...interface{}) string {
126 | if locale == nil {
127 | return translationID
128 | }
129 |
130 | translations := b.translations[locale.ID]
131 | if translations == nil {
132 | return translationID
133 | }
134 |
135 | translation := translations[translationID]
136 | if translation == nil {
137 | return translationID
138 | }
139 |
140 | var count interface{}
141 | if len(args) > 0 && isNumber(args[0]) {
142 | count = args[0]
143 | args = args[1:]
144 | }
145 |
146 | pluralCategory, _ := locale.Language.PluralCategory(count)
147 | template := translation.Template(pluralCategory)
148 | if template == nil {
149 | return translationID
150 | }
151 |
152 | var data map[string]interface{}
153 | if len(args) > 0 {
154 | data, _ = args[0].(map[string]interface{})
155 | }
156 |
157 | if isNumber(count) {
158 | if data == nil {
159 | data = map[string]interface{}{"Count": count}
160 | } else {
161 | data["Count"] = count
162 | }
163 | }
164 |
165 | s := template.Execute(data)
166 | if s == "" {
167 | return translationID
168 | }
169 | return s
170 | }
171 |
172 | func isNumber(n interface{}) bool {
173 | switch n.(type) {
174 | case int, int8, int16, int32, int64, string:
175 | return true
176 | }
177 | return false
178 | }
179 |
--------------------------------------------------------------------------------
/go-i18n/i18n/i18n.go:
--------------------------------------------------------------------------------
1 | // Package i18n supports string translations with variable substitution and CLDR pluralization.
2 | // It is intended to be used in conjunction with github.com/finapps/go-i18n/goi18n,
3 | // although that is not strictly required.
4 | //
5 | // Initialization
6 | //
7 | // Your Go program should load translations during its intialization.
8 | // i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
9 | // If your translations are in a file format not supported by (Must)?LoadTranslationFile,
10 | // then you can use the AddTranslation function to manually add translations.
11 | //
12 | // Fetching a translation
13 | //
14 | // Use Tfunc or MustTfunc to fetch a TranslateFunc that will return the translated string for a specific locale.
15 | // The TranslateFunc will be bound to the first valid locale passed to Tfunc.
16 | // userLocale = "ar-AR" // user preference, accept header, language cookie
17 | // defaultLocale = "en-US" // known valid locale
18 | // T, err := i18n.Tfunc(userLocale, defaultLocale)
19 | // fmt.Println(T("Hello world"))
20 | //
21 | // Usually it is a good idea to identify strings by a generic id rather than the English translation,
22 | // but the rest of this documentation will continue to use the English translation for readability.
23 | // T("program_greeting")
24 | //
25 | // Variables
26 | //
27 | // TranslateFunc supports strings that have variables using the text/template syntax.
28 | // T("Hello {{.Person}}", map[string]interface{}{
29 | // "Person": "Bob",
30 | // })
31 | //
32 | // Pluralization
33 | //
34 | // TranslateFunc supports the pluralization of strings using the CLDR pluralization rules defined here:
35 | // http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
36 | // T("You have {{.Count}} unread emails.", 2)
37 | // T("I am {{.Count}} meters tall.", "1.7")
38 | //
39 | // Plural strings may also have variables.
40 | // T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
41 | // "Person": "Bob",
42 | // })
43 | //
44 | // Compound plural strings can be created with nesting.
45 | // T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
46 | // "Person": "Bob",
47 | // "Timeframe": T("{{.Count}} days", 2),
48 | // })
49 | //
50 | // Templates
51 | //
52 | // You can use the .Funcs() method of a text/template or html/template to register a TranslateFunc
53 | // for usage inside of that template.
54 | package i18n
55 |
56 | import (
57 | "github.com/goinggo/beego-mgo/go-i18n/i18n/bundle"
58 | "github.com/goinggo/beego-mgo/go-i18n/i18n/locale"
59 | "github.com/goinggo/beego-mgo/go-i18n/i18n/translation"
60 | )
61 |
62 | // TranslateFunc returns the translation of the string identified by translationID.
63 | //
64 | // If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
65 | // that contains template data.
66 | //
67 | // If translationID is a plural form, then the first variadic argument must be an integer type
68 | // (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
69 | // The second variadic argument may be a map[string]interface{} that contains template data.
70 | type TranslateFunc func(translationID string, args ...interface{}) string
71 |
72 | // IdentityTfunc returns a TranslateFunc that always returns the translationID passed to it.
73 | //
74 | // It is a useful placeholder when parsing a text/template or html/template
75 | // before the actual Tfunc is available.
76 | func IdentityTfunc() TranslateFunc {
77 | return func(translationID string, args ...interface{}) string {
78 | return translationID
79 | }
80 | }
81 |
82 | var defaultBundle = bundle.New()
83 |
84 | // MustLoadTranslationFile is similar to LoadTranslationFile
85 | // except it panics if an error happens.
86 | func MustLoadTranslationFile(filename string) {
87 | defaultBundle.MustLoadTranslationFile(filename)
88 | }
89 |
90 | // LoadTranslationFile loads the translations from filename into memory.
91 | //
92 | // The locale that the translations are associated with is parsed from the filename.
93 | //
94 | // Generally you should load translation files once during your program's initialization.
95 | func LoadTranslationFile(filename string) error {
96 | return defaultBundle.LoadTranslationFile(filename)
97 | }
98 |
99 | // AddTranslation adds translations for a locale.
100 | //
101 | // It is useful if your translations are in a format not supported by LoadTranslationFile.
102 | func AddTranslation(locale *locale.Locale, translations ...translation.Translation) {
103 | defaultBundle.AddTranslation(locale, translations...)
104 | }
105 |
106 | // MustTfunc is similar to Tfunc except it panics if an error happens.
107 | func MustTfunc(localeID string, localeIDs ...string) TranslateFunc {
108 | return TranslateFunc(defaultBundle.MustTfunc(localeID, localeIDs...))
109 | }
110 |
111 | // Tfunc returns a TranslateFunc that will be bound to the first valid locale from its parameters.
112 | func Tfunc(localeID string, localeIDs ...string) (TranslateFunc, error) {
113 | tf, err := defaultBundle.Tfunc(localeID, localeIDs...)
114 | return TranslateFunc(tf), err
115 | }
116 |
--------------------------------------------------------------------------------
/controllers/baseController/baseController.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of baseController source code is governed by a BSD-style
3 | // license that can be found in the LICENSE handle.
4 |
5 | // Package baseController implements boilerplate code for all baseControllers.
6 | package baseController
7 |
8 | import (
9 | "reflect"
10 | "runtime"
11 |
12 | "fmt"
13 |
14 | "github.com/astaxie/beego"
15 | "github.com/astaxie/beego/validation"
16 | "github.com/goinggo/beego-mgo/localize"
17 | "github.com/goinggo/beego-mgo/services"
18 | "github.com/goinggo/beego-mgo/utilities/mongo"
19 | log "github.com/goinggo/tracelog"
20 | )
21 |
22 | //** TYPES
23 |
24 | type (
25 | // BaseController composes all required types and behavior.
26 | BaseController struct {
27 | beego.Controller
28 | services.Service
29 | }
30 | )
31 |
32 | //** INTERCEPT FUNCTIONS
33 |
34 | // Prepare is called prior to the baseController method.
35 | func (baseController *BaseController) Prepare() {
36 | baseController.UserID = baseController.GetString("userID")
37 | if baseController.UserID == "" {
38 | baseController.UserID = baseController.GetString(":userID")
39 | }
40 | if baseController.UserID == "" {
41 | baseController.UserID = "Unknown"
42 | }
43 |
44 | if err := baseController.Service.Prepare(); err != nil {
45 | log.Errorf(err, baseController.UserID, "BaseController.Prepare", baseController.Ctx.Request.URL.Path)
46 | baseController.ServeError(err)
47 | return
48 | }
49 |
50 | log.Trace(baseController.UserID, "BaseController.Prepare", "UserID[%s] Path[%s]", baseController.UserID, baseController.Ctx.Request.URL.Path)
51 | }
52 |
53 | // Finish is called once the baseController method completes.
54 | func (baseController *BaseController) Finish() {
55 | defer func() {
56 | if baseController.MongoSession != nil {
57 | mongo.CloseSession(baseController.UserID, baseController.MongoSession)
58 | baseController.MongoSession = nil
59 | }
60 | }()
61 |
62 | log.Completedf(baseController.UserID, "Finish", baseController.Ctx.Request.URL.Path)
63 | }
64 |
65 | //** VALIDATION
66 |
67 | // ParseAndValidate will run the params through the validation framework and then
68 | // response with the specified localized or provided message.
69 | func (baseController *BaseController) ParseAndValidate(params interface{}) bool {
70 | // This is not working anymore :(
71 | if err := baseController.ParseForm(params); err != nil {
72 | baseController.ServeError(err)
73 | return false
74 | }
75 |
76 | var valid validation.Validation
77 | ok, err := valid.Valid(params)
78 | if err != nil {
79 | baseController.ServeError(err)
80 | return false
81 | }
82 |
83 | if ok == false {
84 | // Build a map of the Error messages for each field
85 | messages2 := make(map[string]string)
86 |
87 | val := reflect.ValueOf(params).Elem()
88 | for i := 0; i < val.NumField(); i++ {
89 | // Look for an Error tag in the field
90 | typeField := val.Type().Field(i)
91 | tag := typeField.Tag
92 | tagValue := tag.Get("Error")
93 |
94 | // Was there an Error tag
95 | if tagValue != "" {
96 | messages2[typeField.Name] = tagValue
97 | }
98 | }
99 |
100 | // Build the Error response
101 | var errors []string
102 | for _, err := range valid.Errors {
103 | // Match an Error from the validation framework Errors
104 | // to a field name we have a mapping for
105 | message, ok := messages2[err.Field]
106 | if ok == true {
107 | // Use a localized message if one exists
108 | errors = append(errors, localize.T(message))
109 | continue
110 | }
111 |
112 | // No match, so use the message as is
113 | errors = append(errors, err.Message)
114 | }
115 |
116 | baseController.ServeValidationErrors(errors)
117 | return false
118 | }
119 |
120 | return true
121 | }
122 |
123 | //** EXCEPTIONS
124 |
125 | // ServeError prepares and serves an Error exception.
126 | func (baseController *BaseController) ServeError(err error) {
127 | baseController.Data["json"] = struct {
128 | Error string `json:"Error"`
129 | }{err.Error()}
130 | baseController.Ctx.Output.SetStatus(500)
131 | baseController.ServeJson()
132 | }
133 |
134 | // ServeValidationErrors prepares and serves a validation exception.
135 | func (baseController *BaseController) ServeValidationErrors(Errors []string) {
136 | baseController.Data["json"] = struct {
137 | Errors []string `json:"Errors"`
138 | }{Errors}
139 | baseController.Ctx.Output.SetStatus(409)
140 | baseController.ServeJson()
141 | }
142 |
143 | //** CATCHING PANICS
144 |
145 | // CatchPanic is used to catch any Panic and log exceptions. Returns a 500 as the response.
146 | func (baseController *BaseController) CatchPanic(functionName string) {
147 | if r := recover(); r != nil {
148 | buf := make([]byte, 10000)
149 | runtime.Stack(buf, false)
150 |
151 | log.Warning(baseController.Service.UserID, functionName, "PANIC Defered [%v] : Stack Trace : %v", r, string(buf))
152 |
153 | baseController.ServeError(fmt.Errorf("%v", r))
154 | }
155 | }
156 |
157 | //** AJAX SUPPORT
158 |
159 | // AjaxResponse returns a standard ajax response.
160 | func (baseController *BaseController) AjaxResponse(resultCode int, resultString string, data interface{}) {
161 | response := struct {
162 | Result int
163 | ResultString string
164 | ResultObject interface{}
165 | }{
166 | Result: resultCode,
167 | ResultString: resultString,
168 | ResultObject: data,
169 | }
170 |
171 | baseController.Data["json"] = response
172 | baseController.ServeJson()
173 | }
174 |
--------------------------------------------------------------------------------
/go-i18n/i18n/language/language.go:
--------------------------------------------------------------------------------
1 | // Package language defines languages that implement CLDR pluralization.
2 | package language
3 |
4 | import (
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
6 | )
7 |
8 | // Language is a written human language.
9 | //
10 | // Languages are identified by tags defined by RFC 5646.
11 | //
12 | // Typically language tags are a 2 character language code (ISO 639-1)
13 | // optionally followed by a dash and a 2 character country code (ISO 3166-1).
14 | // (e.g. en, pt-BR)
15 | //
16 | // A Language implements CLDR plural rules as defined here:
17 | // http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
18 | // http://unicode.org/reports/tr35/tr35-numbers.html#Operands
19 | type Language struct {
20 | ID string
21 | Name string
22 | PluralCategories map[plural.Category]struct{}
23 | PluralFunc func(*plural.Operands) plural.Category
24 | }
25 |
26 | // Alphabetical by English name.
27 | var languages = map[string]*Language{
28 | // Arabic
29 | "ar": &Language{
30 | ID: "ar",
31 | PluralCategories: newSet(plural.Zero, plural.One, plural.Two, plural.Few, plural.Many, plural.Other),
32 | PluralFunc: func(ops *plural.Operands) plural.Category {
33 | if ops.W == 0 {
34 | // Integer case
35 | switch ops.I {
36 | case 0:
37 | return plural.Zero
38 | case 1:
39 | return plural.One
40 | case 2:
41 | return plural.Two
42 | default:
43 | mod100 := ops.I % 100
44 | if mod100 >= 3 && mod100 <= 10 {
45 | return plural.Few
46 | }
47 | if mod100 >= 11 {
48 | return plural.Many
49 | }
50 | }
51 | }
52 | return plural.Other
53 | },
54 | },
55 |
56 | // Catalan
57 | "ca": &Language{
58 | ID: "ca",
59 | PluralCategories: newSet(plural.One, plural.Other),
60 | PluralFunc: func(ops *plural.Operands) plural.Category {
61 | if ops.I == 1 && ops.V == 0 {
62 | return plural.One
63 | }
64 | return plural.Other
65 | },
66 | },
67 |
68 | // Chinese
69 | // There is no need to distinguish between simplified and traditional
70 | // since they have the same pluralization.
71 | "zh": &Language{
72 | ID: "zh",
73 | PluralCategories: newSet(plural.Other),
74 | PluralFunc: func(ops *plural.Operands) plural.Category {
75 | return plural.Other
76 | },
77 | },
78 |
79 | // Czech
80 | "cs": &Language{
81 | ID: "cs",
82 | PluralCategories: newSet(plural.One, plural.Few, plural.Many, plural.Other),
83 | PluralFunc: func(ops *plural.Operands) plural.Category {
84 | if ops.I == 1 && ops.V == 0 {
85 | return plural.One
86 | }
87 | if ops.I >= 2 && ops.I <= 4 && ops.V == 0 {
88 | return plural.Few
89 | }
90 | if ops.V > 0 {
91 | return plural.Many
92 | }
93 | return plural.Other
94 | },
95 | },
96 |
97 | // Danish
98 | "da": &Language{
99 | ID: "da",
100 | PluralCategories: newSet(plural.One, plural.Other),
101 | PluralFunc: func(ops *plural.Operands) plural.Category {
102 | if ops.I == 1 || (ops.I == 0 && ops.T != 0) {
103 | return plural.One
104 | }
105 | return plural.Other
106 | },
107 | },
108 |
109 | // Dutch
110 | "nl": &Language{
111 | ID: "nl",
112 | PluralCategories: newSet(plural.One, plural.Other),
113 | PluralFunc: func(ops *plural.Operands) plural.Category {
114 | if ops.I == 1 && ops.V == 0 {
115 | return plural.One
116 | }
117 | return plural.Other
118 | },
119 | },
120 |
121 | // English
122 | "en": &Language{
123 | ID: "en",
124 | PluralCategories: newSet(plural.One, plural.Other),
125 | PluralFunc: func(ops *plural.Operands) plural.Category {
126 | if ops.I == 1 && ops.V == 0 {
127 | return plural.One
128 | }
129 | return plural.Other
130 | },
131 | },
132 |
133 | // French
134 | "fr": &Language{
135 | ID: "fr",
136 | PluralCategories: newSet(plural.One, plural.Other),
137 | PluralFunc: func(ops *plural.Operands) plural.Category {
138 | if ops.I == 0 || ops.I == 1 {
139 | return plural.One
140 | }
141 | return plural.Other
142 | },
143 | },
144 |
145 | // German
146 | "de": &Language{
147 | ID: "de",
148 | PluralCategories: newSet(plural.One, plural.Other),
149 | PluralFunc: func(ops *plural.Operands) plural.Category {
150 | if ops.I == 1 && ops.V == 0 {
151 | return plural.One
152 | }
153 | return plural.Other
154 | },
155 | },
156 |
157 | // Italian
158 | "it": &Language{
159 | ID: "it",
160 | PluralCategories: newSet(plural.One, plural.Other),
161 | PluralFunc: func(ops *plural.Operands) plural.Category {
162 | if ops.I == 1 && ops.V == 0 {
163 | return plural.One
164 | }
165 | return plural.Other
166 | },
167 | },
168 |
169 | // Japanese
170 | "ja": &Language{
171 | ID: "ja",
172 | PluralCategories: newSet(plural.Other),
173 | PluralFunc: func(ops *plural.Operands) plural.Category {
174 | return plural.Other
175 | },
176 | },
177 |
178 | // Portuguese (European)
179 | "pt": &Language{
180 | ID: "pt",
181 | PluralCategories: newSet(plural.One, plural.Other),
182 | PluralFunc: func(ops *plural.Operands) plural.Category {
183 | if ops.I == 1 && ops.V == 0 {
184 | return plural.One
185 | }
186 | return plural.Other
187 | },
188 | },
189 |
190 | // Portuguese (Brazilian)
191 | "pt-BR": &Language{
192 | ID: "pt-BR",
193 | PluralCategories: newSet(plural.One, plural.Other),
194 | PluralFunc: func(ops *plural.Operands) plural.Category {
195 | if (ops.I == 1 && ops.V == 0) || (ops.I == 0 && ops.T == 1) {
196 | return plural.One
197 | }
198 | return plural.Other
199 | },
200 | },
201 |
202 | // Spanish
203 | "es": &Language{
204 | ID: "es",
205 | PluralCategories: newSet(plural.One, plural.Other),
206 | PluralFunc: func(ops *plural.Operands) plural.Category {
207 | if ops.I == 1 && ops.W == 0 {
208 | return plural.One
209 | }
210 | return plural.Other
211 | },
212 | },
213 | }
214 |
215 | // LanguageWithID returns the language identified by id
216 | // or nil if the language is not registered.
217 | func LanguageWithID(id string) *Language {
218 | return languages[id]
219 | }
220 |
221 | // Register adds Language l to the collection of available languages.
222 | func Register(l *Language) {
223 | languages[l.ID] = l
224 | }
225 |
226 | // PluralCategory returns the plural category for number as defined by
227 | // the language's CLDR plural rules.
228 | func (l *Language) PluralCategory(number interface{}) (plural.Category, error) {
229 | ops, err := plural.NewOperands(number)
230 | if err != nil {
231 | return plural.Invalid, err
232 | }
233 | return l.PluralFunc(ops), nil
234 | }
235 |
236 | func (l *Language) String() string {
237 | return l.ID
238 | }
239 |
240 | func newSet(pluralCategories ...plural.Category) map[plural.Category]struct{} {
241 | set := make(map[plural.Category]struct{}, len(pluralCategories))
242 | for _, pc := range pluralCategories {
243 | set[pc] = struct{}{}
244 | }
245 | return set
246 | }
247 |
--------------------------------------------------------------------------------
/static/css/bootstrap-select.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-select v1.4.3
3 | * http://silviomoreto.github.io/bootstrap-select/
4 | *
5 | * Copyright 2013 bootstrap-select
6 | * Licensed under the MIT license
7 | */
8 |
9 | .bootstrap-select.btn-group,
10 | .bootstrap-select.btn-group[class*="span"] {
11 | float: none;
12 | display: inline-block;
13 | margin-bottom: 10px;
14 | margin-left: 0;
15 | }
16 | .form-search .bootstrap-select.btn-group,
17 | .form-inline .bootstrap-select.btn-group,
18 | .form-horizontal .bootstrap-select.btn-group {
19 | margin-bottom: 0;
20 | }
21 |
22 | .bootstrap-select.form-control {
23 | margin-bottom: 0;
24 | padding: 0;
25 | border: none;
26 | }
27 |
28 | .bootstrap-select.btn-group.pull-right,
29 | .bootstrap-select.btn-group[class*="span"].pull-right,
30 | .row-fluid .bootstrap-select.btn-group[class*="span"].pull-right {
31 | float: right;
32 | }
33 |
34 | .input-append .bootstrap-select.btn-group {
35 | margin-left: -1px;
36 | }
37 |
38 | .input-prepend .bootstrap-select.btn-group {
39 | margin-right: -1px;
40 | }
41 |
42 | .bootstrap-select:not([class*="span"]):not([class*="col-"]):not([class*="form-control"]) {
43 | width: 220px;
44 | }
45 |
46 | .bootstrap-select {
47 | /*width: 220px\9; IE8 and below*/
48 | width: 220px\0; /*IE9 and below*/
49 | }
50 |
51 | .bootstrap-select.form-control:not([class*="span"]) {
52 | width: 100%;
53 | }
54 |
55 | .bootstrap-select > .btn {
56 | width: 100%;
57 | }
58 |
59 | .error .bootstrap-select .btn {
60 | border: 1px solid #b94a48;
61 | }
62 |
63 |
64 | .dropdown-menu {
65 | z-index: 2000;
66 | }
67 |
68 | .bootstrap-select.show-menu-arrow.open > .btn {
69 | z-index: 2051;
70 | }
71 |
72 | .bootstrap-select .btn:focus {
73 | outline: thin dotted #333333 !important;
74 | outline: 5px auto -webkit-focus-ring-color !important;
75 | outline-offset: -2px;
76 | }
77 |
78 | .bootstrap-select.btn-group .btn .filter-option {
79 | overflow: hidden;
80 | position: absolute;
81 | left: 12px;
82 | right: 25px;
83 | text-align: left;
84 | }
85 |
86 | .bootstrap-select.btn-group .btn .caret {
87 | position: absolute;
88 | top: 50%;
89 | right: 12px;
90 | margin-top: -2px;
91 | vertical-align: middle;
92 | }
93 |
94 | .bootstrap-select.btn-group > .disabled,
95 | .bootstrap-select.btn-group .dropdown-menu li.disabled > a {
96 | cursor: not-allowed;
97 | }
98 |
99 | .bootstrap-select.btn-group > .disabled:focus {
100 | outline: none !important;
101 | }
102 |
103 | .bootstrap-select.btn-group[class*="span"] .btn {
104 | width: 100%;
105 | }
106 |
107 | .bootstrap-select.btn-group .dropdown-menu {
108 | min-width: 100%;
109 | -moz-box-sizing: border-box;
110 | -webkit-box-sizing: border-box;
111 | box-sizing: border-box;
112 | }
113 |
114 | .bootstrap-select.btn-group .dropdown-menu.inner {
115 | position: static;
116 | border: 0;
117 | padding: 0;
118 | margin: 0;
119 | -webkit-border-radius: 0;
120 | -moz-border-radius: 0;
121 | border-radius: 0;
122 | -webkit-box-shadow: none;
123 | -moz-box-shadow: none;
124 | box-shadow: none;
125 | }
126 |
127 | .bootstrap-select.btn-group .dropdown-menu dt {
128 | display: block;
129 | padding: 3px 20px;
130 | cursor: default;
131 | }
132 |
133 | .bootstrap-select.btn-group .div-contain {
134 | overflow: hidden;
135 | }
136 |
137 | .bootstrap-select.btn-group .dropdown-menu li {
138 | position: relative;
139 | }
140 |
141 | .bootstrap-select.btn-group .dropdown-menu li > a.opt {
142 | position: relative;
143 | padding-left: 35px;
144 | }
145 |
146 | .bootstrap-select.btn-group .dropdown-menu li > a {
147 | cursor: pointer;
148 | }
149 |
150 | .bootstrap-select.btn-group .dropdown-menu li > dt small {
151 | font-weight: normal;
152 | }
153 |
154 | .bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a i.check-mark {
155 | display: inline-block;
156 | position: absolute;
157 | right: 15px;
158 | margin-top: 2.5px;
159 | }
160 |
161 | .bootstrap-select.btn-group .dropdown-menu li a i.check-mark {
162 | display: none;
163 | }
164 |
165 | .bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text {
166 | margin-right: 34px;
167 | }
168 |
169 | .bootstrap-select.btn-group .dropdown-menu li small {
170 | padding-left: 0.5em;
171 | }
172 |
173 | .bootstrap-select.btn-group .dropdown-menu li:not(.disabled) > a:hover small,
174 | .bootstrap-select.btn-group .dropdown-menu li:not(.disabled) > a:focus small,
175 | .bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled) > a small {
176 | color: #64b1d8;
177 | color: rgba(255,255,255,0.4);
178 | }
179 |
180 | .bootstrap-select.btn-group .dropdown-menu li > dt small {
181 | font-weight: normal;
182 | }
183 |
184 | .bootstrap-select.show-menu-arrow .dropdown-toggle:before {
185 | content: '';
186 | display: inline-block;
187 | border-left: 7px solid transparent;
188 | border-right: 7px solid transparent;
189 | border-bottom: 7px solid #CCC;
190 | border-bottom-color: rgba(0, 0, 0, 0.2);
191 | position: absolute;
192 | bottom: -4px;
193 | left: 9px;
194 | display: none;
195 | }
196 |
197 | .bootstrap-select.show-menu-arrow .dropdown-toggle:after {
198 | content: '';
199 | display: inline-block;
200 | border-left: 6px solid transparent;
201 | border-right: 6px solid transparent;
202 | border-bottom: 6px solid white;
203 | position: absolute;
204 | bottom: -4px;
205 | left: 10px;
206 | display: none;
207 | }
208 |
209 | .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before {
210 | bottom: auto;
211 | top: -3px;
212 | border-top: 7px solid #ccc;
213 | border-bottom: 0;
214 | border-top-color: rgba(0, 0, 0, 0.2);
215 | }
216 |
217 | .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after {
218 | bottom: auto;
219 | top: -3px;
220 | border-top: 6px solid #ffffff;
221 | border-bottom: 0;
222 | }
223 |
224 | .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before {
225 | right: 12px;
226 | left: auto;
227 | }
228 | .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after {
229 | right: 13px;
230 | left: auto;
231 | }
232 |
233 | .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:before,
234 | .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:after {
235 | display: block;
236 | }
237 |
238 | .bootstrap-select.btn-group .no-results {
239 | padding: 3px;
240 | background: #f5f5f5;
241 | margin: 0 5px;
242 | }
243 |
244 | .mobile-device {
245 | position: absolute;
246 | top: 0;
247 | left: 0;
248 | display: block !important;
249 | width: 100%;
250 | height: 100% !important;
251 | opacity: 0;
252 | }
253 |
254 | .bootstrap-select.fit-width {
255 | width: auto !important;
256 | }
257 |
258 | .bootstrap-select.btn-group.fit-width .btn .filter-option {
259 | position: static;
260 | }
261 |
262 | .bootstrap-select.btn-group.fit-width .btn .caret {
263 | position: static;
264 | top: auto;
265 | margin-top: -1px;
266 | }
267 |
268 | .control-group.error .bootstrap-select .dropdown-toggle{
269 | border-color: #b94a48;
270 | }
271 |
272 | .bootstrap-select-searchbox {
273 | padding: 4px 8px;
274 | }
275 |
276 | .bootstrap-select-searchbox input {
277 | margin-bottom: 0;
278 | }
279 |
--------------------------------------------------------------------------------
/go-i18n/i18n/language/language_test.go:
--------------------------------------------------------------------------------
1 | package language
2 |
3 | import (
4 | "fmt"
5 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
6 | "testing"
7 | )
8 |
9 | type pluralTest struct {
10 | num interface{}
11 | pc plural.Category
12 | }
13 |
14 | func TestArabic(t *testing.T) {
15 | tests := []pluralTest{
16 | {0, plural.Zero},
17 | {"0", plural.Zero},
18 | {"0.0", plural.Zero},
19 | {"0.00", plural.Zero},
20 | {1, plural.One},
21 | {"1", plural.One},
22 | {"1.0", plural.One},
23 | {"1.00", plural.One},
24 | {2, plural.Two},
25 | {"2", plural.Two},
26 | {"2.0", plural.Two},
27 | {"2.00", plural.Two},
28 | {3, plural.Few},
29 | {"3", plural.Few},
30 | {"3.0", plural.Few},
31 | {"3.00", plural.Few},
32 | {10, plural.Few},
33 | {"10", plural.Few},
34 | {"10.0", plural.Few},
35 | {"10.00", plural.Few},
36 | {103, plural.Few},
37 | {"103", plural.Few},
38 | {"103.0", plural.Few},
39 | {"103.00", plural.Few},
40 | {110, plural.Few},
41 | {"110", plural.Few},
42 | {"110.0", plural.Few},
43 | {"110.00", plural.Few},
44 | {11, plural.Many},
45 | {"11", plural.Many},
46 | {"11.0", plural.Many},
47 | {"11.00", plural.Many},
48 | {99, plural.Many},
49 | {"99", plural.Many},
50 | {"99.0", plural.Many},
51 | {"99.00", plural.Many},
52 | {111, plural.Many},
53 | {"111", plural.Many},
54 | {"111.0", plural.Many},
55 | {"111.00", plural.Many},
56 | {199, plural.Many},
57 | {"199", plural.Many},
58 | {"199.0", plural.Many},
59 | {"199.00", plural.Many},
60 | {100, plural.Other},
61 | {"100", plural.Other},
62 | {"100.0", plural.Other},
63 | {"100.00", plural.Other},
64 | {102, plural.Other},
65 | {"102", plural.Other},
66 | {"102.0", plural.Other},
67 | {"102.00", plural.Other},
68 | {200, plural.Other},
69 | {"200", plural.Other},
70 | {"200.0", plural.Other},
71 | {"200.00", plural.Other},
72 | {202, plural.Other},
73 | {"202", plural.Other},
74 | {"202.0", plural.Other},
75 | {"202.00", plural.Other},
76 | }
77 | tests = appendFloatTests(tests, 0.1, 0.9, plural.Other)
78 | tests = appendFloatTests(tests, 1.1, 1.9, plural.Other)
79 | tests = appendFloatTests(tests, 2.1, 2.9, plural.Other)
80 | tests = appendFloatTests(tests, 3.1, 3.9, plural.Other)
81 | tests = appendFloatTests(tests, 4.1, 4.9, plural.Other)
82 | runTests(t, LanguageWithID("ar"), tests)
83 | }
84 |
85 | func TestCatalan(t *testing.T) {
86 | tests := []pluralTest{
87 | {0, plural.Other},
88 | {"0", plural.Other},
89 | {1, plural.One},
90 | {"1", plural.One},
91 | {"1.0", plural.Other},
92 | {2, plural.Other},
93 | {"2", plural.Other},
94 | }
95 | tests = appendIntTests(tests, 2, 10, plural.Other)
96 | tests = appendFloatTests(tests, 0, 10, plural.Other)
97 | runTests(t, LanguageWithID("ca"), tests)
98 | }
99 |
100 | func TestChinese(t *testing.T) {
101 | tests := appendIntTests(nil, 0, 10, plural.Other)
102 | tests = appendFloatTests(tests, 0, 10, plural.Other)
103 | runTests(t, LanguageWithID("zh"), tests)
104 | }
105 |
106 | func TestCzech(t *testing.T) {
107 | tests := []pluralTest{
108 | {0, plural.Other},
109 | {"0", plural.Other},
110 | {1, plural.One},
111 | {"1", plural.One},
112 | {2, plural.Few},
113 | {"2", plural.Few},
114 | {3, plural.Few},
115 | {"3", plural.Few},
116 | {4, plural.Few},
117 | {"4", plural.Few},
118 | {5, plural.Other},
119 | {"5", plural.Other},
120 | }
121 | tests = appendFloatTests(tests, 0, 10, plural.Many)
122 | runTests(t, LanguageWithID("cs"), tests)
123 | }
124 |
125 | func TestDanish(t *testing.T) {
126 | tests := []pluralTest{
127 | {0, plural.Other},
128 | {1, plural.One},
129 | {2, plural.Other},
130 | }
131 | tests = appendFloatTests(tests, 0.1, 1.9, plural.One)
132 | tests = appendFloatTests(tests, 2.0, 10.0, plural.Other)
133 | runTests(t, LanguageWithID("da"), tests)
134 | }
135 |
136 | func TestDutch(t *testing.T) {
137 | tests := []pluralTest{
138 | {0, plural.Other},
139 | {1, plural.One},
140 | {2, plural.Other},
141 | }
142 | tests = appendFloatTests(tests, 0.0, 10.0, plural.Other)
143 | runTests(t, LanguageWithID("nl"), tests)
144 | }
145 |
146 | func TestEnglish(t *testing.T) {
147 | tests := []pluralTest{
148 | {0, plural.Other},
149 | {1, plural.One},
150 | {2, plural.Other},
151 | }
152 | tests = appendFloatTests(tests, 0.0, 10.0, plural.Other)
153 | runTests(t, LanguageWithID("en"), tests)
154 | }
155 |
156 | func TestFrench(t *testing.T) {
157 | tests := []pluralTest{
158 | {0, plural.One},
159 | {1, plural.One},
160 | {2, plural.Other},
161 | }
162 | tests = appendFloatTests(tests, 0.0, 1.9, plural.One)
163 | tests = appendFloatTests(tests, 2.0, 10.0, plural.Other)
164 | runTests(t, LanguageWithID("fr"), tests)
165 | }
166 |
167 | func TestGerman(t *testing.T) {
168 | tests := []pluralTest{
169 | {0, plural.Other},
170 | {1, plural.One},
171 | {2, plural.Other},
172 | }
173 | tests = appendFloatTests(tests, 0.0, 10.0, plural.Other)
174 | runTests(t, LanguageWithID("de"), tests)
175 | }
176 |
177 | func TestItalian(t *testing.T) {
178 | tests := []pluralTest{
179 | {0, plural.Other},
180 | {1, plural.One},
181 | {2, plural.Other},
182 | }
183 | tests = appendFloatTests(tests, 0.0, 10.0, plural.Other)
184 | runTests(t, LanguageWithID("it"), tests)
185 | }
186 |
187 | func TestJapanese(t *testing.T) {
188 | tests := appendIntTests(nil, 0, 10, plural.Other)
189 | tests = appendFloatTests(tests, 0, 10, plural.Other)
190 | runTests(t, LanguageWithID("ja"), tests)
191 | }
192 |
193 | func TestPortuguese(t *testing.T) {
194 | tests := []pluralTest{
195 | {0, plural.Other},
196 | {1, plural.One},
197 | {2, plural.Other},
198 | }
199 | tests = appendFloatTests(tests, 0.0, 10.0, plural.Other)
200 | runTests(t, LanguageWithID("pt"), tests)
201 | }
202 |
203 | func TestPortugueseBrazilian(t *testing.T) {
204 | tests := []pluralTest{
205 | {0, plural.Other},
206 | {"0.0", plural.Other},
207 | {"0.1", plural.One},
208 | {"0.01", plural.One},
209 | {1, plural.One},
210 | {"1", plural.One},
211 | {"1.1", plural.Other},
212 | {"1.01", plural.Other},
213 | {2, plural.Other},
214 | }
215 | tests = appendFloatTests(tests, 2.0, 10.0, plural.Other)
216 | runTests(t, LanguageWithID("pt-BR"), tests)
217 | }
218 |
219 | func TestSpanish(t *testing.T) {
220 | tests := []pluralTest{
221 | {0, plural.Other},
222 | {1, plural.One},
223 | {"1", plural.One},
224 | {"1.0", plural.One},
225 | {"1.00", plural.One},
226 | {2, plural.Other},
227 | }
228 | tests = appendFloatTests(tests, 0.0, 0.9, plural.Other)
229 | tests = appendFloatTests(tests, 1.1, 10.0, plural.Other)
230 | runTests(t, LanguageWithID("es"), tests)
231 | }
232 |
233 | func appendIntTests(tests []pluralTest, from, to int, pc plural.Category) []pluralTest {
234 | for i := from; i <= to; i++ {
235 | tests = append(tests, pluralTest{i, pc})
236 | }
237 | return tests
238 | }
239 |
240 | func appendFloatTests(tests []pluralTest, from, to float64, pc plural.Category) []pluralTest {
241 | stride := 0.1
242 | format := "%.1f"
243 | for f := from; f < to; f += stride {
244 | tests = append(tests, pluralTest{fmt.Sprintf(format, f), pc})
245 | }
246 | tests = append(tests, pluralTest{fmt.Sprintf(format, to), pc})
247 | return tests
248 | }
249 |
250 | func runTests(t *testing.T, language *Language, tests []pluralTest) {
251 | for _, test := range tests {
252 | if pc, err := language.PluralCategory(test.num); pc != test.pc {
253 | t.Errorf("PluralCategory(%#v) returned %s, %v; expected %s", test.num, pc, err, test.pc)
254 | }
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/go-i18n/README.md:
--------------------------------------------------------------------------------
1 | go-i18n [](http://travis-ci.org/nicksnyder/go-i18n)
2 | =======
3 |
4 | go-i18n is a Go [package](#i18n-package) and a [command](#goi18n-command) that can be used to translate Go programs into multiple languages.
5 |
6 | Requires Go 1.2.
7 |
8 | Features
9 | --------
10 |
11 | * Implements [CLDR plural rules](http://cldr.unicode.org/index/cldr-spec/plural-rules).
12 | * Uses [text/template](http://golang.org/pkg/text/template/) for strings with variables.
13 | * Translation files are simple JSON.
14 | * [Documented](http://godoc.org/github.com/nicksnyder/go-i18n) and [tested](https://travis-ci.org/nicksnyder/go-i18n)!
15 |
16 | i18n package
17 | ------------
18 |
19 | The i18n package provides runtime APIs for looking up translated strings.
20 |
21 | ```go
22 | import "github.com/nicksnyder/go-i18n/i18n"
23 | ```
24 |
25 | ##### Loading translations
26 |
27 | Load translation files during your program's initialization.
28 | The name of a translation file must contain a supported [language tag](http://en.wikipedia.org/wiki/IETF_language_tag).
29 |
30 | ```go
31 | i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
32 | ```
33 |
34 | ##### Selecting a locale
35 |
36 | Tfunc returns a function that can lookup the translation of a string for that locale.
37 | It accepts one or more locale parameters so you can gracefully fallback to other locales.
38 |
39 | ```go
40 | userLocale = "ar-AR" // user preference, accept header, language cookie
41 | defaultLocale = "en-US" // known valid locale
42 | T, err := i18n.Tfunc(userLocale, defaultLocale)
43 | ```
44 |
45 | ##### Loading a string translation
46 |
47 | Use the translation function to fetch the translation of a string.
48 |
49 | ```go
50 | fmt.Println(T("Hello world"))
51 | ```
52 |
53 | Usually it is a good idea to identify strings by a generic id rather than the English translation, but the rest of this document will continue to use the English translation for readability.
54 |
55 | ```go
56 | T("program_greeting")
57 | ```
58 |
59 | ##### Strings with variables
60 |
61 | You can have variables in your string using [text/template](http://golang.org/pkg/text/template/) syntax.
62 |
63 | ```go
64 | T("Hello {{.Person}}", map[string]interface{}{
65 | "Person": "Bob",
66 | })
67 | ```
68 |
69 | ##### Plural strings
70 |
71 | Each language handles pluralization differently. A few examples:
72 | * English treats one as singular and all other numbers as plural (e.g. 0 cats, 1 cat, 2 cats).
73 | * French treats zero as singular.
74 | * Japan has a single plural form for all numbers.
75 | * Arabic has six different plural forms!
76 |
77 | The translation function handles [all of this logic](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html) for you.
78 |
79 | ```go
80 | T("You have {{.Count}} unread emails", 2)
81 | T("I am {{.Count}} meters tall.", "1.7")
82 | ```
83 |
84 | With variables:
85 |
86 | ```go
87 | T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
88 | "Person": "Bob",
89 | })
90 | ```
91 |
92 | Sentences with multiple plural components can be supported with nesting.
93 |
94 | ```go
95 | T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
96 | "Person": "Bob",
97 | "Timeframe": T("{{.Count}} days", 2),
98 | })
99 | ```
100 |
101 | A complete example is [here](i18n/example_test.go).
102 |
103 | ##### Strings in templates
104 |
105 | You can call the `.Funcs()` method on a [text/template](http://golang.org/pkg/text/template/#Template.Funcs) or [html/template](http://golang.org/pkg/html/template/#Template.Funcs) to register the translation function for usage inside of that template.
106 |
107 | A complete example is [here](i18n/exampletemplate_test.go).
108 |
109 | goi18n command
110 | --------------
111 |
112 | The goi18n command provides functionality for managing the translation process.
113 |
114 | ### Installation
115 |
116 | Make sure you have [setup GOPATH](http://golang.org/doc/code.html#GOPATH).
117 |
118 | go get -u github.com/nicksnyder/go-i18n/goi18n
119 | goi18n -help
120 |
121 | ### Workflow
122 |
123 | A typical workflow looks like this:
124 |
125 | 1. Add a new string to your source code.
126 |
127 | ```go
128 | T("settings_title")
129 | ```
130 |
131 | 2. Add the string to en-US.all.json
132 |
133 | ```json
134 | [
135 | {
136 | "id": "settings_title",
137 | "translation": "Settings"
138 | }
139 | ]
140 | ```
141 |
142 | 3. Run goi18n
143 |
144 | ```
145 | goi18n path/to/*.all.json
146 | ```
147 |
148 | 4. Send `path/to/*.untranslated.json` to get translated.
149 | 5. Run goi18n again to merge the translations
150 |
151 | ```sh
152 | goi18n path/to/*.all.json path/to/*.untranslated.json
153 | ```
154 |
155 | Translation files
156 | -----------------
157 |
158 | A translation file stores translated and untranslated strings.
159 |
160 | Example:
161 |
162 | ```json
163 | [
164 | {
165 | "id": "d_days",
166 | "translation": {
167 | "one": "{{.Count}} day",
168 | "other": "{{.Count}} days"
169 | }
170 | },
171 | {
172 | "id": "my_height_in_meters",
173 | "translation": {
174 | "one": "I am {{.Count}} meter tall.",
175 | "other": "I am {{.Count}} meters tall."
176 | }
177 | },
178 | {
179 | "id": "person_greeting",
180 | "translation": "Hello {{.Person}}"
181 | },
182 | {
183 | "id": "person_unread_email_count",
184 | "translation": {
185 | "one": "{{.Person}} has {{.Count}} unread email.",
186 | "other": "{{.Person}} has {{.Count}} unread emails."
187 | }
188 | },
189 | {
190 | "id": "person_unread_email_count_timeframe",
191 | "translation": {
192 | "one": "{{.Person}} has {{.Count}} unread email in the past {{.Timeframe}}.",
193 | "other": "{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}."
194 | }
195 | },
196 | {
197 | "id": "program_greeting",
198 | "translation": "Hello world"
199 | },
200 | {
201 | "id": "your_unread_email_count",
202 | "translation": {
203 | "one": "You have {{.Count}} unread email.",
204 | "other": "You have {{.Count}} unread emails."
205 | }
206 | }
207 | ]
208 | ```
209 |
210 | Supported languages
211 | -------------------
212 |
213 | * Arabic (`ar`)
214 | * Catalan (`ca`)
215 | * Chinese (simplified and traditional) (`zh`)
216 | * Czech (`cs`)
217 | * Danish (`da`)
218 | * Dutch (`nl`)
219 | * English (`en`)
220 | * French (`fr`)
221 | * German (`de`)
222 | * Italian (`it`)
223 | * Japanese (`ja`)
224 | * Portuguese (`pt`)
225 | * Portuguese (Brazilian) (`pt-BR`)
226 | * Spanish (`es`)
227 |
228 | More languages are straightforward to add:
229 |
230 | 1. Lookup the language's [CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).
231 | 2. Add the language to [language.go](i18n/language/language.go):
232 |
233 | ```go
234 | var languages = map[string]*Language{
235 | // ...
236 | "en": &Language{
237 | ID: "en",
238 | Name: "English",
239 | PluralCategories: newSet(plural.One, plural.Other),
240 | PluralFunc: func(ops *plural.Operands) plural.Category {
241 | if ops.I == 1 && ops.V == 0 {
242 | return plural.One
243 | }
244 | return plural.Other
245 | },
246 | },
247 | // ...
248 | }
249 | ```
250 |
251 | 3. Add a test to [language_test.go](i18n/language/language_test.go)
252 | 4. Submit a pull request!
253 |
254 | License
255 | -------
256 | go-i18n is available under the MIT license. See the [LICENSE](LICENSE) file for more info.
257 |
--------------------------------------------------------------------------------
/go-i18n/i18n/translation/plural_translation_test.go:
--------------------------------------------------------------------------------
1 | package translation
2 |
3 | import (
4 | "github.com/goinggo/beego-mgo/go-i18n/i18n/plural"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | func mustTemplate(t *testing.T, src string) *template {
10 | tmpl, err := newTemplate(src)
11 | if err != nil {
12 | t.Fatal(err)
13 | }
14 | return tmpl
15 | }
16 |
17 | func pluralTranslationFixture(t *testing.T, id string, pluralCategories ...plural.Category) *pluralTranslation {
18 | templates := make(map[plural.Category]*template, len(pluralCategories))
19 | for _, pc := range pluralCategories {
20 | templates[pc] = mustTemplate(t, string(pc))
21 | }
22 | return &pluralTranslation{id, templates}
23 | }
24 |
25 | func verifyDeepEqual(t *testing.T, actual, expected interface{}) {
26 | if !reflect.DeepEqual(actual, expected) {
27 | t.Fatalf("\n%#v\nnot equal to expected value\n%#v", actual, expected)
28 | }
29 | }
30 |
31 | func TestPluralTranslationMerge(t *testing.T) {
32 | pt := pluralTranslationFixture(t, "id", plural.One, plural.Other)
33 | oneTemplate, otherTemplate := pt.templates[plural.One], pt.templates[plural.Other]
34 |
35 | pt.Merge(pluralTranslationFixture(t, "id"))
36 | verifyDeepEqual(t, pt.templates, map[plural.Category]*template{
37 | plural.One: oneTemplate,
38 | plural.Other: otherTemplate,
39 | })
40 |
41 | pt2 := pluralTranslationFixture(t, "id", plural.One, plural.Two)
42 | pt.Merge(pt2)
43 | verifyDeepEqual(t, pt.templates, map[plural.Category]*template{
44 | plural.One: pt2.templates[plural.One],
45 | plural.Two: pt2.templates[plural.Two],
46 | plural.Other: otherTemplate,
47 | })
48 | }
49 |
50 | /* Test implementations from old idea
51 |
52 | func TestCopy(t *testing.T) {
53 | ls := &LocalizedString{
54 | ID: "id",
55 | Translation: testingTemplate(t, "translation {{.Hello}}"),
56 | Translations: map[plural.Category]*template{
57 | plural.One: testingTemplate(t, "plural {{.One}}"),
58 | plural.Other: testingTemplate(t, "plural {{.Other}}"),
59 | },
60 | }
61 |
62 | c := ls.Copy()
63 | delete(c.Translations, plural.One)
64 | if _, ok := ls.Translations[plural.One]; !ok {
65 | t.Errorf("deleting plural translation from copy deleted it from the original")
66 | }
67 | c.Translations[plural.Two] = testingTemplate(t, "plural {{.Two}}")
68 | if _, ok := ls.Translations[plural.Two]; ok {
69 | t.Errorf("adding plural translation to copy added it to the original")
70 | }
71 | }
72 |
73 | func TestNormalize(t *testing.T) {
74 | oneTemplate := testingTemplate(t, "one {{.One}}")
75 | ls := &LocalizedString{
76 | Translation: testingTemplate(t, "single {{.Single}}"),
77 | Translations: map[plural.Category]*template{
78 | plural.One: oneTemplate,
79 | plural.Two: testingTemplate(t, "two {{.Two}}"),
80 | },
81 | }
82 | ls.Normalize(LanguageWithCode("en"))
83 | if ls.Translation != nil {
84 | t.Errorf("ls.Translation is %#v; expected nil", ls.Translation)
85 | }
86 | if actual := ls.Translations[plural.Two]; actual != nil {
87 | t.Errorf("ls.Translation[plural.Two] is %#v; expected nil", actual)
88 | }
89 | if actual := ls.Translations[plural.One]; actual != oneTemplate {
90 | t.Errorf("ls.Translations[plural.One] is %#v; expected %#v", actual, oneTemplate)
91 | }
92 | if _, ok := ls.Translations[plural.Other]; !ok {
93 | t.Errorf("ls.Translations[plural.Other] shouldn't be empty")
94 | }
95 | }
96 |
97 | func TestMergeTranslation(t *testing.T) {
98 | ls := &LocalizedString{}
99 |
100 | translation := testingTemplate(t, "one {{.Hello}}")
101 | ls.Merge(&LocalizedString{
102 | Translation: translation,
103 | })
104 | if ls.Translation != translation {
105 | t.Errorf("expected %#v; got %#v", translation, ls.Translation)
106 | }
107 |
108 | ls.Merge(&LocalizedString{})
109 | if ls.Translation != translation {
110 | t.Errorf("expected %#v; got %#v", translation, ls.Translation)
111 | }
112 |
113 | translation = testingTemplate(t, "two {{.Hello}}")
114 | ls.Merge(&LocalizedString{
115 | Translation: translation,
116 | })
117 | if ls.Translation != translation {
118 | t.Errorf("expected %#v; got %#v", translation, ls.Translation)
119 | }
120 | }
121 |
122 | func TestMergeTranslations(t *testing.T) {
123 | ls := &LocalizedString{}
124 |
125 | oneTemplate := testingTemplate(t, "one {{.One}}")
126 | otherTemplate := testingTemplate(t, "other {{.Other}}")
127 | ls.Merge(&LocalizedString{
128 | Translations: map[plural.Category]*template{
129 | plural.One: oneTemplate,
130 | plural.Other: otherTemplate,
131 | },
132 | })
133 | if actual := ls.Translations[plural.One]; actual != oneTemplate {
134 | t.Errorf("ls.Translations[plural.One] expected %#v; got %#v", oneTemplate, actual)
135 | }
136 | if actual := ls.Translations[plural.Other]; actual != otherTemplate {
137 | t.Errorf("ls.Translations[plural.Other] expected %#v; got %#v", otherTemplate, actual)
138 | }
139 |
140 | ls.Merge(&LocalizedString{
141 | Translations: map[plural.Category]*template{},
142 | })
143 | if actual := ls.Translations[plural.One]; actual != oneTemplate {
144 | t.Errorf("ls.Translations[plural.One] expected %#v; got %#v", oneTemplate, actual)
145 | }
146 | if actual := ls.Translations[plural.Other]; actual != otherTemplate {
147 | t.Errorf("ls.Translations[plural.Other] expected %#v; got %#v", otherTemplate, actual)
148 | }
149 |
150 | twoTemplate := testingTemplate(t, "two {{.Two}}")
151 | otherTemplate = testingTemplate(t, "second other {{.Other}}")
152 | ls.Merge(&LocalizedString{
153 | Translations: map[plural.Category]*template{
154 | plural.Two: twoTemplate,
155 | plural.Other: otherTemplate,
156 | },
157 | })
158 | if actual := ls.Translations[plural.One]; actual != oneTemplate {
159 | t.Errorf("ls.Translations[plural.One] expected %#v; got %#v", oneTemplate, actual)
160 | }
161 | if actual := ls.Translations[plural.Two]; actual != twoTemplate {
162 | t.Errorf("ls.Translations[plural.Two] expected %#v; got %#v", twoTemplate, actual)
163 | }
164 | if actual := ls.Translations[plural.Other]; actual != otherTemplate {
165 | t.Errorf("ls.Translations[plural.Other] expected %#v; got %#v", otherTemplate, actual)
166 | }
167 | }
168 |
169 | func TestMissingTranslations(t *testing.T) {
170 | en := LanguageWithCode("en")
171 |
172 | tests := []struct {
173 | localizedString *LocalizedString
174 | language *Language
175 | expected bool
176 | }{
177 | {
178 | &LocalizedString{},
179 | en,
180 | true,
181 | },
182 | {
183 | &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")},
184 | en,
185 | false,
186 | },
187 | {
188 | &LocalizedString{
189 | Translation: testingTemplate(t, "single {{.Single}}"),
190 | Translations: map[plural.Category]*template{
191 | plural.One: testingTemplate(t, "one {{.One}}"),
192 | }},
193 | en,
194 | true,
195 | },
196 | {
197 | &LocalizedString{Translations: map[plural.Category]*template{
198 | plural.One: testingTemplate(t, "one {{.One}}"),
199 | }},
200 | en,
201 | true,
202 | },
203 | {
204 | &LocalizedString{Translations: map[plural.Category]*template{
205 | plural.One: nil,
206 | plural.Other: nil,
207 | }},
208 | en,
209 | true,
210 | },
211 | {
212 | &LocalizedString{Translations: map[plural.Category]*template{
213 | plural.One: testingTemplate(t, ""),
214 | plural.Other: testingTemplate(t, ""),
215 | }},
216 | en,
217 | true,
218 | },
219 | {
220 | &LocalizedString{Translations: map[plural.Category]*template{
221 | plural.One: testingTemplate(t, "one {{.One}}"),
222 | plural.Other: testingTemplate(t, "other {{.Other}}"),
223 | }},
224 | en,
225 | false,
226 | },
227 | }
228 |
229 | for _, tt := range tests {
230 | if actual := tt.localizedString.MissingTranslations(tt.language); actual != tt.expected {
231 | t.Errorf("expected %t got %t for %s, %#v",
232 | tt.expected, actual, tt.language.code, tt.localizedString)
233 | }
234 | }
235 | }
236 |
237 | func TestHasTranslations(t *testing.T) {
238 | en := LanguageWithCode("en")
239 |
240 | tests := []struct {
241 | localizedString *LocalizedString
242 | language *Language
243 | expected bool
244 | }{
245 | {
246 | &LocalizedString{},
247 | en,
248 | false,
249 | },
250 | {
251 | &LocalizedString{Translation: testingTemplate(t, "single {{.Single}}")},
252 | en,
253 | true,
254 | },
255 | {
256 | &LocalizedString{
257 | Translation: testingTemplate(t, "single {{.Single}}"),
258 | Translations: map[plural.Category]*template{}},
259 | en,
260 | false,
261 | },
262 | {
263 | &LocalizedString{Translations: map[plural.Category]*template{
264 | plural.One: testingTemplate(t, "one {{.One}}"),
265 | }},
266 | en,
267 | true,
268 | },
269 | {
270 | &LocalizedString{Translations: map[plural.Category]*template{
271 | plural.Two: testingTemplate(t, "two {{.Two}}"),
272 | }},
273 | en,
274 | false,
275 | },
276 | {
277 | &LocalizedString{Translations: map[plural.Category]*template{
278 | plural.One: nil,
279 | }},
280 | en,
281 | false,
282 | },
283 | {
284 | &LocalizedString{Translations: map[plural.Category]*template{
285 | plural.One: testingTemplate(t, ""),
286 | }},
287 | en,
288 | false,
289 | },
290 | }
291 |
292 | for _, tt := range tests {
293 | if actual := tt.localizedString.HasTranslations(tt.language); actual != tt.expected {
294 | t.Errorf("expected %t got %t for %s, %#v",
295 | tt.expected, actual, tt.language.code, tt.localizedString)
296 | }
297 | }
298 | }
299 |
300 | func testingTemplate(t *testing.T, src string) *template {
301 | tmpl, err := newTemplate(src)
302 | if err != nil {
303 | t.Fatal(err)
304 | }
305 | return tmpl
306 | }
307 | */
308 |
--------------------------------------------------------------------------------
/utilities/mongo/mongo.go:
--------------------------------------------------------------------------------
1 | // Copyright 2013 Ardan Studios. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package mongo provides mongo connectivity support.
6 | package mongo
7 |
8 | import (
9 | "encoding/json"
10 | "fmt"
11 | "strings"
12 | "time"
13 |
14 | log "github.com/goinggo/tracelog"
15 | "github.com/kelseyhightower/envconfig"
16 | "gopkg.in/mgo.v2"
17 | "gopkg.in/mgo.v2/bson"
18 | )
19 |
20 | const (
21 | // MasterSession provides direct access to master database.
22 | MasterSession = "master"
23 |
24 | // MonotonicSession provides reads to slaves.
25 | MonotonicSession = "monotonic"
26 | )
27 |
28 | var (
29 | // Reference to the singleton.
30 | singleton mongoManager
31 | )
32 |
33 | type (
34 | // mongoConfiguration contains settings for initialization.
35 | mongoConfiguration struct {
36 | Hosts string
37 | Database string
38 | UserName string
39 | Password string
40 | }
41 |
42 | // mongoManager contains dial and session information.
43 | mongoSession struct {
44 | mongoDBDialInfo *mgo.DialInfo
45 | mongoSession *mgo.Session
46 | }
47 |
48 | // mongoManager manages a map of session.
49 | mongoManager struct {
50 | sessions map[string]mongoSession
51 | }
52 |
53 | // DBCall defines a type of function that can be used
54 | // to excecute code against MongoDB.
55 | DBCall func(*mgo.Collection) error
56 | )
57 |
58 | // Startup brings the manager to a running state.
59 | func Startup(sessionID string) error {
60 | // If the system has already been started ignore the call.
61 | if singleton.sessions != nil {
62 | return nil
63 | }
64 |
65 | log.Started(sessionID, "Startup")
66 |
67 | // Pull in the configuration.
68 | var config mongoConfiguration
69 | if err := envconfig.Process("mgo", &config); err != nil {
70 | log.CompletedError(err, sessionID, "Startup")
71 | return err
72 | }
73 |
74 | // Create the Mongo Manager.
75 | singleton = mongoManager{
76 | sessions: make(map[string]mongoSession),
77 | }
78 |
79 | // Log the mongodb connection straps.
80 | log.Trace(sessionID, "Startup", "MongoDB : Hosts[%s]", config.Hosts)
81 | log.Trace(sessionID, "Startup", "MongoDB : Database[%s]", config.Database)
82 | log.Trace(sessionID, "Startup", "MongoDB : Username[%s]", config.UserName)
83 |
84 | hosts := strings.Split(config.Hosts, ",")
85 |
86 | // Create the strong session.
87 | if err := CreateSession(sessionID, "strong", MasterSession, hosts, config.Database, config.UserName, config.Password); err != nil {
88 | log.CompletedError(err, sessionID, "Startup")
89 | return err
90 | }
91 |
92 | // Create the monotonic session.
93 | if err := CreateSession(sessionID, "monotonic", MonotonicSession, hosts, config.Database, config.UserName, config.Password); err != nil {
94 | log.CompletedError(err, sessionID, "Startup")
95 | return err
96 | }
97 |
98 | log.Completed(sessionID, "Startup")
99 | return nil
100 | }
101 |
102 | // Shutdown systematically brings the manager down gracefully.
103 | func Shutdown(sessionID string) error {
104 | log.Started(sessionID, "Shutdown")
105 |
106 | // Close the databases
107 | for _, session := range singleton.sessions {
108 | CloseSession(sessionID, session.mongoSession)
109 | }
110 |
111 | log.Completed(sessionID, "Shutdown")
112 | return nil
113 | }
114 |
115 | // CreateSession creates a connection pool for use.
116 | func CreateSession(sessionID string, mode string, sessionName string, hosts []string, databaseName string, username string, password string) error {
117 | log.Startedf(sessionID, "CreateSession", "Mode[%s] SessionName[%s] Hosts[%s] DatabaseName[%s] Username[%s]", mode, sessionName, hosts, databaseName, username)
118 |
119 | // Create the database object
120 | mongoSession := mongoSession{
121 | mongoDBDialInfo: &mgo.DialInfo{
122 | Addrs: hosts,
123 | Timeout: 60 * time.Second,
124 | Database: databaseName,
125 | Username: username,
126 | Password: password,
127 | },
128 | }
129 |
130 | // Establish the master session.
131 | var err error
132 | mongoSession.mongoSession, err = mgo.DialWithInfo(mongoSession.mongoDBDialInfo)
133 | if err != nil {
134 | log.CompletedError(err, sessionID, "CreateSession")
135 | return err
136 | }
137 |
138 | switch mode {
139 | case "strong":
140 | // Reads and writes will always be made to the master server using a
141 | // unique connection so that reads and writes are fully consistent,
142 | // ordered, and observing the most up-to-date data.
143 | // http://godoc.org/github.com/finapps/mgo#Session.SetMode
144 | mongoSession.mongoSession.SetMode(mgo.Strong, true)
145 | break
146 |
147 | case "monotonic":
148 | // Reads may not be entirely up-to-date, but they will always see the
149 | // history of changes moving forward, the data read will be consistent
150 | // across sequential queries in the same session, and modifications made
151 | // within the session will be observed in following queries (read-your-writes).
152 | // http://godoc.org/github.com/finapps/mgo#Session.SetMode
153 | mongoSession.mongoSession.SetMode(mgo.Monotonic, true)
154 | }
155 |
156 | // Have the session check for errors.
157 | // http://godoc.org/github.com/finapps/mgo#Session.SetSafe
158 | mongoSession.mongoSession.SetSafe(&mgo.Safe{})
159 |
160 | // Add the database to the map.
161 | singleton.sessions[sessionName] = mongoSession
162 |
163 | log.Completed(sessionID, "CreateSession")
164 | return nil
165 | }
166 |
167 | // CopyMasterSession makes a copy of the master session for client use.
168 | func CopyMasterSession(sessionID string) (*mgo.Session, error) {
169 | return CopySession(sessionID, MasterSession)
170 | }
171 |
172 | // CopyMonotonicSession makes a copy of the monotonic session for client use.
173 | func CopyMonotonicSession(sessionID string) (*mgo.Session, error) {
174 | return CopySession(sessionID, MonotonicSession)
175 | }
176 |
177 | // CopySession makes a copy of the specified session for client use.
178 | func CopySession(sessionID string, useSession string) (*mgo.Session, error) {
179 | log.Startedf(sessionID, "CopySession", "UseSession[%s]", useSession)
180 |
181 | // Find the session object.
182 | session := singleton.sessions[useSession]
183 |
184 | if session.mongoSession == nil {
185 | err := fmt.Errorf("Unable To Locate Session %s", useSession)
186 | log.CompletedError(err, sessionID, "CopySession")
187 | return nil, err
188 | }
189 |
190 | // Copy the master session.
191 | mongoSession := session.mongoSession.Copy()
192 |
193 | log.Completed(sessionID, "CopySession")
194 | return mongoSession, nil
195 | }
196 |
197 | // CloneMasterSession makes a clone of the master session for client use.
198 | func CloneMasterSession(sessionID string) (*mgo.Session, error) {
199 | return CloneSession(sessionID, MasterSession)
200 | }
201 |
202 | // CloneMonotonicSession makes a clone of the monotinic session for client use.
203 | func CloneMonotonicSession(sessionID string) (*mgo.Session, error) {
204 | return CloneSession(sessionID, MonotonicSession)
205 | }
206 |
207 | // CloneSession makes a clone of the specified session for client use.
208 | func CloneSession(sessionID string, useSession string) (*mgo.Session, error) {
209 | log.Startedf(sessionID, "CloneSession", "UseSession[%s]", useSession)
210 |
211 | // Find the session object.
212 | session := singleton.sessions[useSession]
213 |
214 | if session.mongoSession == nil {
215 | err := fmt.Errorf("Unable To Locate Session %s", useSession)
216 | log.CompletedError(err, sessionID, "CloneSession")
217 | return nil, err
218 | }
219 |
220 | // Clone the master session.
221 | mongoSession := session.mongoSession.Clone()
222 |
223 | log.Completed(sessionID, "CloneSession")
224 | return mongoSession, nil
225 | }
226 |
227 | // CloseSession puts the connection back into the pool.
228 | func CloseSession(sessionID string, mongoSession *mgo.Session) {
229 | log.Started(sessionID, "CloseSession")
230 | mongoSession.Close()
231 | log.Completed(sessionID, "CloseSession")
232 | }
233 |
234 | // GetDatabase returns a reference to the specified database.
235 | func GetDatabase(mongoSession *mgo.Session, useDatabase string) *mgo.Database {
236 | return mongoSession.DB(useDatabase)
237 | }
238 |
239 | // GetCollection returns a reference to a collection for the specified database and collection name.
240 | func GetCollection(mongoSession *mgo.Session, useDatabase string, useCollection string) *mgo.Collection {
241 | return mongoSession.DB(useDatabase).C(useCollection)
242 | }
243 |
244 | // CollectionExists returns true if the collection name exists in the specified database.
245 | func CollectionExists(sessionID string, mongoSession *mgo.Session, useDatabase string, useCollection string) bool {
246 | database := mongoSession.DB(useDatabase)
247 | collections, err := database.CollectionNames()
248 |
249 | if err != nil {
250 | return false
251 | }
252 |
253 | for _, collection := range collections {
254 | if collection == useCollection {
255 | return true
256 | }
257 | }
258 |
259 | return false
260 | }
261 |
262 | // ToString converts the quer map to a string.
263 | func ToString(queryMap interface{}) string {
264 | json, err := json.Marshal(queryMap)
265 | if err != nil {
266 | return ""
267 | }
268 |
269 | return string(json)
270 | }
271 |
272 | // ToStringD converts bson.D to a string.
273 | func ToStringD(queryMap bson.D) string {
274 | json, err := json.Marshal(queryMap)
275 | if err != nil {
276 | return ""
277 | }
278 |
279 | return string(json)
280 | }
281 |
282 | // Execute the MongoDB literal function.
283 | func Execute(sessionID string, mongoSession *mgo.Session, databaseName string, collectionName string, dbCall DBCall) error {
284 | log.Startedf(sessionID, "Execute", "Database[%s] Collection[%s]", databaseName, collectionName)
285 |
286 | // Capture the specified collection.
287 | collection := GetCollection(mongoSession, databaseName, collectionName)
288 | if collection == nil {
289 | err := fmt.Errorf("Collection %s does not exist", collectionName)
290 | log.CompletedError(err, sessionID, "Execute")
291 | return err
292 | }
293 |
294 | // Execute the MongoDB call.
295 | err := dbCall(collection)
296 | if err != nil {
297 | log.CompletedError(err, sessionID, "Execute")
298 | return err
299 | }
300 |
301 | log.Completed(sessionID, "Execute")
302 | return nil
303 | }
304 |
--------------------------------------------------------------------------------
/static/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.0.3 (http://getbootstrap.com)
3 | * Copyright 2013 Twitter, Inc.
4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0
5 | */
6 |
7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b),f.trigger(d=a.Event("show.bs.dropdown")),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown"),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=a("[role=menu] li:not(.divider):visible a",f);if(h.length){var i=h.index(h.filter(":focus"));38==b.keyCode&&i>0&&i--,40==b.keyCode&&i').appendTo(document.body),this.$element.on("click.dismiss.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focus",i="hover"==g?"mouseleave":"blur";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show),void 0):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide),void 0):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this.tip();this.setContent(),this.options.animation&&c.addClass("fade");var d="function"==typeof this.options.placement?this.options.placement.call(this,c[0],this.$element[0]):this.options.placement,e=/\s?auto?\s?/i,f=e.test(d);f&&(d=d.replace(e,"")||"top"),c.detach().css({top:0,left:0,display:"block"}).addClass(d),this.options.container?c.appendTo(this.options.container):c.insertAfter(this.$element);var g=this.getPosition(),h=c[0].offsetWidth,i=c[0].offsetHeight;if(f){var j=this.$element.parent(),k=d,l=document.documentElement.scrollTop||document.body.scrollTop,m="body"==this.options.container?window.innerWidth:j.outerWidth(),n="body"==this.options.container?window.innerHeight:j.outerHeight(),o="body"==this.options.container?0:j.offset().left;d="bottom"==d&&g.top+g.height+i-l>n?"top":"top"==d&&g.top-l-i<0?"bottom":"right"==d&&g.right+h>m?"left":"left"==d&&g.left-h'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery);
--------------------------------------------------------------------------------