go cli translator that use bing api and youdao api

first I am going to teach you how to develop a go package

sh 复制代码
git clone <your own repo>
mkdir mstrans-cli && cd mstrans-cli
go mod init <your repo url as package id>
touch mstrans.go lang.go lang.json
  • lang.json
json 复制代码
{
  "af": "Afrikaans",
  "am": "Amharic",
  "ar": "Arabic",
  "as": "Assamese",
  "az": "Azerbaijani",
  "ba": "Bashkir",
  "be": "Belarusian",
  "bg": "Bulgarian",
  "bho": "Bhojpuri",
  "bn": "Bangla",
  "bo": "Tibetan",
  "brx": "Bodo",
  "bs": "Bosnian",
  "ca": "Catalan",
  "cs": "Czech",
  "cy": "Welsh",
  "da": "Danish",
  "de": "German",
  "doi": "Dogri",
  "dsb": "Lower Sorbian",
  "dv": "Divehi",
  "el": "Greek",
  "en": "English",
  "es": "Spanish",
  "et": "Estonian",
  "eu": "Basque",
  "fa": "Persian",
  "fi": "Finnish",
  "fil": "Filipino",
  "fj": "Fijian",
  "fo": "Faroese",
  "fr": "French",
  "fr-CA": "French (Canada)",
  "ga": "Irish",
  "gl": "Galician",
  "gom": "Konkani",
  "gu": "Gujarati",
  "ha": "Hausa",
  "he": "Hebrew",
  "hi": "Hindi",
  "hne": "Chhattisgarhi",
  "hr": "Croatian",
  "hsb": "Upper Sorbian",
  "ht": "Haitian Creole",
  "hu": "Hungarian",
  "hy": "Armenian",
  "id": "Indonesian",
  "ig": "Igbo",
  "ikt": "Inuinnaqtun",
  "is": "Icelandic",
  "it": "Italian",
  "iu": "Inuktitut",
  "iu-Latn": "Inuktitut (Latin)",
  "ja": "Japanese",
  "ka": "Georgian",
  "kk": "Kazakh",
  "km": "Khmer",
  "kmr": "Kurdish (Northern)",
  "kn": "Kannada",
  "ko": "Korean",
  "ks": "Kashmiri",
  "ku": "Kurdish (Central)",
  "ky": "Kyrgyz",
  "lb": "Luxembourgish",
  "ln": "Lingala",
  "lo": "Lao",
  "lt": "Lithuanian",
  "lug": "Ganda",
  "lv": "Latvian",
  "lzh": "Chinese (Literary)",
  "mai": "Maithili",
  "mg": "Malagasy",
  "mi": "Māori",
  "mk": "Macedonian",
  "ml": "Malayalam",
  "mn-Cyrl": "Mongolian (Cyrillic)",
  "mn-Mong": "Mongolian (Traditional)",
  "mni": "Manipuri",
  "mr": "Marathi",
  "ms": "Malay",
  "mt": "Maltese",
  "mww": "Hmong Daw",
  "my": "Myanmar (Burmese)",
  "nb": "Norwegian",
  "ne": "Nepali",
  "nl": "Dutch",
  "nso": "Sesotho sa Leboa",
  "nya": "Nyanja",
  "or": "Odia",
  "otq": "Querétaro Otomi",
  "pa": "Punjabi",
  "pl": "Polish",
  "prs": "Dari",
  "ps": "Pashto",
  "pt": "Portuguese (Brazil)",
  "pt-PT": "Portuguese (Portugal)",
  "ro": "Romanian",
  "ru": "Russian",
  "run": "Rundi",
  "rw": "Kinyarwanda",
  "sd": "Sindhi",
  "si": "Sinhala",
  "sk": "Slovak",
  "sl": "Slovenian",
  "sm": "Samoan",
  "sn": "Shona",
  "so": "Somali",
  "sq": "Albanian",
  "sr-Cyrl": "Serbian (Cyrillic)",
  "sr-Latn": "Serbian (Latin)",
  "st": "Sesotho",
  "sv": "Swedish",
  "sw": "Swahili",
  "ta": "Tamil",
  "te": "Telugu",
  "th": "Thai",
  "ti": "Tigrinya",
  "tk": "Turkmen",
  "tlh-Latn": "Klingon (Latin)",
  "tlh-Piqd": "Klingon (pIqaD)",
  "tn": "Setswana",
  "to": "Tongan",
  "tr": "Turkish",
  "tt": "Tatar",
  "ty": "Tahitian",
  "ug": "Uyghur",
  "uk": "Ukrainian",
  "ur": "Urdu",
  "uz": "Uzbek (Latin)",
  "vi": "Vietnamese",
  "xh": "Xhosa",
  "yo": "Yoruba",
  "yua": "Yucatec Maya",
  "yue": "Cantonese (Traditional)",
  "zh-Hans": "Chinese Simplified",
  "zh-Hant": "Chinese Traditional",
  "zu": "Zulu"
}
  • lang.go
