//nolint:gosec package main import ( "encoding/json" "flag" "fmt" "io/fs" "log/slog" "os" "path/filepath" "sort" "strings" ) const ( exitCodeFailedToReadEnglishFile = 3 exitCodeFailedToReadTranslationsFile = 6 exitCodeFailedToNormalizePath = 2 exitCodeFailedToUnmarshalEnglishFile = 4 exitCodeFailedToUnmarshalTranslationsFile = 7 exitCodeFailedToWalkTranslationsPath = 5 exitCodeNoTranslationsPath = 1 ) var translationsPath string func main() { flag.StringVar(&translationsPath, "translations-path", "", "Path to translations JSON files.") flag.Parse() if translationsPath == "" { slog.Error("No -translations-path specified.") os.Exit(exitCodeNoTranslationsPath) } slog.Info( "Checking translations file for missing translations, using 'en.json' as original source...", "path", translationsPath, ) dirPath, err := filepath.Abs(translationsPath) if err != nil { slog.Error("Failed to normalize translations files path!", "error", err.Error()) os.Exit(exitCodeFailedToNormalizePath) } sourceDataAsBytes, err := os.ReadFile(filepath.Join(dirPath, "en.json")) if err != nil { slog.Error("Failed to read 'en.json' file!", "error", err.Error()) os.Exit(exitCodeFailedToReadEnglishFile) } sourceData := make(map[string]string) if err := json.Unmarshal(sourceDataAsBytes, &sourceData); err != nil { slog.Error("Failed to unmarshal 'en.json' file!", "error", err.Error()) os.Exit(exitCodeFailedToUnmarshalEnglishFile) } langFilesToCheck := make([]string, 0) if err := filepath.WalkDir(dirPath, func(path string, _ fs.DirEntry, _ error) error { if strings.Contains(path, ".json") && !strings.Contains(path, "en.json") { langFilesToCheck = append(langFilesToCheck, path) } return nil }); err != nil { slog.Error("Failed to walk translations path!", "error", err.Error()) os.Exit(exitCodeFailedToWalkTranslationsPath) } slog.Info("Got langfiles to check", "count", len(langFilesToCheck)) missingTranslations := make(map[string][]string) for _, langFile := range langFilesToCheck { slog.Info("Checking language file...", "file", langFile) langFileBytes, err := os.ReadFile(langFile) if err != nil { slog.Error("Failed to read language file!", "file", langFile, "error", err.Error()) os.Exit(exitCodeFailedToReadTranslationsFile) } langFileData := make(map[string]string) if err := json.Unmarshal(langFileBytes, &langFileData); err != nil { slog.Error("Failed to parse language file!", "file", langFile, "error", err.Error()) os.Exit(exitCodeFailedToUnmarshalTranslationsFile) } for originalTranslation := range sourceData { if _, found := langFileData[originalTranslation]; !found { if _, langFound := missingTranslations[langFile]; !langFound { missingTranslations[langFile] = make([]string, 0) } missingTranslations[langFile] = append(missingTranslations[langFile], originalTranslation) } } } if len(missingTranslations) == 0 { slog.Info("Yay, no missing translations!") os.Exit(0) } for langFile, missing := range missingTranslations { _, fileName := filepath.Split(langFile) slog.Info("Found missing translations for file.", "file", fileName) sort.Strings(missing) //nolint:forbidigo fmt.Println("============================== " + fileName + " MISSING START") for _, name := range missing { //nolint:forbidigo fmt.Println(name) } //nolint:forbidigo fmt.Println("============================== " + fileName + " MISSING END") } }