File size: 2,618 Bytes
9f069df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

type TMEntry struct {
	CacheKey   string `json:"cache_key"`
	SegmentID  string `json:"segment_id"`
	SourcePath string `json:"source_path"`
	TextHash   string `json:"text_hash"`
	Text       string `json:"text"`
	Translated string `json:"translated"`
	Provider   string `json:"provider"`
	Model      string `json:"model"`
	SrcLang    string `json:"src_lang"`
	TgtLang    string `json:"tgt_lang"`
	UpdatedAt  string `json:"updated_at"`
}

type TranslationMemory struct {
	path    string
	entries map[string]TMEntry
}

func LoadTranslationMemory(path string) (*TranslationMemory, error) {
	tm := &TranslationMemory{path: path, entries: map[string]TMEntry{}}
	file, err := os.Open(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return tm, nil
		}
		return nil, err
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadBytes('\n')
		if len(line) > 0 {
			trimmed := strings.TrimSpace(string(line))
			if trimmed != "" {
				var entry TMEntry
				if err := json.Unmarshal([]byte(trimmed), &entry); err != nil {
					return nil, fmt.Errorf("translation memory decode failed: %w", err)
				}
				if entry.CacheKey != "" {
					tm.entries[entry.CacheKey] = entry
				}
			}
		}
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			return nil, err
		}
	}
	return tm, nil
}

func (tm *TranslationMemory) Get(cacheKey string) (TMEntry, bool) {
	entry, ok := tm.entries[cacheKey]
	return entry, ok
}

func (tm *TranslationMemory) Put(entry TMEntry) {
	if entry.CacheKey == "" {
		return
	}
	tm.entries[entry.CacheKey] = entry
}

func (tm *TranslationMemory) Save() error {
	if tm.path == "" {
		return nil
	}
	if err := os.MkdirAll(filepath.Dir(tm.path), 0o755); err != nil {
		return err
	}
	tmpPath := tm.path + ".tmp"
	file, err := os.Create(tmpPath)
	if err != nil {
		return err
	}

	keys := make([]string, 0, len(tm.entries))
	for key := range tm.entries {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	writer := bufio.NewWriter(file)
	for _, key := range keys {
		entry := tm.entries[key]
		payload, err := json.Marshal(entry)
		if err != nil {
			_ = file.Close()
			return err
		}
		if _, err := writer.Write(payload); err != nil {
			_ = file.Close()
			return err
		}
		if _, err := writer.WriteString("\n"); err != nil {
			_ = file.Close()
			return err
		}
	}
	if err := writer.Flush(); err != nil {
		_ = file.Close()
		return err
	}
	if err := file.Close(); err != nil {
		return err
	}
	return os.Rename(tmpPath, tm.path)
}