go 复制代码
// lang.go
package mstrans

import (
	_ "embed"
	"encoding/json"
	"strings"
)

//go:embed all:lang.json
var langJSON []byte

// LangMap maps language codes to names (e.g., "zh-Hans": "Chinese Simplified")
var LangMap map[string]string

func init() {
	// Load language map from embedded JSON (runs once at package init)
	if err := json.Unmarshal(langJSON, &LangMap); err != nil {
		panic("failed to load language map: " + err.Error())
	}
}

// GetLangCode normalizes language input (e.g., "zh" → "zh-Hans", "english" → "en")
func GetLangCode(lang string) string {
	if lang == "" || lang == "auto-detect" {
		return "" // Auto-detect maps to empty from parameter
	}

	// Exact match (e.g., "zh-Hans" → "zh-Hans")
	if _, ok := LangMap[lang]; ok {
		return lang
	}

	// Case-insensitive match (e.g., "ZH" → "zh-Hans", "english" → "en")
	lowerLang := strings.ToLower(lang)
	for code, name := range LangMap {
		if strings.ToLower(code) == lowerLang || strings.ToLower(name) == lowerLang {
			return code
		}
	}

	return "" // Unsupported language
}

// IsLangSupported checks if a language code/name is supported
func IsLangSupported(lang string) bool {
	return GetLangCode(lang) != ""
}
  • mstrans.go
go 复制代码
// mstrans.go
package mstrans

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

// DefaultUserAgent matches the JS default (ensures compatibility with Microsoft's API)
const (
	DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
	apiAuthURL       = "https://edge.microsoft.com/translate/auth"
	apiTranslateURL  = "https://api.cognitive.microsofttranslator.com/translate"
)

// Client is the main struct for Microsoft Translator (thread-safe)
type Client struct {
	client      *http.Client
	userAgent   string
	mu          sync.Mutex // For token cache thread safety
	token       string
	tokenExpiry time.Time
	ManualToken string // 手动设置的 Token(优先级高于自动获取)

}

// TranslationResult represents a single translation result (matches MS API response)
type TranslationResult struct {
	DetectedLanguage struct {
		Language string  `json:"language"`
		Score    float64 `json:"score"`
	} `json:"detectedLanguage,omitempty"`
	Translations []struct {
		Text            string `json:"text"`
		To              string `json:"to"`
		Transliteration struct {
			Text string `json:"text,omitempty"`
		} `json:"transliteration,omitempty"`
	} `json:"translations"`
}

// NewClient creates a new Translator client (custom timeout optional)
func NewClient(timeout ...time.Duration) *Client {
	// Default timeout: 10 seconds
	clientTimeout := 10 * time.Second
	if len(timeout) > 0 {
		clientTimeout = timeout[0]
	}

	return &Client{
		client: &http.Client{
			Timeout: clientTimeout,
		},
		userAgent: DefaultUserAgent,
	}
}

// SetManualToken 方法,用于手动设置 Token
func (c *Client) SetManualToken(token string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.ManualToken = token
	// 手动设置 Token 时,临时设置 expiry 为 1 小时后(避免被自动刷新)
	c.tokenExpiry = time.Now().Add(1 * time.Hour)
}

// SetUserAgent overrides the default User-Agent (for customization)
func (c *Client) SetUserAgent(ua string) {
	c.userAgent = ua
}

