andrey1s
4 years ago
commit
9129f25c31
18 changed files with 678 additions and 0 deletions
@ -0,0 +1,13 @@ |
|||
kind: pipeline |
|||
name: default |
|||
|
|||
steps: |
|||
- name: test |
|||
image: golang |
|||
commands: |
|||
- go test -parallel 10 ./... |
|||
|
|||
- name: golangci-lint |
|||
image: golangci/golangci-lint:v1.39 |
|||
commands: |
|||
- golangci-lint run |
@ -0,0 +1,17 @@ |
|||
# ---> Go |
|||
# Binaries for programs and plugins |
|||
*.exe |
|||
*.exe~ |
|||
*.dll |
|||
*.so |
|||
*.dylib |
|||
|
|||
# Test binary, built with `go test -c` |
|||
*.test |
|||
|
|||
# Output of the go coverage tool, specifically when used with LiteIDE |
|||
*.out |
|||
|
|||
# Dependency directories (remove the comment below to include it) |
|||
# vendor/ |
|||
|
@ -0,0 +1,46 @@ |
|||
run: |
|||
timeout: 5m |
|||
|
|||
linters-settings: |
|||
dupl: |
|||
threshold: 100 |
|||
funlen: |
|||
lines: 100 |
|||
statements: 50 |
|||
goconst: |
|||
min-len: 2 |
|||
min-occurrences: 2 |
|||
gocyclo: |
|||
min-complexity: 15 |
|||
golint: |
|||
min-confidence: 0 |
|||
govet: |
|||
check-shadowing: true |
|||
lll: |
|||
line-length: 140 |
|||
maligned: |
|||
suggest-new: true |
|||
misspell: |
|||
locale: US |
|||
|
|||
linters: |
|||
enable-all: true |
|||
disable: |
|||
- exhaustivestruct |
|||
- maligned |
|||
- interfacer |
|||
- scopelint |
|||
|
|||
issues: |
|||
# Excluding configuration per-path, per-linter, per-text and per-source |
|||
exclude-rules: |
|||
- path: _test\.go |
|||
linters: |
|||
- gomnd |
|||
- exhaustivestruct |
|||
- wrapcheck |
|||
- path: test/* |
|||
linters: |
|||
- gomnd |
|||
- exhaustivestruct |
|||
- wrapcheck |
@ -0,0 +1,19 @@ |
|||
MIT License Copyright (c) 2020 4devs |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is furnished |
|||
to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice (including the next |
|||
paragraph) shall be included in all copies or substantial portions of the |
|||
Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
|||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS |
|||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF |
|||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,5 @@ |
|||
# translation |
|||
|
|||
[![Build Status](https://drone.gitoa.ru/api/badges/go-4devs/translation/status.svg)](https://drone.gitoa.ru/go-4devs/translation) |
|||
[![Go Report Card](https://goreportcard.com/badge/gitoa.ru/go-4devs/translation)](https://goreportcard.com/report/gitoa.ru/go-4devs/translation) |
|||
[![GoDoc](https://godoc.org/gitoa.ru/go-4devs/translation?status.svg)](http://godoc.org/gitoa.ru/go-4devs/translation) |
@ -0,0 +1,19 @@ |
|||
package arg |
|||
|
|||
import "fmt" |
|||
|
|||
// Arg defaults argument.
|
|||
type Arg struct { |
|||
Key string |
|||
Value interface{} |
|||
} |
|||
|
|||
// String arg to string.
|
|||
func (a Arg) String() string { |
|||
return fmt.Sprintf("key:%s, value:%v", a.Key, a.Value) |
|||
} |
|||
|
|||
// Val gets valuet argument.
|
|||
func (a Arg) Val() interface{} { |
|||
return a.Value |
|||
} |
@ -0,0 +1,45 @@ |
|||
package arg |
|||
|
|||
import "fmt" |
|||
|
|||
// FormatCurrency types format.
|
|||
type FormatCurrency int |
|||
|
|||
// Currency format.
|
|||
const ( |
|||
CurrencyFormatSymbol FormatCurrency = iota + 1 |
|||
CurrencyFormatISO |
|||
CurrencyFormatNarrowSymbol |
|||
) |
|||
|
|||
// CurrencyOption configures option.
|
|||
type CurrencyOption func(*Currency) |
|||
|
|||
// WithCurrencyFormat sets format currency.
|
|||
func WithCurrencyFormat(format FormatCurrency) CurrencyOption { |
|||
return func(c *Currency) { c.Format = format } |
|||
} |
|||
|
|||
// WithCurrencyISO sets ISO 4217 code currecy.
|
|||
func WithCurrencyISO(iso string) CurrencyOption { |
|||
return func(c *Currency) { c.ISO = iso } |
|||
} |
|||
|
|||
// Currency argument.
|
|||
type Currency struct { |
|||
Key string |
|||
Value interface{} |
|||
Format FormatCurrency |
|||
// ISO 3-letter ISO 4217
|
|||
ISO string |
|||
} |
|||
|
|||
// String gets string from currency.
|
|||
func (a Currency) String() string { |
|||
return fmt.Sprintf("currency key:%s, value:%v", a.Key, a.Value) |
|||
} |
|||
|
|||
// Val gets value currency.
|
|||
func (a Currency) Val() interface{} { |
|||
return a.Value |
|||
} |
@ -0,0 +1,49 @@ |
|||
package arg |
|||
|
|||
import "fmt" |
|||
|
|||
// FormatNumber format number.
|
|||
type FormatNumber int |
|||
|
|||
// format argument.
|
|||
const ( |
|||
NumberFormatDecimal FormatNumber = iota + 1 |
|||
NumberFormatPercent |
|||
NumberFormatPerMille |
|||
NumberFormatEngineering |
|||
NumberFormatScientific |
|||
) |
|||
|
|||
// NumberOption configure number argument.
|
|||
type NumberOption func(*Number) |
|||
|
|||
// WithNumberFormat sets format number.
|
|||
func WithNumberFormat(format FormatNumber) NumberOption { |
|||
return func(n *Number) { n.Format = format } |
|||
} |
|||
|
|||
// Number argument.
|
|||
type Number struct { |
|||
Key string |
|||
Value interface{} |
|||
Format FormatNumber |
|||
} |
|||
|
|||
// Configure number.
|
|||
func (n Number) Configure(opts ...NumberOption) Number { |
|||
for _, o := range opts { |
|||
o(&n) |
|||
} |
|||
|
|||
return n |
|||
} |
|||
|
|||
// Val gets number value.
|
|||
func (n Number) Val() interface{} { |
|||
return n.Value |
|||
} |
|||
|
|||
// String number to string.
|
|||
func (n Number) String() string { |
|||
return fmt.Sprintf("number key: %s, value: %v", n.Key, n.Value) |
|||
} |
@ -0,0 +1,27 @@ |
|||
package translation |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"golang.org/x/text/language" |
|||
) |
|||
|
|||
type ctxkey uint8 |
|||
|
|||
const ( |
|||
localeKey ctxkey = iota |
|||
) |
|||
|
|||
// WithLanguage sets language to context.
|
|||
func WithLanguage(ctx context.Context, lang language.Tag) context.Context { |
|||
return context.WithValue(ctx, localeKey, lang) |
|||
} |
|||
|
|||
// FromContext get language from context or use default.
|
|||
func FromContext(ctx context.Context, def language.Tag) language.Tag { |
|||
if t, ok := ctx.Value(localeKey).(language.Tag); ok { |
|||
return t |
|||
} |
|||
|
|||
return def |
|||
} |
@ -0,0 +1,5 @@ |
|||
module gitoa.ru/go-4devs/translation |
|||
|
|||
go 1.16 |
|||
|
|||
require golang.org/x/text v0.3.6 |
@ -0,0 +1,3 @@ |
|||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= |
|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
|||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
@ -0,0 +1,22 @@ |
|||
package icu |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"gitoa.ru/go-4devs/translation" |
|||
"gitoa.ru/go-4devs/translation/provider/gotext" |
|||
"golang.org/x/text/language" |
|||
) |
|||
|
|||
// nolint: gochecknoglobals
|
|||
var trans = translation.New(language.English, gotext.NewProvider()) |
|||
|
|||
// Trans translate message.
|
|||
func Trans(ctx context.Context, key string, opts ...translation.Option) string { |
|||
return trans.Trans(ctx, key, opts...) |
|||
} |
|||
|
|||
// SetTranslator sets translator.
|
|||
func SetTranslator(tr *translation.Translator) { |
|||
trans = tr |
|||
} |
@ -0,0 +1,41 @@ |
|||
package icu_test |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"log" |
|||
|
|||
"gitoa.ru/go-4devs/translation" |
|||
"gitoa.ru/go-4devs/translation/arg" |
|||
"gitoa.ru/go-4devs/translation/icu" |
|||
"golang.org/x/text/language" |
|||
"golang.org/x/text/message" |
|||
"golang.org/x/text/message/catalog" |
|||
) |
|||
|
|||
func ExampleTrans_withLanguage() { |
|||
err := message.Set(language.Russian, "Hello {city}", catalog.String("Привет %s")) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
|
|||
err = message.Set(language.Russian, "It costs {cost}", catalog.String("Это стоит %.2f.")) |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
|
|||
lang, err := language.Parse("ru") |
|||
if err != nil { |
|||
log.Fatal(err) |
|||
} |
|||
|
|||
ctx := translation.WithLanguage(context.Background(), lang) |
|||
|
|||
tr := icu.Trans(ctx, "Hello {city}", translation.WithArgs("Москва")) |
|||
fmt.Println(tr) |
|||
tr = icu.Trans(ctx, "It costs {cost}", translation.WithNumber("cost", 1000.00, arg.WithNumberFormat(arg.NumberFormatDecimal))) |
|||
fmt.Println(tr) |
|||
// Output:
|
|||
// Привет Москва
|
|||
// Это стоит 1 000,00.
|
|||
} |
@ -0,0 +1,108 @@ |
|||
package gotext |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"gitoa.ru/go-4devs/translation" |
|||
"gitoa.ru/go-4devs/translation/arg" |
|||
"golang.org/x/text/currency" |
|||
"golang.org/x/text/language" |
|||
"golang.org/x/text/message" |
|||
"golang.org/x/text/message/catalog" |
|||
"golang.org/x/text/number" |
|||
) |
|||
|
|||
var _ translation.Provider = (*Provider)(nil) |
|||
|
|||
// Option confires message provider.
|
|||
type Option func(*Provider) |
|||
|
|||
// WithCatalog set coatalog bu domain name.
|
|||
func WithCatalog(domain string, cat catalog.Catalog) Option { |
|||
return func(mp *Provider) { |
|||
mp.catalog[domain] = cat |
|||
} |
|||
} |
|||
|
|||
// NewProvider creates new messgae provider.
|
|||
func NewProvider(opts ...Option) *Provider { |
|||
mp := &Provider{ |
|||
catalog: map[string]catalog.Catalog{ |
|||
translation.DefaultDomain: message.DefaultCatalog, |
|||
}, |
|||
} |
|||
|
|||
for _, o := range opts { |
|||
o(mp) |
|||
} |
|||
|
|||
return mp |
|||
} |
|||
|
|||
// Provider provider messages.
|
|||
type Provider struct { |
|||
catalog map[string]catalog.Catalog |
|||
} |
|||
|
|||
// Translate by key and args.
|
|||
func (mp *Provider) Translate(ctx context.Context, key string, opt translation.Translate) string { |
|||
return message.NewPrinter(opt.Locale, message.Catalog(mp.catalog[opt.Domain])). |
|||
Sprintf(key, messageArgs(opt.Locale, opt.Args)...) |
|||
} |
|||
|
|||
func currencyMessage(lang language.Tag) func(v arg.Currency) interface{} { |
|||
return func(v arg.Currency) interface{} { |
|||
var u currency.Unit |
|||
|
|||
if v.ISO != "" { |
|||
u, _ = currency.ParseISO(v.ISO) |
|||
} else { |
|||
u, _ = currency.FromTag(lang) |
|||
} |
|||
|
|||
switch v.Format { |
|||
case arg.CurrencyFormatISO: |
|||
return currency.ISO(u.Amount(v.Val())) |
|||
case arg.CurrencyFormatSymbol: |
|||
return currency.Symbol(u.Amount(v.Val())) |
|||
case arg.CurrencyFormatNarrowSymbol: |
|||
return currency.NarrowSymbol(u.Amount(v.Val())) |
|||
} |
|||
|
|||
return u.Amount(v.Val()) |
|||
} |
|||
} |
|||
|
|||
func numberMessage(v arg.Number) interface{} { |
|||
switch v.Format { |
|||
case arg.NumberFormatPercent: |
|||
return number.Percent(v.Val()) |
|||
case arg.NumberFormatEngineering: |
|||
return number.Engineering(v.Val()) |
|||
case arg.NumberFormatPerMille: |
|||
return number.PerMille(v.Val()) |
|||
case arg.NumberFormatDecimal: |
|||
return number.Decimal(v.Val()) |
|||
case arg.NumberFormatScientific: |
|||
return number.Scientific(v.Val()) |
|||
} |
|||
|
|||
return v.Val() |
|||
} |
|||
|
|||
func messageArgs(lang language.Tag, in []translation.Arg) []interface{} { |
|||
out := make([]interface{}, 0, len(in)) |
|||
|
|||
for _, a := range in { |
|||
switch v := a.(type) { |
|||
case arg.Currency: |
|||
out = append(out, currencyMessage(lang)(v)) |
|||
case arg.Number: |
|||
out = append(out, numberMessage(v)) |
|||
default: |
|||
out = append(out, v.Val()) |
|||
} |
|||
} |
|||
|
|||
return out |
|||
} |
@ -0,0 +1,80 @@ |
|||
package translation |
|||
|
|||
import ( |
|||
"fmt" |
|||
|
|||
"gitoa.ru/go-4devs/translation/arg" |
|||
"golang.org/x/text/language" |
|||
) |
|||
|
|||
// Option configures translate.
|
|||
type Option func(*Translate) |
|||
|
|||
// Translate configure translate.
|
|||
type Translate struct { |
|||
Domain string |
|||
Locale language.Tag |
|||
Args []Arg |
|||
} |
|||
|
|||
// ArgValues gets value arguments.
|
|||
func (t *Translate) ArgValues() []interface{} { |
|||
a := make([]interface{}, len(t.Args)) |
|||
for i, v := range t.Args { |
|||
a[i] = v.Val() |
|||
} |
|||
|
|||
return a |
|||
} |
|||
|
|||
// Arg arg translate.
|
|||
type Arg interface { |
|||
Val() interface{} |
|||
fmt.Stringer |
|||
} |
|||
|
|||
// WithNumber sets number with options.
|
|||
func WithNumber(key string, val interface{}, opts ...arg.NumberOption) Option { |
|||
return func(t *Translate) { |
|||
t.Args = append(t.Args, arg.Number{Key: key, Value: val}.Configure(opts...)) |
|||
} |
|||
} |
|||
|
|||
// WithCurrency sets date argument.
|
|||
func WithCurrency(key string, val interface{}, opts ...arg.CurrencyOption) Option { |
|||
return func(t *Translate) { |
|||
c := arg.Currency{Value: val, Key: key} |
|||
|
|||
for _, o := range opts { |
|||
o(&c) |
|||
} |
|||
|
|||
t.Args = append(t.Args, c) |
|||
} |
|||
} |
|||
|
|||
// WithDomain sets domain translate.
|
|||
func WithDomain(domain string) Option { |
|||
return func(o *Translate) { |
|||
o.Domain = domain |
|||
} |
|||
} |
|||
|
|||
// WithLocale sets locale translate.
|
|||
func WithLocale(locale string) Option { |
|||
return func(o *Translate) { |
|||
o.Locale = language.Make(locale) |
|||
} |
|||
} |
|||
|
|||
// WithArgs sets arguments value.
|
|||
func WithArgs(vals ...interface{}) Option { |
|||
return func(t *Translate) { |
|||
args := make([]Arg, len(vals)) |
|||
for i, val := range vals { |
|||
args[i] = arg.Arg{Value: val} |
|||
} |
|||
|
|||
t.Args = append(t.Args, args...) |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
package translation |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"golang.org/x/text/language" |
|||
) |
|||
|
|||
// Default values.
|
|||
const ( |
|||
DefaultDomain = "messages" |
|||
) |
|||
|
|||
// Provider for translate key.
|
|||
type Provider interface { |
|||
Translate(ctx context.Context, key string, opt Translate) string |
|||
} |
|||
|
|||
// TranslatorOption options translator.
|
|||
type TranslatorOption func(*Translator) |
|||
|
|||
// New creates new translator.
|
|||
func New(locale language.Tag, provider Provider, opts ...TranslatorOption) *Translator { |
|||
tr := Translator{ |
|||
locale: locale, |
|||
provider: provider, |
|||
domain: DefaultDomain, |
|||
} |
|||
|
|||
for _, o := range opts { |
|||
o(&tr) |
|||
} |
|||
|
|||
return &tr |
|||
} |
|||
|
|||
// Translator struct.
|
|||
type Translator struct { |
|||
provider Provider |
|||
domain string |
|||
locale language.Tag |
|||
} |
|||
|
|||
// Trans translates key by options.
|
|||
func (t *Translator) Trans(ctx context.Context, key string, opts ...Option) string { |
|||
opt := Translate{ |
|||
Locale: FromContext(ctx, t.locale), |
|||
Domain: t.domain, |
|||
} |
|||
|
|||
for _, o := range opts { |
|||
o(&opt) |
|||
} |
|||
|
|||
return t.provider.Translate(ctx, key, opt) |
|||
} |
@ -0,0 +1,91 @@ |
|||
package translation_test |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
|
|||
"gitoa.ru/go-4devs/translation" |
|||
"gitoa.ru/go-4devs/translation/arg" |
|||
"gitoa.ru/go-4devs/translation/provider/gotext" |
|||
"golang.org/x/text/feature/plural" |
|||
"golang.org/x/text/language" |
|||
"golang.org/x/text/message/catalog" |
|||
) |
|||
|
|||
func ExampleTranslator_Trans() { |
|||
cat := catalog.NewBuilder() |
|||
|
|||
_ = cat.Set(language.Russian, "Hello World!", catalog.String("Привет Мир!")) |
|||
_ = cat.Set(language.Slovak, "Hello World!", catalog.String("Ahoj Svet!")) |
|||
_ = cat.Set(language.Hebrew, "Hello World!", catalog.String("שלום עולם")) |
|||
_ = cat.Set(language.Russian, "Hello {name}!", catalog.String("Привет %[1]s!")) |
|||
_ = cat.Set(language.English, "Hello {name}!", catalog.String("Hello %[1]s!")) |
|||
_ = cat.Set(language.English, "There are {bikes} bikes per household.", catalog.String("There are %v bikes per household.")) |
|||
_ = cat.Set(language.Russian, "There are {bikes} bikes per household.", catalog.String("В каждом доме есть %v велосипеда.")) |
|||
_ = cat.Set(language.English, "You are {minute} minute(s) late.", |
|||
plural.Selectf(1, "", |
|||
"=0", "You're on time.", |
|||
plural.One, "You are %v minute late.", |
|||
plural.Other, "You are %v minutes late.")) |
|||
_ = cat.Set(language.Russian, "You are {minute} minute(s) late.", |
|||
plural.Selectf(1, "", |
|||
"=1", "Вы опоздали на одну минуту.", |
|||
"=0", "Вы вовремя.", |
|||
plural.One, "Вы опоздали на %v минуту.", |
|||
plural.Few, "Вы опоздали на %v минуты.", |
|||
plural.Other, "Вы опоздали на %v минут.", |
|||
), |
|||
) |
|||
_ = cat.Set(language.Russian, "It costs {cost}.", |
|||
catalog.String("Это стоит %.2f.")) |
|||
_ = cat.Set(language.English, "It costs {cost}.", |
|||
catalog.String("It costs %.2f.")) |
|||
|
|||
provider := gotext.NewProvider(gotext.WithCatalog(translation.DefaultDomain, cat)) |
|||
|
|||
trans := translation.New(language.Russian, provider) |
|||
ctx := context.Background() |
|||
// context with language
|
|||
heCtx := translation.WithLanguage(ctx, language.Make("he")) |
|||
|
|||
fmt.Println(trans.Trans(ctx, "Hello World!")) |
|||
fmt.Println(trans.Trans(ctx, "Hello World!", translation.WithLocale("en"))) |
|||
fmt.Println(trans.Trans(ctx, "Hello World!", translation.WithLocale("sk"))) |
|||
fmt.Println(trans.Trans(heCtx, "Hello World!")) |
|||
|
|||
fmt.Println(trans.Trans(ctx, "Hello {name}!", translation.WithArgs("Andrey"))) |
|||
|
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithNumber("minute", 1))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithNumber("minute", 0))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithNumber("minute", 101))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithArgs(123456.78))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithArgs(50))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithArgs(1), translation.WithLocale("en"))) |
|||
fmt.Println(trans.Trans(ctx, "You are {minute} minute(s) late.", translation.WithArgs(33), translation.WithLocale("en"))) |
|||
|
|||
fmt.Println(trans.Trans(ctx, "There are {bikes} bikes per household.", translation.WithNumber("bikes", 1.2))) |
|||
fmt.Println(trans.Trans(ctx, "There are {bikes} bikes per household.", translation.WithNumber("bikes", 1.2), translation.WithLocale("en"))) |
|||
|
|||
fmt.Println(trans.Trans(ctx, "It costs {cost}.", |
|||
translation.WithCurrency("cost", 12.0101, arg.WithCurrencyISO("rub"), arg.WithCurrencyFormat(arg.CurrencyFormatSymbol)))) |
|||
fmt.Println(trans.Trans(ctx, "It costs {cost}.", |
|||
translation.WithCurrency("cost", 15.0, arg.WithCurrencyISO("HKD"), arg.WithCurrencyFormat(arg.CurrencyFormatSymbol)), |
|||
translation.WithLocale("en"))) |
|||
// Output:
|
|||
// Привет Мир!
|
|||
// Hello World!
|
|||
// Ahoj Svet!
|
|||
// שלום עולם
|
|||
// Привет Andrey!
|
|||
// Вы опоздали на одну минуту.
|
|||
// Вы вовремя.
|
|||
// Вы опоздали на 101 минуту.
|
|||
// Вы опоздали на 123 456,78 минут.
|
|||
// Вы опоздали на 50 минут.
|
|||
// You are 1 minute late.
|
|||
// You are 33 minutes late.
|
|||
// В каждом доме есть 1,2 велосипеда.
|
|||
// There are 1.2 bikes per household.
|
|||
// Это стоит ₽ 12.0101.
|
|||
// It costs HK$ 15.
|
|||
} |
@ -0,0 +1,32 @@ |
|||
package translation_test |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"testing" |
|||
|
|||
"gitoa.ru/go-4devs/translation" |
|||
"golang.org/x/text/language" |
|||
) |
|||
|
|||
type TestProvider struct{} |
|||
|
|||
func (tp *TestProvider) Translate(_ context.Context, key string, opt translation.Translate) string { |
|||
args := make([]interface{}, 0, len(opt.Args)+1) |
|||
args = append(args, key) |
|||
args = append(args, opt.ArgValues()...) |
|||
|
|||
return fmt.Sprint(args...) |
|||
} |
|||
|
|||
func TestTranslator(t *testing.T) { |
|||
t.Parallel() |
|||
|
|||
ctx := context.Background() |
|||
trans := translation.New(language.Russian, &TestProvider{}) |
|||
tr := trans.Trans(ctx, "key", translation.WithArgs("arg1", "arg2")) |
|||
|
|||
if tr != "keyarg1arg2" { |
|||
t.Fatalf("expect: keyarg1arg2, got:%s", tr) |
|||
} |
|||
} |
Loading…
Reference in new issue