Compare commits
11 Commits
geoip/v1.1
...
copierutil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c03ac708f0 | ||
|
|
9035e79520 | ||
|
|
3b2678de10 | ||
|
|
982e18a991 | ||
|
|
f2f5388906 | ||
|
|
9eca340c7e | ||
|
|
376746f4db | ||
|
|
efc24c452f | ||
|
|
78cef077e5 | ||
|
|
0420e35a30 | ||
|
|
0b18560901 |
43
copierutil/converters.go
Normal file
43
copierutil/converters.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package copierutil
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/tx7do/go-utils/timeutil"
|
||||
"github.com/tx7do/go-utils/trans"
|
||||
)
|
||||
|
||||
var TimeToStringConverter = copier.TypeConverter{
|
||||
SrcType: &time.Time{}, // 源类型
|
||||
DstType: trans.Ptr(""), // 目标类型
|
||||
Fn: func(src interface{}) (interface{}, error) {
|
||||
return timeutil.TimeToTimeString(src.(*time.Time)), nil
|
||||
},
|
||||
}
|
||||
|
||||
var StringToTimeConverter = copier.TypeConverter{
|
||||
SrcType: trans.Ptr(""),
|
||||
DstType: &time.Time{},
|
||||
Fn: func(src interface{}) (interface{}, error) {
|
||||
return timeutil.StringTimeToTime(src.(*string)), nil
|
||||
},
|
||||
}
|
||||
|
||||
var TimeToTimestamppbConverter = copier.TypeConverter{
|
||||
SrcType: &time.Time{},
|
||||
DstType: ×tamppb.Timestamp{},
|
||||
Fn: func(src interface{}) (interface{}, error) {
|
||||
return timeutil.TimeToTimestamppb(src.(*time.Time)), nil
|
||||
},
|
||||
}
|
||||
|
||||
var TimestamppbToTimeConverter = copier.TypeConverter{
|
||||
SrcType: ×tamppb.Timestamp{},
|
||||
DstType: &time.Time{},
|
||||
Fn: func(src interface{}) (interface{}, error) {
|
||||
return timeutil.TimestamppbToTime(src.(*timestamppb.Timestamp)), nil
|
||||
},
|
||||
}
|
||||
14
copierutil/go.mod
Normal file
14
copierutil/go.mod
Normal file
@@ -0,0 +1,14 @@
|
||||
module github.com/tx7do/go-utils/copierutil
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/tx7do/go-utils v1.1.22
|
||||
)
|
||||
|
||||
require google.golang.org/protobuf v1.36.6 // indirect
|
||||
|
||||
replace github.com/tx7do/go-utils => ../
|
||||
16
copierutil/go.sum
Normal file
16
copierutil/go.sum
Normal file
@@ -0,0 +1,16 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
|
||||
"math/rand/v2"
|
||||
"crypto/rand"
|
||||
)
|
||||
|
||||
// DefaultAESKey 默认AES密钥(16字节)
|
||||
@@ -19,8 +18,7 @@ func GenerateAESKey(length int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("invalid key length: %d, must be 16, 24, or 32 bytes", length)
|
||||
}
|
||||
key := make([]byte, length)
|
||||
src := rand.ChaCha8{}
|
||||
_, err := src.Read(key)
|
||||
_, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -42,23 +40,44 @@ func PKCS5UnPadding(origData []byte) []byte {
|
||||
}
|
||||
|
||||
// AesEncrypt AES加密
|
||||
func AesEncrypt(origData, key []byte) ([]byte, error) {
|
||||
func AesEncrypt(plainText, key, iv []byte) ([]byte, error) {
|
||||
if plainText == nil {
|
||||
return nil, fmt.Errorf("plain text is nil")
|
||||
}
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("key is nil")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
// AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
origData = PKCS5Padding(origData, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
return crypted, nil
|
||||
|
||||
if iv == nil {
|
||||
// 初始向量的长度必须等于块block的长度16字节
|
||||
iv = key[:blockSize]
|
||||
}
|
||||
|
||||
plainText = PKCS5Padding(plainText, blockSize)
|
||||
|
||||
blockMode := cipher.NewCBCEncrypter(block, iv)
|
||||
cryptedText := make([]byte, len(plainText))
|
||||
blockMode.CryptBlocks(cryptedText, plainText)
|
||||
return cryptedText, nil
|
||||
}
|
||||
|
||||
// AesDecrypt AES解密
|
||||
func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
func AesDecrypt(cryptedText, key, iv []byte) ([]byte, error) {
|
||||
if cryptedText == nil {
|
||||
return nil, fmt.Errorf("crypted text is nil")
|
||||
}
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("key is nil")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -66,9 +85,16 @@ func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||
|
||||
//AES分组长度为128位,所以blockSize=16,单位字节
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) //初始向量的长度必须等于块block的长度16字节
|
||||
origData := make([]byte, len(crypted))
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
origData = PKCS5UnPadding(origData)
|
||||
return origData, nil
|
||||
|
||||
if iv == nil {
|
||||
// 初始向量的长度必须等于块block的长度16字节
|
||||
iv = key[:blockSize]
|
||||
}
|
||||
|
||||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||
|
||||
plainText := make([]byte, len(cryptedText))
|
||||
blockMode.CryptBlocks(plainText, cryptedText)
|
||||
plainText = PKCS5UnPadding(plainText)
|
||||
return plainText, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestDecryptAES(t *testing.T) {
|
||||
aesKey = DefaultAESKey
|
||||
|
||||
plainText := []byte("cloud123456")
|
||||
encryptText, err := AesEncrypt(plainText, aesKey)
|
||||
encryptText, err := AesEncrypt(plainText, aesKey, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
@@ -30,7 +30,7 @@ func TestDecryptAES(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
decryptText, err := AesDecrypt(bytesPass, aesKey)
|
||||
decryptText, err := AesDecrypt(bytesPass, aesKey, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -59,44 +54,3 @@ func GenerateSalt(length int) (string, error) {
|
||||
}
|
||||
return hex.EncodeToString(salt), nil
|
||||
}
|
||||
|
||||
// DecryptAES AES解密函数
|
||||
func DecryptAES(cipherText, key string) (string, error) {
|
||||
// 将密文从Base64解码
|
||||
cipherData, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 创建AES块
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 检查密文长度是否为块大小的倍数
|
||||
if len(cipherData)%aes.BlockSize != 0 {
|
||||
return "", fmt.Errorf("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
// 创建CBC解密器
|
||||
iv := cipherData[:aes.BlockSize] // 提取IV
|
||||
cipherData = cipherData[aes.BlockSize:]
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
|
||||
// 解密
|
||||
plainText := make([]byte, len(cipherData))
|
||||
mode.CryptBlocks(plainText, cipherData)
|
||||
|
||||
// 去除填充
|
||||
plainText = pkcs7Unpad(plainText)
|
||||
|
||||
return string(plainText), nil
|
||||
}
|
||||
|
||||
// pkcs7Unpad PKCS7填充去除
|
||||
func pkcs7Unpad(data []byte) []byte {
|
||||
length := len(data)
|
||||
padding := int(data[length-1])
|
||||
return data[:length-padding]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,15 @@ func TestVerifyPassword(t *testing.T) {
|
||||
assert.True(t, bMatched)
|
||||
}
|
||||
|
||||
func TestVerifyPasswordWithSalt_CorrectPassword(t *testing.T) {
|
||||
password := "securePassword"
|
||||
salt, _ := GenerateSalt(16)
|
||||
hashedPassword, _ := HashPasswordWithSalt(password, salt)
|
||||
|
||||
result := VerifyPasswordWithSalt(password, salt, hashedPassword)
|
||||
assert.True(t, result, "Password verification should succeed with correct password and salt")
|
||||
}
|
||||
|
||||
func TestJwtToken(t *testing.T) {
|
||||
const bearerWord string = "Bearer"
|
||||
token := "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjowfQ.XgcKAAjHbA6o4sxxbEaMi05ingWvKdCNnyW9wowbJvs"
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/go-kratos/kratos/v2 v2.8.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tx7do/go-utils v1.1.16
|
||||
github.com/tx7do/go-utils v1.1.19
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
20
stringutil/replace.go
Normal file
20
stringutil/replace.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package stringutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// ReplaceJSONField 使用正则表达式替换 JSON 字符串中指定字段的值
|
||||
// fieldNames: 要替换的多个字段名,使用竖线(|)分隔(例如:"tenantId|tenant_id")
|
||||
// newValue: 新的值(字符串形式,将被包装在引号中)
|
||||
// jsonStr: 原始 JSON 字符串
|
||||
func ReplaceJSONField(fieldNames, newValue, jsonStr string) string {
|
||||
// 构建正则表达式模式
|
||||
// 匹配模式: ("field1"|"field2"|...): "任意值"
|
||||
pattern := fmt.Sprintf(`(?i)("(%s)")\s*:\s*"([^"]*)"`, fieldNames)
|
||||
re := regexp.MustCompile(pattern)
|
||||
|
||||
// 替换匹配到的内容
|
||||
return re.ReplaceAllString(jsonStr, fmt.Sprintf(`${1}: "%s"`, newValue))
|
||||
}
|
||||
38
stringutil/replace_test.go
Normal file
38
stringutil/replace_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package stringutil
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReplaceJSONField(t *testing.T) {
|
||||
// 测试替换单个字段
|
||||
jsonStr := `{"tenantId": "123", "name": "test"}`
|
||||
result := ReplaceJSONField("tenantId", "456", jsonStr)
|
||||
expected := `{"tenantId": "456", "name": "test"}`
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试替换多个字段
|
||||
jsonStr = `{"tenantId": "123", "tenant_id": "789", "name": "test"}`
|
||||
result = ReplaceJSONField("tenantId|tenant_id", "456", jsonStr)
|
||||
expected = `{"tenantId": "456", "tenant_id": "456", "name": "test"}`
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试字段不存在
|
||||
jsonStr = `{"name": "test"}`
|
||||
result = ReplaceJSONField("tenantId", "456", jsonStr)
|
||||
expected = `{"name": "test"}`
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试空 JSON 字符串
|
||||
jsonStr = ``
|
||||
result = ReplaceJSONField("tenantId", "456", jsonStr)
|
||||
expected = ``
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试空字段名
|
||||
jsonStr = `{"tenantId": "123"}`
|
||||
result = ReplaceJSONField("", "456", jsonStr)
|
||||
expected = `{"tenantId": "123"}`
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
6
tag.bat
6
tag.bat
@@ -1,9 +1,11 @@
|
||||
git tag v1.1.17
|
||||
git tag v1.1.22
|
||||
|
||||
git tag bank_card/v1.1.5
|
||||
git tag geoip/v1.1.5
|
||||
git tag translator/v1.1.1
|
||||
git tag copierutil/v0.0.1
|
||||
|
||||
git tag entgo/v1.1.26
|
||||
git tag entgo/v1.1.27
|
||||
git tag gorm/v1.1.5
|
||||
|
||||
git push origin --tags
|
||||
|
||||
@@ -9,10 +9,20 @@ import (
|
||||
"github.com/tx7do/go-utils/trans"
|
||||
)
|
||||
|
||||
var DefaultTimeLocation *time.Location
|
||||
var defaultTimeLocation *time.Location
|
||||
|
||||
func RefreshDefaultTimeLocation(name string) {
|
||||
DefaultTimeLocation, _ = time.LoadLocation(name)
|
||||
func RefreshDefaultTimeLocation(name string) *time.Location {
|
||||
if defaultTimeLocation == nil {
|
||||
defaultTimeLocation, _ = time.LoadLocation(name)
|
||||
}
|
||||
return defaultTimeLocation
|
||||
}
|
||||
|
||||
func GetDefaultTimeLocation() *time.Location {
|
||||
if defaultTimeLocation == nil {
|
||||
RefreshDefaultTimeLocation(DefaultTimeLocationName)
|
||||
}
|
||||
return defaultTimeLocation
|
||||
}
|
||||
|
||||
// UnixMilliToStringPtr 毫秒时间戳 -> 字符串
|
||||
@@ -26,6 +36,10 @@ func UnixMilliToStringPtr(tm *int64) *string {
|
||||
|
||||
// StringToUnixMilliInt64Ptr 字符串 -> 毫秒时间戳
|
||||
func StringToUnixMilliInt64Ptr(tm *string) *int64 {
|
||||
if tm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
theTime := StringTimeToTime(tm)
|
||||
if theTime == nil {
|
||||
return nil
|
||||
@@ -34,6 +48,46 @@ func StringToUnixMilliInt64Ptr(tm *string) *int64 {
|
||||
return &unixTime
|
||||
}
|
||||
|
||||
// UnixMilliToTimePtr 毫秒时间戳 -> 时间
|
||||
func UnixMilliToTimePtr(tm *int64) *time.Time {
|
||||
if tm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
unixMilli := time.UnixMilli(*tm)
|
||||
return &unixMilli
|
||||
}
|
||||
|
||||
// TimeToUnixMilliInt64Ptr 时间 -> 毫秒时间戳
|
||||
func TimeToUnixMilliInt64Ptr(tm *time.Time) *int64 {
|
||||
if tm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
unixTime := tm.UnixMilli()
|
||||
return &unixTime
|
||||
}
|
||||
|
||||
// UnixSecondToTimePtr 秒时间戳 -> 时间
|
||||
func UnixSecondToTimePtr(tm *int64) *time.Time {
|
||||
if tm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
unixMilli := time.Unix(*tm, 0)
|
||||
return &unixMilli
|
||||
}
|
||||
|
||||
// TimeToUnixSecondInt64Ptr 时间 -> 秒时间戳
|
||||
func TimeToUnixSecondInt64Ptr(tm *time.Time) *int64 {
|
||||
if tm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
unixTime := tm.Unix()
|
||||
return &unixTime
|
||||
}
|
||||
|
||||
// StringTimeToTime 时间字符串 -> 时间
|
||||
func StringTimeToTime(str *string) *time.Time {
|
||||
if str == nil {
|
||||
@@ -43,24 +97,20 @@ func StringTimeToTime(str *string) *time.Time {
|
||||
return nil
|
||||
}
|
||||
|
||||
if DefaultTimeLocation == nil {
|
||||
RefreshDefaultTimeLocation(DefaultTimeLocationName)
|
||||
}
|
||||
|
||||
var err error
|
||||
var theTime time.Time
|
||||
|
||||
theTime, err = time.ParseInLocation(TimeLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(TimeLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
|
||||
theTime, err = time.ParseInLocation(DateLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(DateLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
|
||||
theTime, err = time.ParseInLocation(ClockLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(ClockLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
@@ -85,24 +135,20 @@ func StringDateToTime(str *string) *time.Time {
|
||||
return nil
|
||||
}
|
||||
|
||||
if DefaultTimeLocation == nil {
|
||||
RefreshDefaultTimeLocation(DefaultTimeLocationName)
|
||||
}
|
||||
|
||||
var err error
|
||||
var theTime time.Time
|
||||
|
||||
theTime, err = time.ParseInLocation(TimeLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(TimeLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
|
||||
theTime, err = time.ParseInLocation(DateLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(DateLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
|
||||
theTime, err = time.ParseInLocation(ClockLayout, *str, DefaultTimeLocation)
|
||||
theTime, err = time.ParseInLocation(ClockLayout, *str, GetDefaultTimeLocation())
|
||||
if err == nil {
|
||||
return &theTime
|
||||
}
|
||||
@@ -138,7 +184,16 @@ func FloatToDurationpb(duration *float64, timePrecision time.Duration) *duration
|
||||
if duration == nil {
|
||||
return nil
|
||||
}
|
||||
return durationpb.New(time.Duration(*duration) * timePrecision)
|
||||
return durationpb.New(time.Duration(*duration * float64(timePrecision)))
|
||||
}
|
||||
|
||||
func Float64ToDurationpb(d float64) *durationpb.Duration {
|
||||
duration := time.Duration(d * float64(time.Second))
|
||||
return durationpb.New(duration)
|
||||
}
|
||||
|
||||
func SecondToDurationpb(seconds *float64) *durationpb.Duration {
|
||||
return FloatToDurationpb(seconds, time.Second)
|
||||
}
|
||||
|
||||
func DurationpbToFloat(duration *durationpb.Duration, timePrecision time.Duration) *float64 {
|
||||
@@ -180,3 +235,12 @@ func DurationpbToDuration(duration *durationpb.Duration) *time.Duration {
|
||||
d := duration.AsDuration()
|
||||
return &d
|
||||
}
|
||||
|
||||
func DurationpbSecond(duration *durationpb.Duration) *float64 {
|
||||
if duration == nil {
|
||||
return nil
|
||||
}
|
||||
seconds := duration.AsDuration().Seconds()
|
||||
secondsInt64 := seconds
|
||||
return &secondsInt64
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package timeutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tx7do/go-utils/trans"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
@@ -28,11 +30,152 @@ func TestUnixMilliToStringPtr(t *testing.T) {
|
||||
|
||||
fmt.Println(StringTimeToTime(trans.Ptr("2023-03-08 00:00:00")).UnixMilli())
|
||||
fmt.Println(StringDateToTime(trans.Ptr("2023-03-07")).UnixMilli())
|
||||
|
||||
// 测试有效输入
|
||||
now = time.Now().UnixMilli()
|
||||
result := UnixMilliToStringPtr(&now)
|
||||
assert.NotNil(t, result)
|
||||
expected := time.UnixMilli(now).Format(TimeLayout)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试空输入
|
||||
result = UnixMilliToStringPtr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestStringToUnixMilliInt64Ptr(t *testing.T) {
|
||||
// 测试有效输入
|
||||
input := "2023-03-09 00:00:00"
|
||||
expected := time.Date(2023, 3, 9, 0, 0, 0, 0, GetDefaultTimeLocation()).UnixMilli()
|
||||
result := StringToUnixMilliInt64Ptr(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试无效输入
|
||||
invalidInput := "invalid-date"
|
||||
result = StringToUnixMilliInt64Ptr(&invalidInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空字符串输入
|
||||
emptyInput := ""
|
||||
result = StringToUnixMilliInt64Ptr(&emptyInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空指针输入
|
||||
result = StringToUnixMilliInt64Ptr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestStringTimeToTime(t *testing.T) {
|
||||
// 测试有效时间字符串输入
|
||||
input := "2023-03-09 12:34:56"
|
||||
expected := time.Date(2023, 3, 9, 12, 34, 56, 0, GetDefaultTimeLocation())
|
||||
result := StringTimeToTime(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试有效日期字符串输入
|
||||
input = "2023-03-09"
|
||||
expected = time.Date(2023, 3, 9, 0, 0, 0, 0, GetDefaultTimeLocation())
|
||||
result = StringTimeToTime(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试无效时间字符串输入
|
||||
invalidInput := "invalid-date"
|
||||
result = StringTimeToTime(&invalidInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空字符串输入
|
||||
emptyInput := ""
|
||||
result = StringTimeToTime(&emptyInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空指针输入
|
||||
result = StringTimeToTime(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimeToTimeString(t *testing.T) {
|
||||
// 测试非空输入
|
||||
now := time.Now()
|
||||
result := TimeToTimeString(&now)
|
||||
assert.NotNil(t, result)
|
||||
expected := now.Format(TimeLayout)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimeToTimeString(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestStringDateToTime(t *testing.T) {
|
||||
// 测试有效日期字符串输入
|
||||
input := "2023-03-09"
|
||||
expected := time.Date(2023, 3, 9, 0, 0, 0, 0, GetDefaultTimeLocation())
|
||||
result := StringDateToTime(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试无效日期字符串输入
|
||||
invalidInput := "invalid-date"
|
||||
result = StringDateToTime(&invalidInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空字符串输入
|
||||
emptyInput := ""
|
||||
result = StringDateToTime(&emptyInput)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 测试空指针输入
|
||||
result = StringDateToTime(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimeToDateString(t *testing.T) {
|
||||
fmt.Println(*TimeToTimeString(trans.Time(time.Now())))
|
||||
fmt.Println(*TimeToDateString(trans.Time(time.Now())))
|
||||
|
||||
// 测试非空输入
|
||||
now := time.Now()
|
||||
result := TimeToDateString(&now)
|
||||
assert.NotNil(t, result)
|
||||
expected := now.Format(DateLayout)
|
||||
assert.Equal(t, expected, *result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimeToDateString(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimestamppbToTime(t *testing.T) {
|
||||
// 测试有效输入
|
||||
timestamp := timestamppb.Now()
|
||||
result := TimestamppbToTime(timestamp)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, timestamp.AsTime(), *result)
|
||||
|
||||
// 测试零时间输入
|
||||
zeroTimestamp := timestamppb.New(time.Time{})
|
||||
result = TimestamppbToTime(zeroTimestamp)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, time.Time{}, *result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimestamppbToTime(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimeToTimestamppb(t *testing.T) {
|
||||
// 测试非空输入
|
||||
now := time.Now()
|
||||
result := TimeToTimestamppb(&now)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, timestamppb.New(now), result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimeToTimestamppb(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestDurationpb(t *testing.T) {
|
||||
@@ -56,3 +199,157 @@ func TestDurationpb(t *testing.T) {
|
||||
fmt.Println(NumberToDurationpb(trans.Ptr(100.0), time.Minute).String())
|
||||
fmt.Println(*DurationpbToNumber[float64](durationpb.New(100*time.Minute), time.Minute))
|
||||
}
|
||||
|
||||
func TestFloatToDurationpb(t *testing.T) {
|
||||
// 测试有效输入
|
||||
input := 1.5 // 1.5秒
|
||||
timePrecision := time.Second
|
||||
expected := durationpb.New(1500 * time.Millisecond)
|
||||
result := FloatToDurationpb(&input, timePrecision)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试零输入
|
||||
input = 0.0
|
||||
expected = durationpb.New(0)
|
||||
result = FloatToDurationpb(&input, timePrecision)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试空输入
|
||||
result = FloatToDurationpb(nil, timePrecision)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestDurationToDurationpb(t *testing.T) {
|
||||
// 测试非空输入
|
||||
duration := time.Duration(5 * time.Second)
|
||||
result := DurationToDurationpb(&duration)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, durationpb.New(duration), result)
|
||||
|
||||
// 测试空输入
|
||||
result = DurationToDurationpb(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestDurationpbToDuration(t *testing.T) {
|
||||
// 测试非空输入
|
||||
durationpbValue := durationpb.New(5 * time.Second)
|
||||
result := DurationpbToDuration(durationpbValue)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, 5*time.Second, *result)
|
||||
|
||||
// 测试空输入
|
||||
result = DurationpbToDuration(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestFloat64ToDurationpb(t *testing.T) {
|
||||
// 测试有效输入
|
||||
input := 1.5 // 1.5秒
|
||||
expected := durationpb.New(1500 * time.Millisecond)
|
||||
result := Float64ToDurationpb(input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试零输入
|
||||
input = 0.0
|
||||
expected = durationpb.New(0)
|
||||
result = Float64ToDurationpb(input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试负数输入
|
||||
input = -2.5 // -2.5秒
|
||||
expected = durationpb.New(-2500 * time.Millisecond)
|
||||
result = Float64ToDurationpb(input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func TestSecondToDurationpb(t *testing.T) {
|
||||
// 测试有效输入
|
||||
input := 2.5 // 2.5秒
|
||||
expected := durationpb.New(2500 * time.Millisecond)
|
||||
result := SecondToDurationpb(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试零输入
|
||||
input = 0.0
|
||||
expected = durationpb.New(0)
|
||||
result = SecondToDurationpb(&input)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// 测试空输入
|
||||
result = SecondToDurationpb(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestDurationpbSecond(t *testing.T) {
|
||||
// 测试非空输入
|
||||
duration := durationpb.New(5 * time.Second)
|
||||
result := DurationpbSecond(duration)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, 5.0, *result, "应返回正确的秒数")
|
||||
|
||||
// 测试零输入
|
||||
duration = durationpb.New(0)
|
||||
result = DurationpbSecond(duration)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, 0.0, *result, "应返回零秒")
|
||||
|
||||
// 测试空输入
|
||||
result = DurationpbSecond(nil)
|
||||
assert.Nil(t, result, "空输入应返回nil")
|
||||
}
|
||||
|
||||
func TestUnixMilliToTimePtr(t *testing.T) {
|
||||
// 测试有效输入
|
||||
now := time.Now().UnixMilli()
|
||||
result := UnixMilliToTimePtr(&now)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, time.UnixMilli(now), *result)
|
||||
|
||||
// 测试空输入
|
||||
result = UnixMilliToTimePtr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimeToUnixMilliInt64Ptr(t *testing.T) {
|
||||
// 测试有效输入
|
||||
now := time.Now()
|
||||
result := TimeToUnixMilliInt64Ptr(&now)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, now.UnixMilli(), *result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimeToUnixMilliInt64Ptr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestUnixSecondToTimePtr(t *testing.T) {
|
||||
// 测试有效输入
|
||||
now := time.Now().Unix()
|
||||
result := UnixSecondToTimePtr(&now)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, time.Unix(now, 0), *result)
|
||||
|
||||
// 测试空输入
|
||||
result = UnixSecondToTimePtr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestTimeToUnixSecondInt64Ptr(t *testing.T) {
|
||||
// 测试有效输入
|
||||
now := time.Now()
|
||||
result := TimeToUnixSecondInt64Ptr(&now)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, now.Unix(), *result)
|
||||
|
||||
// 测试空输入
|
||||
result = TimeToUnixSecondInt64Ptr(nil)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
113
translator/README.md
Normal file
113
translator/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 翻译器
|
||||
|
||||
## 目标语言
|
||||
|
||||
1. 阿尔巴尼亚语: `sq`
|
||||
2. 阿拉伯语: `ar`
|
||||
3. 阿姆哈拉语: `am`
|
||||
4. 阿塞拜疆语: `az`
|
||||
5. 爱尔兰语: `ga`
|
||||
6. 爱沙尼亚语: `et`
|
||||
7. 奥利亚语: `or`
|
||||
8. 巴斯克语: `eu`
|
||||
9. 白俄罗斯语: `be`
|
||||
10. 保加利亚语: `bg`
|
||||
11. 冰岛语: `is`
|
||||
12. 波兰语: `pl`
|
||||
13. 波斯尼亚语: `bs`
|
||||
14. 波斯语: `fa`
|
||||
15. 布尔语(南非荷兰语): `af`
|
||||
16. 鞑靼语: `tt`
|
||||
17. 丹麦语: `da`
|
||||
18. 德语: `de`
|
||||
19. 俄语: `ru`
|
||||
20. 法语: `fr`
|
||||
21. 菲律宾语: `tl`
|
||||
22. 芬兰语: `fi`
|
||||
23. 弗里西语: `fy`
|
||||
24. 高棉语: `km`
|
||||
25. 格鲁吉亚语: `ka`
|
||||
26. 古吉拉特语: `gu`
|
||||
27. 哈萨克语: `kk`
|
||||
28. 海地克里奥尔语: `ht`
|
||||
29. 韩语: `ko`
|
||||
30. 豪萨语: `ha`
|
||||
31. 荷兰语: `nl`
|
||||
32. 吉尔吉斯语: `ky`
|
||||
33. 加利西亚语: `gl`
|
||||
34. 加泰罗尼亚语: `ca`
|
||||
35. 捷克语: `cs`
|
||||
36. 卡纳达语: `kn`
|
||||
37. 科西嘉语: `co`
|
||||
38. 克罗地亚语: `hr`
|
||||
39. 库尔德语: `ku`
|
||||
40. 拉丁语: `la`
|
||||
41. 拉脱维亚语: `lv`
|
||||
42. 老挝语: `lo`
|
||||
43. 立陶宛语: `lt`
|
||||
44. 卢森堡语: `lb`
|
||||
45. 卢旺达语: `rw`
|
||||
46. 罗马尼亚语: `ro`
|
||||
47. 马尔加什语: `mg`
|
||||
48. 马耳他语: `mt`
|
||||
49. 马拉地语: `mr`
|
||||
50. 马拉雅拉姆语: `ml`
|
||||
51. 马来语: `ms`
|
||||
52. 马其顿语: `mk`
|
||||
53. 毛利语: `mi`
|
||||
54. 蒙古语: `mn`
|
||||
55. 孟加拉语: `bn`
|
||||
56. 缅甸语: `my`
|
||||
57. 苗语: `hmn`
|
||||
58. 南非科萨语: `xh`
|
||||
59. 南非祖鲁语: `zu`
|
||||
60. 尼泊尔语: `ne`
|
||||
61. 挪威语: `no`
|
||||
62. 旁遮普语: `pa`
|
||||
63. 葡萄牙语: `pt`
|
||||
64. 普什图语: `ps`
|
||||
65. 齐切瓦语: `ny`
|
||||
66. 日语: `ja`
|
||||
67. 瑞典语: `sv`
|
||||
68. 萨摩亚语: `sm`
|
||||
69. 塞尔维亚语: `sr`
|
||||
70. 塞索托语: `st`
|
||||
71. 僧伽罗语: `si`
|
||||
72. 世界语: `eo`
|
||||
73. 斯洛伐克语: `sk`
|
||||
74. 斯洛文尼亚语: `sl`
|
||||
75. 斯瓦希里语: `sw`
|
||||
76. 苏格兰盖尔语: `gd`
|
||||
77. 宿务语: `ceb`
|
||||
78. 索马里语: `so`
|
||||
79. 塔吉克语: `tg`
|
||||
80. 泰卢固语: `te`
|
||||
81. 泰米尔语: `ta`
|
||||
82. 泰语: `th`
|
||||
83. 土耳其语: `tr`
|
||||
84. 土库曼语: `tk`
|
||||
85. 威尔士语: `cy`
|
||||
86. 维吾尔语: `ug`
|
||||
87. 乌尔都语: `ur`
|
||||
88. 乌克兰语: `uk`
|
||||
89. 乌兹别克语: `uz`
|
||||
90. 西班牙语: `es`
|
||||
91. 希伯来语: `iw`
|
||||
92. 希腊语: `el`
|
||||
93. 夏威夷语: `haw`
|
||||
94. 信德语: `sd`
|
||||
95. 匈牙利语: `hu`
|
||||
96. 修纳语: `sn`
|
||||
97. 亚美尼亚语: `hy`
|
||||
98. 伊博语: `ig`
|
||||
99. 意大利语: `it`
|
||||
100. 意第绪语: `yi`
|
||||
101. 印地语: `hi`
|
||||
102. 印尼巽他语: `su`
|
||||
103. 印尼语: `id`
|
||||
104. 印尼爪哇语: `jw`
|
||||
105. 英语: `en`
|
||||
106. 约鲁巴语: `yo`
|
||||
107. 越南语: `vi`
|
||||
108. 中文(繁体): `zh-TW`
|
||||
109. 中文(简体): `zh-CN`
|
||||
49
translator/go.mod
Normal file
49
translator/go.mod
Normal file
@@ -0,0 +1,49 @@
|
||||
module github.com/tx7do/go-utils/translator
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
cloud.google.com/go/translate v1.12.5
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/text v0.25.0
|
||||
google.golang.org/api v0.233.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/grpc v1.72.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/tx7do/go-utils => ../
|
||||
90
translator/go.sum
Normal file
90
translator/go.sum
Normal file
@@ -0,0 +1,90 @@
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
|
||||
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
|
||||
cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
|
||||
cloud.google.com/go/translate v1.12.5 h1:QPMNi4WCtHwc2PPfxbyUMwdN/0+cyCGLaKi2tig41J8=
|
||||
cloud.google.com/go/translate v1.12.5/go.mod h1:o/v+QG/bdtBV1d1edmtau0PwTfActvxPk/gtqdSDBi4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI=
|
||||
google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
20
translator/google/options.go
Normal file
20
translator/google/options.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package google
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
type options struct {
|
||||
version string
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func WithVersion(version string) Option {
|
||||
return func(o *options) {
|
||||
o.version = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithApiKey(key string) Option {
|
||||
return func(o *options) {
|
||||
o.apiKey = key
|
||||
}
|
||||
}
|
||||
40
translator/google/translator.go
Normal file
40
translator/google/translator.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
translateV2 "cloud.google.com/go/translate"
|
||||
translateV3 "cloud.google.com/go/translate/apiv3"
|
||||
)
|
||||
|
||||
type Translator struct {
|
||||
options *options
|
||||
|
||||
clientV2 *translateV2.Client
|
||||
clientV3 *translateV3.TranslationClient
|
||||
}
|
||||
|
||||
func NewTranslator(opts ...Option) *Translator {
|
||||
op := options{}
|
||||
for _, o := range opts {
|
||||
o(&op)
|
||||
}
|
||||
|
||||
return &Translator{
|
||||
options: &op,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) Translate(source, sourceLang, targetLang string) (string, error) {
|
||||
switch t.options.version {
|
||||
default:
|
||||
fallthrough
|
||||
case "v1":
|
||||
return t.TranslateV1(source, sourceLang, targetLang)
|
||||
|
||||
case "v2":
|
||||
text, _, err := t.TranslateV2(source, sourceLang, targetLang)
|
||||
return text, err
|
||||
|
||||
case "v3":
|
||||
return t.TranslateV3(source, sourceLang, targetLang)
|
||||
}
|
||||
}
|
||||
98
translator/google/translator_test.go
Normal file
98
translator/google/translator_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestTranslateV1(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v1"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
// you can use "auto" for source language
|
||||
// so, translator will detect language
|
||||
result, err := translator.TranslateV1(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV1(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestTranslateV2(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v2"),
|
||||
WithApiKey("apikey"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
|
||||
result, _, err := translator.TranslateV2(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, _, err = translator.TranslateV2(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestTranslateV3(t *testing.T) {
|
||||
translator := NewTranslator(
|
||||
WithVersion("v3"),
|
||||
WithApiKey("apikey"),
|
||||
)
|
||||
assert.NotNil(t, translator)
|
||||
|
||||
const text string = `Hello, World!`
|
||||
// you can use "auto" for source language
|
||||
// so, translator will detect language
|
||||
result, err := translator.TranslateV3(text, "en", "es")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
// Output: "Hola, Mundo!"
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "zh-CN")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "lo")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
|
||||
result, err = translator.TranslateV3(text, "en", "my")
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func TestLanguageParse(t *testing.T) {
|
||||
lang, _ := language.Parse("en")
|
||||
assert.Equal(t, lang, language.English)
|
||||
fmt.Println(lang)
|
||||
fmt.Println(language.English.String())
|
||||
}
|
||||
9
translator/google/utils.go
Normal file
9
translator/google/utils.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package google
|
||||
|
||||
import "net/url"
|
||||
|
||||
// javascript "encodeURI()"
|
||||
// so we embed js to our golang program
|
||||
func encodeURI(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
55
translator/google/v1.go
Normal file
55
translator/google/v1.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV1(source, sourceLang, targetLang string) (string, error) {
|
||||
var text []string
|
||||
var result []interface{}
|
||||
|
||||
encodedSource := encodeURI(source)
|
||||
uri := "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" +
|
||||
sourceLang + "&tl=" + targetLang + "&dt=t&q=" + encodedSource
|
||||
|
||||
r, err := http.Get(uri)
|
||||
if err != nil {
|
||||
return "err", errors.New("error getting translate.googleapis.com")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "err", errors.New("error reading response body")
|
||||
}
|
||||
|
||||
bReq := strings.Contains(string(body), `<title>Error 400 (Bad Request)`)
|
||||
if bReq {
|
||||
return "err", errors.New("error 400 (Bad Request)")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &result)
|
||||
if err != nil {
|
||||
return "err", errors.New("error unmarshalling data")
|
||||
}
|
||||
|
||||
if len(result) > 0 {
|
||||
inner := result[0]
|
||||
for _, slice := range inner.([]interface{}) {
|
||||
for _, translatedText := range slice.([]interface{}) {
|
||||
text = append(text, fmt.Sprintf("%v", translatedText))
|
||||
break
|
||||
}
|
||||
}
|
||||
cText := strings.Join(text, "")
|
||||
|
||||
return cText, nil
|
||||
} else {
|
||||
return "err", errors.New("no translated data in response")
|
||||
}
|
||||
}
|
||||
33
translator/google/v2.go
Normal file
33
translator/google/v2.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
translateV2 "cloud.google.com/go/translate"
|
||||
"golang.org/x/text/language"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV2(source, _, targetLanguage string) (string, language.Tag, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
lang, err := language.Parse(targetLanguage)
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
|
||||
if t.clientV2 == nil {
|
||||
client, err := translateV2.NewClient(ctx, option.WithAPIKey(t.options.apiKey))
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
t.clientV2 = client
|
||||
}
|
||||
|
||||
resp, err := t.clientV2.Translate(ctx, []string{source}, lang, nil)
|
||||
if err != nil {
|
||||
return "", language.English, err
|
||||
}
|
||||
|
||||
return resp[0].Text, resp[0].Source, nil
|
||||
}
|
||||
40
translator/google/v3.go
Normal file
40
translator/google/v3.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
translateV3 "cloud.google.com/go/translate/apiv3"
|
||||
"cloud.google.com/go/translate/apiv3/translatepb"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
func (t *Translator) TranslateV3(source, sourceLang, targetLang string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
if t.clientV3 == nil {
|
||||
client, err := translateV3.NewTranslationClient(ctx, option.WithAPIKey(t.options.apiKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NewTranslationClient: %w", err)
|
||||
}
|
||||
t.clientV3 = client
|
||||
}
|
||||
|
||||
req := &translatepb.TranslateTextRequest{
|
||||
SourceLanguageCode: sourceLang,
|
||||
TargetLanguageCode: targetLang,
|
||||
MimeType: "text/plain", // Mime types: "text/plain", "text/html"
|
||||
Contents: []string{source},
|
||||
}
|
||||
|
||||
resp, err := t.clientV3.TranslateText(ctx, req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("TranslateText: %w", err)
|
||||
}
|
||||
|
||||
if len(resp.GetTranslations()) != 1 {
|
||||
return "", fmt.Errorf("TranslateText: %w", err)
|
||||
}
|
||||
|
||||
return resp.GetTranslations()[0].GetTranslatedText(), nil
|
||||
}
|
||||
7
translator/translator.go
Normal file
7
translator/translator.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package translator
|
||||
|
||||
// Translator 翻译器
|
||||
type Translator interface {
|
||||
// Translate 翻译
|
||||
Translate(source, sourceLang, targetLang string) (string, error)
|
||||
}
|
||||
9
translator/translator_test.go
Normal file
9
translator/translator_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package translator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTranslator(t *testing.T) {
|
||||
|
||||
}
|
||||
16
upgrade.bat
16
upgrade.bat
@@ -10,10 +10,22 @@ cd %DIR%/bank_card
|
||||
go get all
|
||||
go mod tidy
|
||||
|
||||
cd %DIR%/geoip
|
||||
go get all
|
||||
go mod tidy
|
||||
|
||||
cd %DIR%/copierutil
|
||||
go get all
|
||||
go mod tidy
|
||||
|
||||
cd %DIR%/translator
|
||||
go get all
|
||||
go mod tidy
|
||||
|
||||
cd %DIR%/entgo
|
||||
go get all
|
||||
go mod tidy
|
||||
|
||||
cd %DIR%/geoip
|
||||
cd %DIR%/gorm
|
||||
go get all
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
Reference in New Issue
Block a user