// fetchToken gets a new JWT token from Microsoft (caches it for ~10 minutes)
func (c *Client) fetchToken() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	// 优先使用手动设置的 Token
	if c.ManualToken != "" {
		c.token = c.ManualToken
		return nil
	}

	// 原有自动获取逻辑(保留,供网络正常时使用)
	if !c.tokenExpiry.IsZero() && time.Until(c.tokenExpiry) > 1*time.Minute {
		return nil
	}

	req, err := http.NewRequest("GET", apiAuthURL, nil)
	if err != nil {
		return fmt.Errorf("create auth request: %w", err)
	}
	req.Header.Set("User-Agent", c.userAgent)

	resp, err := c.client.Do(req)
	if err != nil {
		return fmt.Errorf("fetch token: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("auth request failed: %s", resp.Status)
	}

	var tokenBytes bytes.Buffer
	if _, err := tokenBytes.ReadFrom(resp.Body); err != nil {
		return fmt.Errorf("read token: %w", err)
	}
	tokenStr := tokenBytes.String()

	parts := strings.Split(tokenStr, ".")
	if len(parts) < 2 {
		return fmt.Errorf("invalid JWT token format")
	}

	payloadBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return fmt.Errorf("decode JWT payload: %w", err)
	}

	var payload struct {
		Exp int64 `json:"exp"`
	}
	if err := json.Unmarshal(payloadBytes, &payload); err != nil {
		return fmt.Errorf("parse JWT payload: %w", err)
	}

	c.token = tokenStr
	c.tokenExpiry = time.Unix(payload.Exp, 0)

	return nil
}

// Translate is the core API: translates text (single/multiple) between languages
// - text: string or []string (content to translate)
// - from: source language (use "" or "auto-detect" for auto-detection)
// - to: target language (e.g., "zh-Hans", "en")
func (c *Client) Translate(text any, from, to string) ([]TranslationResult, error) {
	// Step 1: Validate inputs
	fromCode := GetLangCode(from)
	toCode := GetLangCode(to)

	if !IsLangSupported(toCode) {
		return nil, fmt.Errorf("unsupported target language: %s", to)
	}

	// Convert text to []string (supports single string or slice)
	var textSlice []string
	switch v := text.(type) {
	case string:
		if v == "" {
			return nil, fmt.Errorf("text cannot be empty")
		}
		textSlice = []string{v}
	case []string:
		if len(v) == 0 || (len(v) == 1 && v[0] == "") {
			return nil, fmt.Errorf("text slice cannot be empty")
		}
		textSlice = v
	default:
		return nil, fmt.Errorf("text must be string or []string (got %T)", text)
	}

	// Step 2: Ensure valid token
	if err := c.fetchToken(); err != nil {
		return nil, fmt.Errorf("get token: %w", err)
	}

	// Step 3: Prepare translation request payload
	payload := make([]map[string]string, len(textSlice))
	for i, t := range textSlice {
		payload[i] = map[string]string{"Text": t}
	}
	payloadBytes, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("marshal payload: %w", err)
	}

	// Step 4: Build translation URL with query params
	query := url.Values{}
	query.Set("api-version", "3.0")
	if fromCode != "" {
		query.Set("from", fromCode)
	}
	query.Set("to", toCode)

	translateURL := fmt.Sprintf("%s?%s", apiTranslateURL, query.Encode())

	// Step 5: Create POST request
	req, err := http.NewRequest("POST", translateURL, bytes.NewReader(payloadBytes))
	if err != nil {
		return nil, fmt.Errorf("create translate request: %w", err)
	}

	// Set required headers
	req.Header.Set("User-Agent", c.userAgent)
	req.Header.Set("Authorization", "Bearer "+c.token)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	// Step 6: Send request and parse response
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("send translate request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// Read error details for debugging
		var errBody bytes.Buffer
		_, _ = errBody.ReadFrom(resp.Body)
		return nil, fmt.Errorf("translation failed: %s (body: %s)", resp.Status, errBody.String())
	}

	// Step 7: Parse translation result
	var results []TranslationResult
	if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
		return nil, fmt.Errorf("parse translation response: %w", err)
	}

	return results, nil
}

// TranslateSimple is a simplified API for single-text translation (returns only translated text)
func (c *Client) TranslateSimple(text, from, to string) (string, error) {
	results, err := c.Translate(text, from, to)
	if err != nil {
		return "", err
	}

	if len(results) == 0 || len(results[0].Translations) == 0 {
		return "", fmt.Errorf("no translation result")
	}

	return results[0].Translations[0].Text, nil
}

now use this package

bash 复制代码
mkdir mstrans-cli && cd mstrans-cli
go mod init mstrans-cli
# use your own repo by following example code
GOPRIVATE=gitee.com/EEPPEE_admin/mstrans
go get gitee.com/EEPPEE_admin/mstrans

