decred.org/dcrdex@v1.0.5/client/cmd/translationsreport/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "flag" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "decred.org/dcrdex/client/core" 13 "decred.org/dcrdex/client/intl" 14 "decred.org/dcrdex/client/webserver" 15 "decred.org/dcrdex/client/webserver/locales" 16 "decred.org/dcrdex/dex" 17 ) 18 19 func main() { 20 if err := mainErr(); err != nil { 21 fmt.Fprint(os.Stderr, err, "\n") 22 os.Exit(1) 23 } 24 os.Exit(0) 25 } 26 27 func mainErr() error { 28 var formatFile string 29 flag.StringVar(&formatFile, "format", "", "filename. format the completed translation report for Go and print to stdout then quit") 30 flag.Parse() 31 32 if formatFile != "" { 33 return formatReport(formatFile) 34 } 35 36 core.RegisterTranslations() 37 webserver.RegisterTranslations() 38 locales.RegisterTranslations() 39 40 reports := intl.Report() 41 if err := os.MkdirAll("worksheets", 0755); err != nil { 42 return fmt.Errorf("error making worksheets directory: %v", err) 43 } 44 45 for lang, r := range reports { 46 if lang == "en-US" { 47 continue 48 } 49 worksheet := make([]*WorksheetEntry, 0) 50 for callerID, ts := range r.Missing { 51 for translationID, t := range ts { 52 worksheet = append(worksheet, &WorksheetEntry{ 53 ID: translationID, 54 Version: t.Version, 55 Context: callerID, 56 Notes: t.Notes, 57 English: t.T, 58 }) 59 } 60 } 61 62 worksheetPath := filepath.Join("worksheets", lang+".json") 63 f, err := os.OpenFile(worksheetPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 64 if err != nil { 65 return fmt.Errorf("OpenFile(%q) error: %w", worksheetPath, err) 66 } 67 68 enc := json.NewEncoder(f) 69 enc.SetEscapeHTML(false) // This is why we're using json.Encoder 70 enc.SetIndent("", " ") 71 err = enc.Encode(worksheet) 72 f.Close() 73 if err != nil { 74 return fmt.Errorf("json encode error: %w", err) 75 } 76 } 77 return nil 78 } 79 80 type WorksheetEntry struct { 81 ID string `json:"id"` 82 Version int `json:"version,omitempty"` 83 Context string `json:"context"` 84 Notes string `json:"notes,omitempty"` 85 English string `json:"english"` 86 Translation string `json:"translation"` 87 } 88 89 func formatReport(reportFile string) error { 90 b, err := os.ReadFile(dex.CleanAndExpandPath(reportFile)) 91 if err != nil { 92 return fmt.Errorf("error reading file at %q: %w", reportFile, err) 93 } 94 var entries []*WorksheetEntry 95 if err := json.Unmarshal(b, &entries); err != nil { 96 return fmt.Errorf("JSON error for report file at %q: %w", reportFile, err) 97 } 98 sortedEntries := make(map[string][]*WorksheetEntry, 3) 99 entryErrs := make([]string, 0) 100 for _, e := range entries { 101 if e.Context == "" { 102 entryErrs = append(entryErrs, fmt.Sprintf("no context found for report with ID %q", e.ID)) 103 continue 104 } 105 sortedEntries[e.Context] = append(sortedEntries[e.Context], e) 106 } 107 108 entryErrs = append(entryErrs, printCoreLines(sortedEntries["notifications"])...) 109 entryErrs = append(entryErrs, printJSLines(sortedEntries["js"])...) 110 entryErrs = append(entryErrs, printHTMLLines(sortedEntries["html"])...) 111 112 if len(entryErrs) == 0 { 113 return nil 114 } 115 116 fmt.Printf("\nErrors encountered\n") 117 for i := range entryErrs { 118 fmt.Println(entryErrs[i]) 119 } 120 121 return errors.New("completed with errors") 122 } 123 124 func printCoreLines(es []*WorksheetEntry) (entryErrs []string) { 125 fmt.Printf("\nCore notifications. Put in correct locale map in client/core/locale_ntfn.go\n------------\n") 126 noteEntries := make(map[string][2]*WorksheetEntry) 127 for _, e := range es { 128 var i int 129 var entryID string 130 switch { 131 case strings.HasSuffix(e.ID, " subject"): 132 entryID = e.ID[:len(e.ID)-len(" subject")] 133 case strings.HasSuffix(e.ID, " template"): 134 i = 1 135 entryID = e.ID[:len(e.ID)-len(" template")] 136 default: 137 entryErrs = append(entryErrs, fmt.Sprintf("corrupted ID %q for web notification", e.ID)) 138 continue 139 } 140 ne := noteEntries[entryID] 141 ne[i] = e 142 noteEntries[entryID] = ne 143 } 144 145 var unpaired []*WorksheetEntry 146 for entryID, st := range noteEntries { 147 subjectEntry, templateEntry := st[0], st[1] 148 if subjectEntry == nil { 149 if templateEntry == nil { 150 entryErrs = append(entryErrs, fmt.Sprintf("Missing subject or template for translation with ID %s", entryID)) 151 } else { 152 unpaired = append(unpaired, templateEntry) 153 } 154 continue 155 } else if templateEntry == nil { 156 unpaired = append(unpaired, subjectEntry) 157 continue 158 } 159 fmt.Printf(webNoteTemplate, entryID, formatReportWithType(subjectEntry), formatReportWithType(templateEntry)) 160 } 161 for _, e := range unpaired { 162 fmt.Println("Topic"+e.ID, ": ", formatReportWithType(e)+",") 163 } 164 165 return 166 } 167 168 func printJSLines(es []*WorksheetEntry) (entryErrs []string) { 169 fmt.Printf("\nJS Notifications. Add these to the appropriate map in client/webserver/jsintl.go, replacing the IDs with the appropriate variables\n------------\n") 170 for _, e := range es { 171 fmt.Printf(" %s: %s,\n", e.ID, formatReportEntry(e)) 172 } 173 return 174 } 175 176 func printHTMLLines(es []*WorksheetEntry) (entryErrs []string) { 177 fmt.Printf("\nHTML Notifications. Add these to the language file in client/webserver/locales\n------------\n") 178 for _, e := range es { 179 fmt.Printf(` "%s": %s,`+"\n", e.ID, formatReportEntry(e)) 180 } 181 return 182 } 183 184 func escapeTemplate(s string) string { 185 b, _ := json.Marshal(s) 186 return string(b) 187 } 188 189 func formatReportEntry(e *WorksheetEntry) string { 190 var v string 191 if e.Version > 0 { 192 v = fmt.Sprintf("Version: %d, ", e.Version) 193 } 194 return fmt.Sprintf("{%sT: %s}", v, escapeTemplate(e.Translation)) 195 } 196 197 func formatReportWithType(e *WorksheetEntry) string { 198 return fmt.Sprintf("intl.Translation%s", formatReportEntry(e)) 199 } 200 201 var webNoteTemplate = ` Topic%s: { 202 subject: %s, 203 template: %s, 204 }, 205 `