From 3b2678de10df208a2dde9a789314d32011082972 Mon Sep 17 00:00:00 2001 From: Bobo Date: Wed, 14 May 2025 20:43:37 +0800 Subject: [PATCH] feat: translator --- tag.bat | 1 + translator/README.md | 113 +++++++++++++++++++++++++++ translator/go.mod | 49 ++++++++++++ translator/go.sum | 90 +++++++++++++++++++++ translator/google/options.go | 20 +++++ translator/google/translator.go | 40 ++++++++++ translator/google/translator_test.go | 98 +++++++++++++++++++++++ translator/google/utils.go | 9 +++ translator/google/v1.go | 55 +++++++++++++ translator/google/v2.go | 33 ++++++++ translator/google/v3.go | 40 ++++++++++ translator/translator.go | 7 ++ translator/translator_test.go | 9 +++ 13 files changed, 564 insertions(+) create mode 100644 translator/README.md create mode 100644 translator/go.mod create mode 100644 translator/go.sum create mode 100644 translator/google/options.go create mode 100644 translator/google/translator.go create mode 100644 translator/google/translator_test.go create mode 100644 translator/google/utils.go create mode 100644 translator/google/v1.go create mode 100644 translator/google/v2.go create mode 100644 translator/google/v3.go create mode 100644 translator/translator.go create mode 100644 translator/translator_test.go diff --git a/tag.bat b/tag.bat index a3e8135..3b87a99 100644 --- a/tag.bat +++ b/tag.bat @@ -2,6 +2,7 @@ git tag v1.1.21 git tag bank_card/v1.1.5 git tag geoip/v1.1.5 +git tag translator/v1.1.1 git tag entgo/v1.1.27 git tag gorm/v1.1.5 diff --git a/translator/README.md b/translator/README.md new file mode 100644 index 0000000..00c379a --- /dev/null +++ b/translator/README.md @@ -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` diff --git a/translator/go.mod b/translator/go.mod new file mode 100644 index 0000000..507f943 --- /dev/null +++ b/translator/go.mod @@ -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 => ../ diff --git a/translator/go.sum b/translator/go.sum new file mode 100644 index 0000000..c3dfe8b --- /dev/null +++ b/translator/go.sum @@ -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= diff --git a/translator/google/options.go b/translator/google/options.go new file mode 100644 index 0000000..372d91a --- /dev/null +++ b/translator/google/options.go @@ -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 + } +} diff --git a/translator/google/translator.go b/translator/google/translator.go new file mode 100644 index 0000000..c50b2e2 --- /dev/null +++ b/translator/google/translator.go @@ -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) + } +} diff --git a/translator/google/translator_test.go b/translator/google/translator_test.go new file mode 100644 index 0000000..8759850 --- /dev/null +++ b/translator/google/translator_test.go @@ -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()) +} diff --git a/translator/google/utils.go b/translator/google/utils.go new file mode 100644 index 0000000..053b506 --- /dev/null +++ b/translator/google/utils.go @@ -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) +} diff --git a/translator/google/v1.go b/translator/google/v1.go new file mode 100644 index 0000000..05fecb5 --- /dev/null +++ b/translator/google/v1.go @@ -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), `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") + } +} diff --git a/translator/google/v2.go b/translator/google/v2.go new file mode 100644 index 0000000..516b607 --- /dev/null +++ b/translator/google/v2.go @@ -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 +} diff --git a/translator/google/v3.go b/translator/google/v3.go new file mode 100644 index 0000000..289b60e --- /dev/null +++ b/translator/google/v3.go @@ -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 +} diff --git a/translator/translator.go b/translator/translator.go new file mode 100644 index 0000000..25f0566 --- /dev/null +++ b/translator/translator.go @@ -0,0 +1,7 @@ +package translator + +// Translator 翻译器 +type Translator interface { + // Translate 翻译 + Translate(source, sourceLang, targetLang string) (string, error) +} diff --git a/translator/translator_test.go b/translator/translator_test.go new file mode 100644 index 0000000..c4889b4 --- /dev/null +++ b/translator/translator_test.go @@ -0,0 +1,9 @@ +package translator + +import ( + "testing" +) + +func TestTranslator(t *testing.T) { + +}