touch main.go
  • main.go
go 复制代码
package main

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
	// TODO: should change to your own repo
	"gitee.com/EEPPEE_admin/mstrans"

	"github.com/atotto/clipboard"
	"github.com/spf13/cobra"
)

// 程序版本信息
const version = "v0.0.1"

// 全局CLI参数
var (
	fromLang    string
	toLang      string
	manualToken string
	timeout     int // 超时时间(秒)
)

// startProgress 启动翻译进度动画
func startProgress() chan struct{} {
	stop := make(chan struct{})
	chars := []rune{'|', '/', '-', '\\'}
	go func() {
		i := 0
		for {
			select {
			case <-stop:
				// 清除进度行
				fmt.Printf("\r\033[K")
				return
			default:
				fmt.Printf("\r\033[34m[正在翻译]\033[0m %c", chars[i%4])
				i++
				time.Sleep(100 * time.Millisecond)
			}
		}
	}()
	return stop
}

// truncateText 截断长文本(用于剪贴板预览)
func truncateText(text string, maxLen int) string {
	runes := []rune(text)
	if len(runes) <= maxLen {
		return text
	}
	return string(runes[:maxLen]) + "..."
}

// readInput 读取输入(优先级:命令行参数 > 剪贴板 > 标准输入)
func readInput(args []string) (string, error) {
	// 1. 优先读取命令行参数
	if len(args) > 0 {
		return strings.Join(args, " "), nil
	}

	// 2. 读取剪贴板内容
	clipboardText, err := clipboard.ReadAll()
	if err == nil && strings.TrimSpace(clipboardText) != "" {
		fmt.Printf("\033[36m[提示]\033[0m 已从剪贴板读取文本:%s\n", truncateText(clipboardText, 50))
		return strings.TrimSpace(clipboardText), nil
	}

	// 3. 读取标准输入
	fmt.Println("\033[36m[提示]\033[0m 剪贴板为空,读取标准输入(按Ctrl+D结束):")
	bytes, err := io.ReadAll(os.Stdin)
	if err != nil {
		return "", fmt.Errorf("读取标准输入失败: %w", err)
	}
	inputText := strings.TrimSpace(string(bytes))
	inputText = strings.ReplaceAll(inputText, "\n", " ")

	if inputText == "" {
		return "", fmt.Errorf("输入文本不能为空")
	}
	return inputText, nil
}

// printTranslationResult 格式化输出翻译结果
func printTranslationResult(inputText string, results []mstrans.TranslationResult) {
	if len(results) == 0 {
		fmt.Printf("\033[31m[错误]\033[0m 未获取到翻译结果\n")
		return
	}

	// 遍历结果(支持多文本翻译)
	for i, result := range results {
		// 显示检测到的源语言(如果是自动检测)
		if result.DetectedLanguage.Language != "" {
			fmt.Printf("\033[35m[检测语言]\033[0m %s (置信度: %.2f)\n",
				result.DetectedLanguage.Language, result.DetectedLanguage.Score)
		}

		// 原文(多文本时显示序号)
		if len(results) > 1 {
			fmt.Printf("\033[34m[原文 %d]\033[0m %s\n", i+1, inputText)
		} else {
			fmt.Printf("\033[34m[原文]\033[0m %s\n", inputText)
		}

		// 翻译结果
		for _, trans := range result.Translations {
			fmt.Printf("\033[32m[翻译]\033[0m %s\n", trans.Text)
			// 显示音译(如果有)
			if trans.Transliteration.Text != "" {
				fmt.Printf("\033[36m[音译]\033[0m %s\n", trans.Transliteration.Text)
			}
		}
		fmt.Println()
	}
}

