Skip to content

Commit be407bd

Browse files
committed
feat: Git hook support for translating git messages into English
--story=1
1 parent bb96865 commit be407bd

File tree

4 files changed

+146
-14
lines changed

4 files changed

+146
-14
lines changed

cmd/codei18n/hook.go

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ func init() {
3636
}
3737

3838
func runHookInstall() {
39-
if err := installHook(); err != nil {
40-
log.Fatal("安装 hook 失败: %v", err)
39+
if err := installPreCommitHook(); err != nil {
40+
log.Fatal("安装 pre-commit hook 失败: %v", err)
4141
}
42-
log.Success("Hook 安装成功")
42+
if err := installCommitMsgHook(); err != nil {
43+
log.Fatal("安装 commit-msg hook 失败: %v", err)
44+
}
45+
log.Success("Git Hooks 安装成功")
4346
}
4447

45-
func installHook() error {
48+
func installPreCommitHook() error {
4649
gitDir := ".git"
4750
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
4851
return os.ErrNotExist
@@ -103,14 +106,66 @@ exit 0
103106
return nil
104107
}
105108

109+
func installCommitMsgHook() error {
110+
gitDir := ".git"
111+
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
112+
return os.ErrNotExist
113+
}
114+
115+
hookPath := filepath.Join(gitDir, "hooks", "commit-msg")
116+
117+
hookContent := `#!/bin/sh
118+
# CodeI18n Commit-Msg Hook
119+
# Translates Chinese commit messages to English
120+
121+
COMMIT_MSG_FILE=$1
122+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
123+
124+
# Check for Chinese characters using Perl
125+
if echo "$COMMIT_MSG" | perl -C -ne 'exit 0 if /\p{Han}/; exit 1'; then
126+
echo "CodeI18n: Detected Chinese in commit message. Translating..." >&2
127+
128+
CODEI18N_CMD="codei18n"
129+
# Check if codei18n is in PATH
130+
if ! command -v $CODEI18N_CMD >/dev/null 2>&1; then
131+
# Fallback to go run if in project root
132+
if [ -f "go.mod" ] && [ -d "cmd/codei18n" ]; then
133+
CODEI18N_CMD="go run cmd/codei18n/*.go"
134+
else
135+
echo "CodeI18n: command not found. Skipping translation." >&2
136+
exit 0
137+
fi
138+
fi
139+
140+
# Translate
141+
TRANSLATED=$(echo "$COMMIT_MSG" | $CODEI18N_CMD translate -s zh-CN -t en 2>/dev/null)
142+
143+
if [ $? -eq 0 ] && [ -n "$TRANSLATED" ]; then
144+
echo "$TRANSLATED" > "$COMMIT_MSG_FILE"
145+
echo "CodeI18n: Translated to: $TRANSLATED" >&2
146+
else
147+
echo "CodeI18n: Translation failed. Keeping original message." >&2
148+
fi
149+
fi
150+
`
151+
152+
if err := os.WriteFile(hookPath, []byte(hookContent), 0755); err != nil {
153+
return err
154+
}
155+
return nil
156+
}
157+
106158
func runHookUninstall() {
107-
hookPath := filepath.Join(".git", "hooks", "pre-commit")
159+
uninstallHook("pre-commit")
160+
uninstallHook("commit-msg")
161+
log.Success("Git Hooks 已卸载")
162+
}
163+
164+
func uninstallHook(name string) {
165+
hookPath := filepath.Join(".git", "hooks", name)
108166
if err := os.Remove(hookPath); err != nil {
109-
if os.IsNotExist(err) {
110-
log.Warn("Hook 不存在")
111-
return
167+
if !os.IsNotExist(err) {
168+
log.Warn("卸载 %s hook 失败: %v", name, err)
112169
}
113-
log.Fatal("卸载 hook 失败: %v", err)
114170
}
115-
log.Success("Hook 已卸载")
116-
}
171+
}

cmd/codei18n/init.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,14 @@ func setupGitIntegration() {
135135

136136
// 2. Install hook
137137
// Assuming installHook is available in package main (from hook.go)
138-
if err := installHook(); err != nil {
139-
log.Warn("安装 git hook 失败: %v", err)
138+
if err := installPreCommitHook(); err != nil {
139+
log.Warn("安装 pre-commit hook 失败: %v", err)
140140
} else {
141141
log.Success("Git Pre-commit Hook 已自动安装")
142142
}
143+
if err := installCommitMsgHook(); err != nil {
144+
log.Warn("安装 commit-msg hook 失败: %v", err)
145+
} else {
146+
log.Success("Git Commit-msg Hook 已自动安装")
147+
}
143148
}

cmd/codei18n/translate.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package main
22

33
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
48
"github.com/spf13/cobra"
59
"github.com/studyzy/codei18n/core/config"
610
"github.com/studyzy/codei18n/core/workflow"
@@ -12,12 +16,16 @@ var (
1216
translateModel string
1317
translateConcurrency int
1418
translateBatchSize int
19+
translateTarget string
20+
translateSource string
1521
)
1622

1723
var translateCmd = &cobra.Command{
1824
Use: "translate",
1925
Short: "自动翻译缺失的注释",
20-
Long: `调用配置的翻译引擎(LLM(OpenAI/DeepSeek) 或本地 Ollama)自动翻译映射文件中缺失的条目。`,
26+
Long: `调用配置的翻译引擎(LLM(OpenAI/DeepSeek) 或本地 Ollama)自动翻译映射文件中缺失的条目。
27+
如果通过管道传入文本,则直接翻译该文本并输出到标准输出。
28+
可以使用 --target 和 --source 标志来覆盖默认的语言设置。`,
2129
Run: func(cmd *cobra.Command, args []string) {
2230
runTranslate()
2331
},
@@ -30,6 +38,8 @@ func init() {
3038
translateCmd.Flags().StringVar(&translateModel, "model", "", "指定模型 (如 gpt-3.5-turbo)")
3139
translateCmd.Flags().IntVar(&translateConcurrency, "concurrency", 5, "并发请求数")
3240
translateCmd.Flags().IntVar(&translateBatchSize, "batch-size", 0, "每批翻译的数量 (覆盖配置)")
41+
translateCmd.Flags().StringVarP(&translateTarget, "target", "t", "", "指定目标语言 (如 en, zh-CN)")
42+
translateCmd.Flags().StringVarP(&translateSource, "source", "s", "", "指定源语言 (如 zh-CN, en)")
3343
}
3444

3545
func runTranslate() {
@@ -40,13 +50,42 @@ func runTranslate() {
4050
cfg = config.DefaultConfig()
4151
}
4252

53+
// Apply language overrides
54+
if translateTarget != "" {
55+
cfg.LocalLanguage = translateTarget
56+
}
57+
if translateSource != "" {
58+
cfg.SourceLanguage = translateSource
59+
}
60+
4361
opts := workflow.TranslateOptions{
4462
Concurrency: translateConcurrency,
4563
Provider: translateProvider,
4664
Model: translateModel,
4765
BatchSize: translateBatchSize,
4866
}
4967

68+
// Check for stdin input
69+
stat, _ := os.Stdin.Stat()
70+
if (stat.Mode() & os.ModeCharDevice) == 0 {
71+
// Data is being piped to stdin
72+
bytes, err := io.ReadAll(os.Stdin)
73+
if err != nil {
74+
log.Fatal("Failed to read from stdin: %v", err)
75+
}
76+
text := string(bytes)
77+
if text == "" {
78+
return
79+
}
80+
81+
translated, err := workflow.TranslateText(cfg, opts, text)
82+
if err != nil {
83+
log.Fatal("Translation failed: %v", err)
84+
}
85+
fmt.Print(translated)
86+
return
87+
}
88+
5089
result, err := workflow.Translate(cfg, opts)
5190
if err != nil {
5291
log.Fatal("Translation failed: %v", err)

core/workflow/stream_translate.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package workflow
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/studyzy/codei18n/adapters/translator"
8+
"github.com/studyzy/codei18n/core/config"
9+
)
10+
11+
// TranslateText translates a single text string with options
12+
func TranslateText(cfg *config.Config, opts TranslateOptions, text string) (string, error) {
13+
// Apply overrides
14+
if opts.Provider != "" {
15+
cfg.TranslationProvider = opts.Provider
16+
}
17+
if opts.Model != "" {
18+
if cfg.TranslationConfig == nil {
19+
cfg.TranslationConfig = make(map[string]string)
20+
}
21+
cfg.TranslationConfig["model"] = opts.Model
22+
}
23+
24+
// Init Translator
25+
trans, err := translator.NewFromConfig(cfg)
26+
if err != nil {
27+
return "", fmt.Errorf("初始化翻译引擎失败: %w", err)
28+
}
29+
30+
// Translate
31+
// Default direction: Source -> Local
32+
return trans.Translate(context.Background(), text, cfg.SourceLanguage, cfg.LocalLanguage)
33+
}

0 commit comments

Comments
 (0)