// 根命令
var rootCmd = &cobra.Command{
	Use:   "mstrans-cli",
	Short: "微软翻译CLI工具 - 轻量、高效的命令行翻译工具",
	Long: `微软翻译CLI工具 (mstrans-cli)
基于微软Edge翻译API实现,支持多语言互译
核心功能:
  1. 自动/手动指定源语言/目标语言
  2. 支持手动设置认证Token
  3. 智能输入:命令行参数 > 剪贴板 > 标准输入
  4. 翻译进度实时显示
  5. 自动检测源语言+置信度展示
  6. 支持音译结果输出

支持的语言码示例:
  中文(简): zh-Hans, 英文: en, 日语: ja, 韩语: ko, 法语: fr
  德语: de, 俄语: ru, 西班牙语: es, 葡萄牙语: pt`,
	Version: version,
	Run: func(cmd *cobra.Command, args []string) {
		// 处理中断信号(优雅停止进度动画)
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
		var wg sync.WaitGroup
		wg.Add(1)
		go func() {
			<-sigChan
			fmt.Printf("\r\033[K\033[31m[中断]\033[0m 翻译请求已取消\n")
			os.Exit(1)
		}()
		defer wg.Done()

		// 1. 读取输入文本
		inputText, err := readInput(args)
		if err != nil {
			fmt.Printf("\033[31m[错误]\033[0m %v\n", err)
			os.Exit(1)
		}

		// 2. 初始化微软翻译客户端
		clientTimeout := time.Duration(timeout) * time.Second
		client := mstrans.NewClient(clientTimeout)

		// 设置手动Token(如果指定)
		if manualToken != "" {
			client.SetManualToken(manualToken)
			fmt.Printf("\033[36m[提示]\033[0m 已使用手动指定的认证Token\n")
		}

		// 3. 处理语言参数(标准化)
		fromCode := mstrans.GetLangCode(fromLang)
		toCode := mstrans.GetLangCode(toLang)

		// 验证目标语言
		if !mstrans.IsLangSupported(toCode) {
			fmt.Printf("\033[31m[错误]\033[0m 不支持的目标语言:%s\n", toLang)
			os.Exit(1)
		}

		// 4. 执行翻译(带进度显示)
		stopProgress := startProgress()
		results, err := client.Translate(inputText, fromCode, toCode)
		close(stopProgress) // 停止进度动画
		if err != nil {
			fmt.Printf("\033[31m[错误]\033[0m 翻译失败: %v\n", err)
			os.Exit(1)
		}

		// 5. 格式化输出结果
		printTranslationResult(inputText, results)
	},
}

// translate子命令(显式翻译,和根命令逻辑一致)
var translateCmd = &cobra.Command{
	Use:   "translate [文本]",
	Short: "翻译指定文本(核心功能)",
	Args:  cobra.MinimumNArgs(0), // 允许无参数(从剪贴板/标准输入读)
	Run: func(cmd *cobra.Command, args []string) {
		rootCmd.Run(cmd, args)
	},
}

func init() {
	// 全局参数配置
	rootCmd.PersistentFlags().StringVarP(&fromLang, "from", "f", "auto-detect",
		"源语言(auto-detect=自动检测,支持码/名称:zh-Hans, en, 中文, english)")
	rootCmd.PersistentFlags().StringVarP(&toLang, "to", "t", "zh-Hans",
		"目标语言(默认:zh-Hans,支持码/名称:en, ja, 英文, 日语)")
	rootCmd.PersistentFlags().StringVarP(&manualToken, "token", "k", "",
		"手动设置认证Token(优先级高于自动获取)")
	rootCmd.PersistentFlags().IntVarP(&timeout, "timeout", "o", 10,
		"请求超时时间(秒,默认:10)")

	// 添加子命令
	rootCmd.AddCommand(translateCmd)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Printf("\033[31m[错误]\033[0m 执行失败: %v\n", err)
		os.Exit(1)
	}
}
  • so now, you maybe get some idea from this article

for youdao translate api

相关推荐
Ronin3051 小时前
【Qt常用控件】按钮类控件
开发语言·qt·常用控件·按钮类控件
慧都小项1 小时前
JAVA开发工具IntelliJ IDEA v2026更新前瞻:更优的交互视觉,编程体验升级
java·开发语言·intellij-idea
prince_zxill1 小时前
Raspberry PI传感器数据上云:Python IoT集成
开发语言·python·物联网
亚历山大海1 小时前
AiPPT接口文件PHP版本全,智能生成PPT文件并下载
开发语言·ai·php
ノBye~1 小时前
Spring的IOC详解
java·开发语言
147API1 小时前
Claude 模型选型:Opus/Sonnet/Haiku + 成本/限速预算(Kotlin)
android·开发语言·kotlin·147api
电商API_180079052472 小时前
企业级应用:京东商品详情 API 的高可用架构与多级缓存设计
开发语言·人工智能·python·数据分析·网络爬虫·php
MoonBit月兔2 小时前
MoonBit 0.8.3版本更新
开发语言·人工智能·算法·ai编程·moonbit