From 216d683aafe92f2952ef420663dfe7af10eecea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B8=D0=BA=D0=B0=D0=BB=D0=BE=D0=B2=20=D0=9D=D0=B8?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=B0=D0=B9=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D0=B0=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sun, 11 May 2025 00:27:35 +0300 Subject: [PATCH] feat(2b2f01a5ddca69d4421a671f975a6f4a947335a5): all modules support i18n in 6 languages --- README.md | 2 +- internal/app/app.go | 30 ++++++------ internal/display/display.go | 42 ++++++++--------- internal/fileops/archive.go | 81 +++++++++++++++++---------------- internal/fileops/fileops.go | 34 +++++++------- internal/fileops/permissions.go | 10 ++-- internal/fileops/softdelete.go | 37 +++++++-------- internal/fileops/viewer.go | 17 +++---- internal/i18n/de.json | 64 +++++++++++++++++++++++++- internal/i18n/en.json | 64 +++++++++++++++++++++++++- internal/i18n/es.json | 64 +++++++++++++++++++++++++- internal/i18n/fr.json | 64 +++++++++++++++++++++++++- internal/i18n/ru.json | 64 +++++++++++++++++++++++++- internal/i18n/zh.json | 59 +++++++++++++++++++++++- 14 files changed, 501 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index 5a7393d..56b7974 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ cmd/ ```bash # Клонировать репозиторий -$ git clone https://github.com/yourusername/file-manager.git +$ git clone https://github.com/nps-rf/file-manager.git $ cd file-manager # Запустить приложение diff --git a/internal/app/app.go b/internal/app/app.go index 9820fdc..b5618c2 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -370,7 +370,7 @@ func (a *App) cmdListDir(_ []string) error { func (a *App) cmdChangeDir(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } return a.navigator.ChangeDirectory(args[0]) } @@ -386,7 +386,7 @@ func (a *App) cmdPrintWorkingDir(_ []string) error { func (a *App) cmdMakeDir(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -398,7 +398,7 @@ func (a *App) cmdMakeDir(args []string) error { func (a *App) cmdCreateFile(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -410,7 +410,7 @@ func (a *App) cmdCreateFile(args []string) error { func (a *App) cmdRemoveFile(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -422,7 +422,7 @@ func (a *App) cmdRemoveFile(args []string) error { func (a *App) cmdRemoveDir(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -434,7 +434,7 @@ func (a *App) cmdRemoveDir(args []string) error { func (a *App) cmdCopy(args []string) error { if len(args) != 2 { - return fmt.Errorf("ожидается 2 аргумента, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_2"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -457,7 +457,7 @@ func (a *App) cmdCopy(args []string) error { func (a *App) cmdMove(args []string) error { if len(args) != 2 { - return fmt.Errorf("ожидается 2 аргумента, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_2"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -471,7 +471,7 @@ func (a *App) cmdMove(args []string) error { func (a *App) cmdFindByName(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -487,7 +487,7 @@ func (a *App) cmdFindByName(args []string) error { func (a *App) cmdFindByContent(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -503,7 +503,7 @@ func (a *App) cmdFindByContent(args []string) error { func (a *App) cmdFileInfo(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -527,7 +527,7 @@ func (a *App) cmdExit(_ []string) error { func (a *App) cmdViewFile(args []string) error { if len(args) < 1 || len(args) > 3 { - return fmt.Errorf("ожидается от 1 до 3 аргументов, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_1_to_3"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -562,7 +562,7 @@ func (a *App) cmdViewFile(args []string) error { func (a *App) cmdChangePermissions(args []string) error { if len(args) != 2 { - return fmt.Errorf("ожидается 2 аргумента, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_2"), len(args)) } mode := args[0] dir, err := a.navigator.GetCurrentDirectory() @@ -575,7 +575,7 @@ func (a *App) cmdChangePermissions(args []string) error { func (a *App) cmdCreateArchive(args []string) error { if len(args) < 3 { - return fmt.Errorf("ожидается минимум 3 аргумента, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_min_3"), len(args)) } archiveName := args[0] format := args[1] @@ -593,7 +593,7 @@ func (a *App) cmdCreateArchive(args []string) error { func (a *App) cmdExtractArchive(args []string) error { if len(args) != 2 { - return fmt.Errorf("ожидается 2 аргумента, получено %d", len(args)) + return fmt.Errorf(i18n.T("args_expected_2"), len(args)) } dir, err := a.navigator.GetCurrentDirectory() if err != nil { @@ -822,7 +822,7 @@ func (a *App) cmdTrashList(_ []string) error { func (a *App) cmdRestoreFromTrash(args []string) error { if len(args) != 1 { - return fmt.Errorf("ожидается 1 аргумент — имя файла в корзине") + return fmt.Errorf(i18n.T("args_expected_1"), len(args)) } err := a.fileOperator.SoftDeleter.RestoreFromTrash(args[0]) if err != nil { diff --git a/internal/display/display.go b/internal/display/display.go index 628ad86..509db3d 100644 --- a/internal/display/display.go +++ b/internal/display/display.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "file-manager/internal/i18n" + "gopkg.in/djherbis/times.v1" ) @@ -39,7 +41,7 @@ func NewDisplay() *Display { func (d *Display) GetFileInfo(path string) (*FileInfo, error) { info, err := os.Stat(path) if err != nil { - return nil, fmt.Errorf("не удалось получить информацию о %s: %w", path, err) + return nil, fmt.Errorf(i18n.T("display_stat_error"), path, err) } // Проверяем, является ли файл исполняемым @@ -76,33 +78,30 @@ func (d *Display) GetFileInfo(path string) (*FileInfo, error) { func (d *Display) FormatFileInfo(fileInfo *FileInfo) string { var sb strings.Builder - // Определяем тип файла - fileType := "Файл" + fileType := i18n.T("file") if fileInfo.IsDir { - fileType = "Директория" + fileType = i18n.T("directory") } - // Добавляем заголовок с подходящим цветом if d.UseColors { - _, _ = HeaderColor.Fprintf(&sb, "Информация о файле: %s\n", fileInfo.Name) + _, _ = HeaderColor.Fprintf(&sb, i18n.T("file_info")+"\n", fileInfo.Name) } else { - sb.WriteString(fmt.Sprintf("Информация о файле: %s\n", fileInfo.Name)) + sb.WriteString(fmt.Sprintf(i18n.T("file_info")+"\n", fileInfo.Name)) } - sb.WriteString(fmt.Sprintf("Путь: %s\n", fileInfo.Path)) - sb.WriteString(fmt.Sprintf("Тип: %s\n", fileType)) + sb.WriteString(fmt.Sprintf(i18n.T("path")+": %s\n", fileInfo.Path)) + sb.WriteString(fmt.Sprintf(i18n.T("type")+": %s\n", fileType)) - // Размер (только для файлов) if !fileInfo.IsDir { - sb.WriteString(fmt.Sprintf("Размер: %s\n", formatSize(fileInfo.Size))) + sb.WriteString(fmt.Sprintf(i18n.T("size")+"\n", formatSize(fileInfo.Size))) } - sb.WriteString(fmt.Sprintf("Разрешения: %s\n", fileInfo.Mode.String())) - sb.WriteString(fmt.Sprintf("Последнее изменение: %s\n", fileInfo.LastModified.Format("02.01.2006 15:04:05"))) - sb.WriteString(fmt.Sprintf("Дата создания: %s\n", fileInfo.CreatedAt.Format("02.01.2006 15:04:05"))) + sb.WriteString(fmt.Sprintf(i18n.T("permissions")+"\n", fileInfo.Mode.String())) + sb.WriteString(fmt.Sprintf(i18n.T("last_modified")+"\n", fileInfo.LastModified.Format("02.01.2006 15:04:05"))) + sb.WriteString(fmt.Sprintf(i18n.T("created_at")+"\n", fileInfo.CreatedAt.Format("02.01.2006 15:04:05"))) if fileInfo.IsExecutable { - sb.WriteString("Исполняемый: Да\n") + sb.WriteString(i18n.T("executable") + "\n") } return sb.String() @@ -114,14 +113,14 @@ func (d *Display) FormatDirEntry(entry os.DirEntry, basePath string) (string, er info, err := entry.Info() if err != nil { - return "", fmt.Errorf("не удалось получить информацию о %s: %w", fullPath, err) + return "", fmt.Errorf(i18n.T("display_stat_error"), fullPath, err) } var prefix string if entry.IsDir() { - prefix = "DIR " + prefix = i18n.T("dir_prefix") } else { - prefix = "FILE " + prefix = i18n.T("file_prefix") } size := "" @@ -137,7 +136,6 @@ func (d *Display) FormatDirEntry(entry os.DirEntry, basePath string) (string, er size, info.ModTime().Format("02.01.2006 15:04:05")) - // Применяем цвета если они включены if d.UseColors { color := GetColorByFileType(entry.Name(), entry.IsDir(), isExec) return color.Sprint(result), nil @@ -151,12 +149,12 @@ func (d *Display) FormatSearchResults(results []string, query string) string { var sb strings.Builder if d.UseColors { - _, _ = HeaderColor.Fprintf(&sb, "Результаты поиска для: %s\n", query) + _, _ = HeaderColor.Fprintf(&sb, i18n.T("search_results")+"\n", query) } else { - sb.WriteString(fmt.Sprintf("Результаты поиска для: %s\n", query)) + sb.WriteString(fmt.Sprintf(i18n.T("search_results")+"\n", query)) } - sb.WriteString(fmt.Sprintf("Найдено элементов: %d\n\n", len(results))) + sb.WriteString(fmt.Sprintf(i18n.T("found_items")+"\n\n", len(results))) for i, result := range results { if d.UseColors { diff --git a/internal/fileops/archive.go b/internal/fileops/archive.go index 62a48b9..db992aa 100644 --- a/internal/fileops/archive.go +++ b/internal/fileops/archive.go @@ -12,6 +12,9 @@ import ( "path/filepath" "strings" + "errors" + "file-manager/internal/i18n" + "github.com/ulikunitz/xz" ) @@ -44,32 +47,32 @@ func (a *Archiver) ArchiveFiles(sources []string, destination string, format str case "tar": return a.archiveTar(sources, destination) default: - return fmt.Errorf("поддерживаются только zip, tar, tar.gz, tar.bz2, tar.xz") + return errors.New(i18n.T("archive_format_error")) } } func (a *Archiver) archiveZip(sources []string, destination string) error { zipFile, err := os.Create(destination) if err != nil { - return fmt.Errorf("не удалось создать архив: %w", err) + return fmt.Errorf(i18n.T("archive_create_error"), err) } defer func() { err := zipFile.Close() if err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии zip-файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_zip_error")+"\n", err) } }() zipWriter := zip.NewWriter(zipFile) defer func() { err := zipWriter.Close() if err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии zipWriter: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_zipwriter_error")+"\n", err) } }() for _, src := range sources { err := addFileToZip(zipWriter, src, "") if err != nil { - return fmt.Errorf("ошибка при добавлении %s: %w", src, err) + return fmt.Errorf(i18n.T("archive_add_error"), src, err) } } return nil @@ -78,11 +81,11 @@ func (a *Archiver) archiveZip(sources []string, destination string) error { func (a *Archiver) archiveTarCompressed(sources []string, destination, compression string) error { file, err := os.Create(destination) if err != nil { - return fmt.Errorf("не удалось создать архив: %w", err) + return fmt.Errorf(i18n.T("archive_create_error"), err) } defer func() { if err := file.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() var tw *tar.Writer @@ -92,36 +95,36 @@ func (a *Archiver) archiveTarCompressed(sources []string, destination, compressi gw := gzip.NewWriter(file) defer func() { if err := gw.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии gzip writer: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_gzip_error")+"\n", err) } }() writer = gw case "bz2": - return fmt.Errorf("создание tar.bz2 не поддерживается стандартной библиотекой Go") + return errors.New(i18n.T("archive_bz2_unsupported")) case "xz": xzw, err := xz.NewWriter(file) if err != nil { - return fmt.Errorf("не удалось создать xz writer: %w", err) + return fmt.Errorf(i18n.T("archive_create_xz_error"), err) } defer func() { if err := xzw.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии xz writer: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_xz_error")+"\n", err) } }() writer = xzw default: - return fmt.Errorf("неизвестный тип сжатия: %s", compression) + return fmt.Errorf(i18n.T("archive_unknown_compression"), compression) } tw = tar.NewWriter(writer) defer func() { if err := tw.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии tar writer: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_tar_error")+"\n", err) } }() for _, src := range sources { err := addFileToTar(tw, src, "") if err != nil { - return fmt.Errorf("ошибка при добавлении %s: %w", src, err) + return fmt.Errorf(i18n.T("archive_add_error"), src, err) } } return nil @@ -130,23 +133,23 @@ func (a *Archiver) archiveTarCompressed(sources []string, destination, compressi func (a *Archiver) archiveTar(sources []string, destination string) error { file, err := os.Create(destination) if err != nil { - return fmt.Errorf("не удалось создать архив: %w", err) + return fmt.Errorf(i18n.T("archive_create_error"), err) } defer func() { if err := file.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() tw := tar.NewWriter(file) defer func() { if err := tw.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии tar writer: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_tar_error")+"\n", err) } }() for _, src := range sources { err := addFileToTar(tw, src, "") if err != nil { - return fmt.Errorf("ошибка при добавлении %s: %w", src, err) + return fmt.Errorf(i18n.T("archive_add_error"), src, err) } } return nil @@ -183,7 +186,7 @@ func addFileToTar(tw *tar.Writer, src, baseInTar string) error { } defer func() { if err := file.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() hdr, err := tar.FileInfoHeader(info, "") @@ -213,27 +216,27 @@ func (a *Archiver) ExtractArchive(source, destination string) error { } else if format == ".zip" { return a.ExtractZip(source, destination) } - return fmt.Errorf("поддерживаются только zip, tar, tar.gz, tar.bz2, tar.xz") + return errors.New(i18n.T("archive_format_error")) } // ExtractZip извлекает zip-архив в указанную директорию. func (a *Archiver) ExtractZip(source, destination string) error { zipReader, err := zip.OpenReader(source) if err != nil { - return fmt.Errorf("не удалось открыть архив: %w", err) + return fmt.Errorf(i18n.T("archive_open_error"), err) } defer func() { if err := zipReader.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии zipReader: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_zipreader_error")+"\n", err) } }() for _, f := range zipReader.File { if strings.Contains(f.Name, "..") || filepath.IsAbs(f.Name) { - return fmt.Errorf("архив содержит небезопасный путь: %s", f.Name) + return fmt.Errorf(i18n.T("archive_unsafe_path_error"), f.Name) } fpath := filepath.Join(destination, f.Name) if !strings.HasPrefix(filepath.Clean(fpath)+string(os.PathSeparator), filepath.Clean(destination)+string(os.PathSeparator)) { - return fmt.Errorf("path traversal: %s", fpath) + return fmt.Errorf(i18n.T("archive_path_traversal_error"), fpath) } if f.FileInfo().IsDir() { if err := os.MkdirAll(fpath, f.Mode()); err != nil { @@ -252,7 +255,7 @@ func (a *Archiver) ExtractZip(source, destination string) error { if err != nil { errClose := outFile.Close() if errClose != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии outFile: %v\n", errClose) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_outfile_error")+"\n", errClose) } return err } @@ -260,10 +263,10 @@ func (a *Archiver) ExtractZip(source, destination string) error { errClose1 := outFile.Close() errClose2 := rc.Close() if errClose1 != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии outFile: %v\n", errClose1) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_outfile_error")+"\n", errClose1) } if errClose2 != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии rc: %v\n", errClose2) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_rc_error")+"\n", errClose2) } if err != nil { return err @@ -279,7 +282,7 @@ func extractTarCompressed(source, destination, compression string) error { } defer func() { if err := file.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() var tr *tar.Reader @@ -291,7 +294,7 @@ func extractTarCompressed(source, destination, compression string) error { } defer func() { if err := gr.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии gzip reader: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_gzipreader_error")+"\n", err) } }() tr = tar.NewReader(gr) @@ -307,7 +310,7 @@ func extractTarCompressed(source, destination, compression string) error { case "none": tr = tar.NewReader(file) default: - return fmt.Errorf("неизвестный тип сжатия: %s", compression) + return fmt.Errorf(i18n.T("archive_unknown_compression"), compression) } for { hdr, err := tr.Next() @@ -319,7 +322,7 @@ func extractTarCompressed(source, destination, compression string) error { } fpath := filepath.Join(destination, hdr.Name) if !strings.HasPrefix(filepath.Clean(fpath)+string(os.PathSeparator), filepath.Clean(destination)+string(os.PathSeparator)) { - return fmt.Errorf("path traversal: %s", fpath) + return fmt.Errorf(i18n.T("archive_path_traversal_error"), fpath) } if hdr.FileInfo().IsDir() { if err := os.MkdirAll(fpath, hdr.FileInfo().Mode()); err != nil { @@ -337,7 +340,7 @@ func extractTarCompressed(source, destination, compression string) error { _, err = io.Copy(outFile, tr) errClose := outFile.Close() if errClose != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии outFile: %v\n", errClose) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_outfile_error")+"\n", errClose) } if err != nil { return err @@ -360,17 +363,17 @@ func (a *Archiver) ListArchiveContents(source string) ([]string, error) { } else if format == ".zip" { return a.listZip(source) } - return nil, fmt.Errorf("поддерживаются только zip, tar, tar.gz, tar.bz2, tar.xz") + return nil, errors.New(i18n.T("archive_format_error")) } func (a *Archiver) listZip(source string) ([]string, error) { zipReader, err := zip.OpenReader(source) if err != nil { - return nil, fmt.Errorf("не удалось открыть архив: %w", err) + return nil, fmt.Errorf(i18n.T("archive_open_error"), err) } defer func() { if err := zipReader.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии zipReader: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_zipreader_error")+"\n", err) } }() var files []string @@ -387,7 +390,7 @@ func listTarCompressed(source, compression string) ([]string, error) { } defer func() { if err := file.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() var tr *tar.Reader @@ -399,7 +402,7 @@ func listTarCompressed(source, compression string) ([]string, error) { } defer func() { if err := gr.Close(); err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии gzip reader: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_gzipreader_error")+"\n", err) } }() tr = tar.NewReader(gr) @@ -415,7 +418,7 @@ func listTarCompressed(source, compression string) ([]string, error) { case "none": tr = tar.NewReader(file) default: - return nil, fmt.Errorf("неизвестный тип сжатия: %s", compression) + return nil, fmt.Errorf(i18n.T("archive_unknown_compression"), compression) } var files []string for { @@ -463,7 +466,7 @@ func addFileToZip(zipWriter *zip.Writer, src, baseInZip string) error { defer func() { err := file.Close() if err != nil { - fmt.Fprintf(os.Stderr, "ошибка при закрытии файла: %v\n", err) + fmt.Fprintf(os.Stderr, i18n.T("archive_close_file_error")+"\n", err) } }() zipHeader, err := zip.FileInfoHeader(info) diff --git a/internal/fileops/fileops.go b/internal/fileops/fileops.go index a26dc5c..f825212 100644 --- a/internal/fileops/fileops.go +++ b/internal/fileops/fileops.go @@ -5,6 +5,8 @@ import ( "io" "os" "path/filepath" + + "file-manager/internal/i18n" ) // FileOperator предоставляет функции для работы с файлами и директориями @@ -23,11 +25,11 @@ func NewFileOperator() *FileOperator { func (f *FileOperator) CreateFile(path string) error { file, err := os.Create(path) if err != nil { - return fmt.Errorf("не удалось создать файл %s: %w", path, err) + return fmt.Errorf(i18n.T("fileops_create_file_error"), path, err) } defer func() { if err := file.Close(); err != nil { - panic(fmt.Errorf("ошибка при закрытии файла %s: %w", path, err)) + panic(fmt.Errorf(i18n.T("fileops_close_file_error"), path, err)) } }() return nil @@ -37,7 +39,7 @@ func (f *FileOperator) CreateFile(path string) error { func (f *FileOperator) CreateDirectory(path string) error { err := os.MkdirAll(path, 0755) if err != nil { - return fmt.Errorf("не удалось создать директорию %s: %w", path, err) + return fmt.Errorf(i18n.T("fileops_create_dir_error"), path, err) } return nil } @@ -47,41 +49,41 @@ func (f *FileOperator) CopyFile(source, destination string) error { // Открываем исходный файл src, err := os.Open(source) if err != nil { - return fmt.Errorf("не удалось открыть исходный файл %s: %w", source, err) + return fmt.Errorf(i18n.T("fileops_open_source_error"), source, err) } defer func() { if err := src.Close(); err != nil { - panic(fmt.Errorf("ошибка при закрытии исходного файла %s: %w", source, err)) + panic(fmt.Errorf(i18n.T("fileops_close_source_error"), source, err)) } }() // Создаем файл назначения dst, err := os.Create(destination) if err != nil { - return fmt.Errorf("не удалось создать файл назначения %s: %w", destination, err) + return fmt.Errorf(i18n.T("fileops_create_dest_error"), destination, err) } defer func() { if err := dst.Close(); err != nil { - panic(fmt.Errorf("ошибка при закрытии файла назначения %s: %w", destination, err)) + panic(fmt.Errorf(i18n.T("fileops_close_dest_error"), destination, err)) } }() // Копируем содержимое _, err = io.Copy(dst, src) if err != nil { - return fmt.Errorf("не удалось скопировать содержимое: %w", err) + return fmt.Errorf(i18n.T("fileops_copy_content_error"), err) } // Получаем информацию о разрешениях исходного файла srcInfo, err := os.Stat(source) if err != nil { - return fmt.Errorf("не удалось получить информацию о файле %s: %w", source, err) + return fmt.Errorf(i18n.T("fileops_stat_error"), source, err) } // Копируем разрешения err = os.Chmod(destination, srcInfo.Mode()) if err != nil { - return fmt.Errorf("не удалось скопировать разрешения файла: %w", err) + return fmt.Errorf(i18n.T("fileops_chmod_error"), err) } return nil @@ -92,19 +94,19 @@ func (f *FileOperator) CopyDirectory(source, destination string) error { // Получаем информацию об исходной директории srcInfo, err := os.Stat(source) if err != nil { - return fmt.Errorf("не удалось получить информацию о директории %s: %w", source, err) + return fmt.Errorf(i18n.T("fileops_stat_dir_error"), source, err) } // Создаем директорию назначения с теми же разрешениями err = os.MkdirAll(destination, srcInfo.Mode()) if err != nil { - return fmt.Errorf("не удалось создать директорию %s: %w", destination, err) + return fmt.Errorf(i18n.T("fileops_create_dir_error"), destination, err) } // Читаем содержимое исходной директории entries, err := os.ReadDir(source) if err != nil { - return fmt.Errorf("не удалось прочитать директорию %s: %w", source, err) + return fmt.Errorf(i18n.T("fileops_read_dir_error"), source, err) } // Копируем каждую запись @@ -114,7 +116,7 @@ func (f *FileOperator) CopyDirectory(source, destination string) error { fileInfo, err := os.Stat(sourcePath) if err != nil { - return fmt.Errorf("не удалось получить информацию о файле %s: %w", sourcePath, err) + return fmt.Errorf(i18n.T("fileops_stat_error"), sourcePath, err) } if fileInfo.IsDir() { @@ -137,7 +139,7 @@ func (f *FileOperator) CopyDirectory(source, destination string) error { func (f *FileOperator) MoveFile(source, destination string) error { err := os.Rename(source, destination) if err != nil { - return fmt.Errorf("не удалось переместить %s в %s: %w", source, destination, err) + return fmt.Errorf(i18n.T("fileops_move_error"), source, destination, err) } return nil } @@ -151,7 +153,7 @@ func (f *FileOperator) DeleteFile(path string) error { func (f *FileOperator) DeleteDirectory(path string) error { err := os.RemoveAll(path) if err != nil { - return fmt.Errorf("не удалось удалить директорию %s: %w", path, err) + return fmt.Errorf(i18n.T("fileops_delete_dir_error"), path, err) } return nil } diff --git a/internal/fileops/permissions.go b/internal/fileops/permissions.go index d38422c..97808d0 100644 --- a/internal/fileops/permissions.go +++ b/internal/fileops/permissions.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "strconv" + + "file-manager/internal/i18n" ) // PermissionsManager предоставляет функции для управления правами доступа к файлам @@ -19,13 +21,13 @@ func (p *PermissionsManager) ChangePermissions(path string, permissions string) // Преобразование строки с восьмеричным числом в uint32 mode, err := strconv.ParseUint(permissions, 8, 32) if err != nil { - return fmt.Errorf("некорректный формат прав доступа: %w", err) + return fmt.Errorf(i18n.T("permissions_invalid_format_error"), err) } // Применение новых прав доступа err = os.Chmod(path, os.FileMode(mode)) if err != nil { - return fmt.Errorf("не удалось изменить права доступа для %s: %w", path, err) + return fmt.Errorf(i18n.T("permissions_chmod_error"), path, err) } return nil @@ -36,7 +38,7 @@ func (p *PermissionsManager) GetPermissions(path string) (string, error) { // Получение информации о файле fileInfo, err := os.Stat(path) if err != nil { - return "", fmt.Errorf("не удалось получить информацию о %s: %w", path, err) + return "", fmt.Errorf(i18n.T("permissions_stat_error"), path, err) } // Преобразование прав доступа в строку восьмеричного числа @@ -48,7 +50,7 @@ func (p *PermissionsManager) GetPermissions(path string) (string, error) { func (p *PermissionsManager) ChangeOwner(path string, uid, gid int) error { err := os.Chown(path, uid, gid) if err != nil { - return fmt.Errorf("не удалось изменить владельца для %s: %w", path, err) + return fmt.Errorf(i18n.T("permissions_chown_error"), path, err) } return nil diff --git a/internal/fileops/softdelete.go b/internal/fileops/softdelete.go index 30f6114..87213ef 100644 --- a/internal/fileops/softdelete.go +++ b/internal/fileops/softdelete.go @@ -7,6 +7,9 @@ import ( "runtime" "strings" "time" + + "errors" + "file-manager/internal/i18n" ) // SoftDeleter определяет интерфейс для soft-delete (корзины) @@ -35,15 +38,15 @@ type linuxSoftDeleter struct{} func (l *linuxSoftDeleter) MoveToTrash(path string) error { home, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("не удалось определить домашнюю директорию: %w", err) + return fmt.Errorf(i18n.T("softdelete_home_error"), err) } trashDir := filepath.Join(home, ".local", "share", "Trash", "files") infoDir := filepath.Join(home, ".local", "share", "Trash", "info") if err := os.MkdirAll(trashDir, 0755); err != nil { - return fmt.Errorf("не удалось создать папку Trash: %w", err) + return fmt.Errorf(i18n.T("softdelete_trashdir_error"), err) } if err := os.MkdirAll(infoDir, 0755); err != nil { - return fmt.Errorf("не удалось создать папку info: %w", err) + return fmt.Errorf(i18n.T("softdelete_infodir_error"), err) } fileName := filepath.Base(path) baseName := fileName @@ -57,13 +60,13 @@ func (l *linuxSoftDeleter) MoveToTrash(path string) error { } dest := filepath.Join(trashDir, fileName) if err := os.Rename(path, dest); err != nil { - return fmt.Errorf("не удалось переместить файл в корзину: %w", err) + return fmt.Errorf(i18n.T("softdelete_move_error"), err) } // Создаём .trashinfo trashInfo := fmt.Sprintf("[Trash Info]\nPath=%s\nDeletionDate=%s\n", path, time.Now().Format("2006-01-02T15:04:05")) infoPath := filepath.Join(infoDir, fileName+".trashinfo") if err := os.WriteFile(infoPath, []byte(trashInfo), 0644); err != nil { - return fmt.Errorf("не удалось создать trashinfo: %w", err) + return fmt.Errorf(i18n.T("softdelete_trashinfo_error"), err) } return nil } @@ -78,7 +81,7 @@ func (l *linuxSoftDeleter) RestoreFromTrash(fileName string) error { infoPath := filepath.Join(infoDir, fileName+".trashinfo") data, err := os.ReadFile(infoPath) if err != nil { - return fmt.Errorf("не удалось прочитать trashinfo: %w", err) + return fmt.Errorf(i18n.T("softdelete_read_trashinfo_error"), err) } lines := strings.Split(string(data), "\n") var origPath string @@ -89,11 +92,11 @@ func (l *linuxSoftDeleter) RestoreFromTrash(fileName string) error { } } if origPath == "" { - return fmt.Errorf("оригинальный путь не найден в trashinfo") + return errors.New(i18n.T("softdelete_origpath_not_found")) } filePath := filepath.Join(trashDir, fileName) if err := os.Rename(filePath, origPath); err != nil { - return fmt.Errorf("не удалось восстановить файл: %w", err) + return fmt.Errorf(i18n.T("softdelete_restore_error"), err) } if err := os.Remove(infoPath); err != nil { return err @@ -146,11 +149,11 @@ type macSoftDeleter struct{} func (m *macSoftDeleter) MoveToTrash(path string) error { home, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("не удалось определить домашнюю директорию: %w", err) + return fmt.Errorf(i18n.T("softdelete_home_error"), err) } trashDir := filepath.Join(home, ".Trash") if err := os.MkdirAll(trashDir, 0755); err != nil { - return fmt.Errorf("не удалось создать папку .Trash: %w", err) + return fmt.Errorf(i18n.T("softdelete_trashdir_error"), err) } fileName := filepath.Base(path) baseName := fileName @@ -167,8 +170,7 @@ func (m *macSoftDeleter) MoveToTrash(path string) error { } func (m *macSoftDeleter) RestoreFromTrash(_ string) error { - // Для macOS: восстановление не реализовано (нет .trashinfo) - return fmt.Errorf("восстановление не поддерживается на macOS") + return errors.New(i18n.T("softdelete_restore_unsupported_mac")) } func (m *macSoftDeleter) EmptyTrash() error { @@ -212,11 +214,11 @@ type windowsSoftDeleter struct{} func (w *windowsSoftDeleter) MoveToTrash(path string) error { userProfile := os.Getenv("USERPROFILE") if userProfile == "" { - return fmt.Errorf("не удалось определить USERPROFILE") + return fmt.Errorf(i18n.T("softdelete_userprofile_error")) } trashDir := filepath.Join(userProfile, "Recycle.Bin") if err := os.MkdirAll(trashDir, 0755); err != nil { - return fmt.Errorf("не удалось создать папку корзины: %w", err) + return fmt.Errorf(i18n.T("softdelete_trashdir_error"), err) } fileName := filepath.Base(path) baseName := fileName @@ -233,14 +235,13 @@ func (w *windowsSoftDeleter) MoveToTrash(path string) error { } func (w *windowsSoftDeleter) RestoreFromTrash(_ string) error { - // Для Windows: восстановление не реализовано (нет .trashinfo) - return fmt.Errorf("восстановление не поддерживается на Windows") + return fmt.Errorf(i18n.T("softdelete_restore_unsupported_win")) } func (w *windowsSoftDeleter) EmptyTrash() error { userProfile := os.Getenv("USERPROFILE") if userProfile == "" { - return fmt.Errorf("не удалось определить USERPROFILE") + return fmt.Errorf(i18n.T("softdelete_userprofile_error")) } trashDir := filepath.Join(userProfile, "Recycle.Bin") entries, err := os.ReadDir(trashDir) @@ -258,7 +259,7 @@ func (w *windowsSoftDeleter) EmptyTrash() error { func (w *windowsSoftDeleter) ListTrash() ([]string, error) { userProfile := os.Getenv("USERPROFILE") if userProfile == "" { - return nil, fmt.Errorf("не удалось определить USERPROFILE") + return nil, fmt.Errorf(i18n.T("softdelete_userprofile_error")) } trashDir := filepath.Join(userProfile, "Recycle.Bin") entries, err := os.ReadDir(trashDir) diff --git a/internal/fileops/viewer.go b/internal/fileops/viewer.go index 2d30a8d..17dfbb8 100644 --- a/internal/fileops/viewer.go +++ b/internal/fileops/viewer.go @@ -23,26 +23,23 @@ func NewFileViewer() *FileViewer { // ViewTextFile показывает содержимое текстового файла func (v *FileViewer) ViewTextFile(path string, startLine, maxLines int) ([]string, error) { - // Открываем файл для чтения file, err := os.Open(path) if err != nil { return nil, fmt.Errorf(i18n.T("viewer_open"), path, err) } defer func() { if err := file.Close(); err != nil { - panic(fmt.Errorf("ошибка при закрытии файла %s: %w", path, err)) + panic(fmt.Errorf(i18n.T("viewer_close_error"), path, err)) } }() - // Проверяем, является ли файл бинарным if isBinaryFile(file) { - return nil, fmt.Errorf("невозможно отобразить бинарный файл как текст") + return nil, fmt.Errorf(i18n.T("viewer_binary_error")) } - // Сбрасываем позицию файла в начало после проверки _, err = file.Seek(0, 0) if err != nil { - return nil, fmt.Errorf("ошибка сброса позиции файла: %w", err) + return nil, fmt.Errorf(i18n.T("viewer_seek_error"), err) } // Читаем файл построчно @@ -68,7 +65,7 @@ func (v *FileViewer) ViewTextFile(path string, startLine, maxLines int) ([]strin } if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("ошибка при чтении файла: %w", err) + return nil, fmt.Errorf(i18n.T("viewer_read_error"), err) } return lines, nil @@ -78,11 +75,11 @@ func (v *FileViewer) ViewTextFile(path string, startLine, maxLines int) ([]strin func (v *FileViewer) GetTotalLines(path string) (int, error) { file, err := os.Open(path) if err != nil { - return 0, fmt.Errorf("не удалось открыть файл %s: %w", path, err) + return 0, fmt.Errorf(i18n.T("viewer_open"), path, err) } defer func() { if err := file.Close(); err != nil { - panic(fmt.Errorf("ошибка при закрытии файла %s: %w", path, err)) + panic(fmt.Errorf(i18n.T("viewer_close_error"), path, err)) } }() @@ -93,7 +90,7 @@ func (v *FileViewer) GetTotalLines(path string) (int, error) { } if err := scanner.Err(); err != nil { - return 0, fmt.Errorf("ошибка при подсчете строк: %w", err) + return 0, fmt.Errorf(i18n.T("viewer_count_error"), err) } return lineCount, nil diff --git a/internal/i18n/de.json b/internal/i18n/de.json index 5440c1b..c1de8da 100644 --- a/internal/i18n/de.json +++ b/internal/i18n/de.json @@ -86,5 +86,67 @@ "viewer_open": "Datei %s konnte nicht geöffnet werden: %v", "log_home": "Home-Verzeichnis konnte nicht ermittelt werden: %v", "log_dir": "Konfigurationsverzeichnis konnte nicht erstellt werden: %v", - "log_load": "Protokoll konnte nicht geladen werden: %v" + "log_load": "Protokoll konnte nicht geladen werden: %v", + "args_expected_1": "1 Argument erwartet, %d erhalten", + "args_expected_2": "2 Argumente erwartet, %d erhalten", + "args_expected_1_to_3": "1 bis 3 Argumente erwartet, %d erhalten", + "args_expected_min_3": "Mindestens 3 Argumente erwartet, %d erhalten", + "display_stat_error": "Informationen für %s konnten nicht abgerufen werden: %v", + "path": "Pfad", + "type": "Typ", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "Datei %s konnte nicht erstellt werden: %v", + "fileops_close_file_error": "Fehler beim Schließen der Datei %s: %v", + "fileops_create_dir_error": "Verzeichnis %s konnte nicht erstellt werden: %v", + "fileops_open_source_error": "Quelldatei %s konnte nicht geöffnet werden: %v", + "fileops_close_source_error": "Fehler beim Schließen der Quelldatei %s: %v", + "fileops_create_dest_error": "Zieldatei %s konnte nicht erstellt werden: %v", + "fileops_close_dest_error": "Fehler beim Schließen der Zieldatei %s: %v", + "fileops_copy_content_error": "Inhalt konnte nicht kopiert werden: %v", + "fileops_stat_error": "Dateiinformationen %s konnten nicht abgerufen werden: %v", + "fileops_chmod_error": "Dateiberechtigungen konnten nicht kopiert werden: %v", + "fileops_stat_dir_error": "Verzeichnisinformationen %s konnten nicht abgerufen werden: %v", + "fileops_read_dir_error": "Verzeichnis %s konnte nicht gelesen werden: %v", + "fileops_move_error": "Verschieben von %s nach %s fehlgeschlagen: %v", + "fileops_delete_dir_error": "Verzeichnis %s konnte nicht gelöscht werden: %v", + "softdelete_home_error": "Home-Verzeichnis konnte nicht ermittelt werden: %v", + "softdelete_trashdir_error": "Papierkorb-Verzeichnis konnte nicht erstellt werden: %v", + "softdelete_infodir_error": "Info-Verzeichnis konnte nicht erstellt werden: %v", + "softdelete_move_error": "Datei konnte nicht in den Papierkorb verschoben werden: %v", + "softdelete_trashinfo_error": "Trashinfo konnte nicht erstellt werden: %v", + "softdelete_read_trashinfo_error": "Trashinfo konnte nicht gelesen werden: %v", + "softdelete_origpath_not_found": "Originalpfad im Trashinfo nicht gefunden", + "softdelete_restore_error": "Datei konnte nicht wiederhergestellt werden: %v", + "softdelete_restore_unsupported_mac": "Wiederherstellung wird auf macOS nicht unterstützt", + "softdelete_userprofile_error": "USERPROFILE konnte nicht ermittelt werden", + "softdelete_restore_unsupported_win": "Wiederherstellung wird unter Windows nicht unterstützt", + "archive_format_error": "Nur zip, tar, tar.gz, tar.bz2, tar.xz werden unterstützt", + "archive_create_error": "Archiv konnte nicht erstellt werden: %v", + "archive_close_zip_error": "Fehler beim Schließen der Zip-Datei: %v", + "archive_close_zipwriter_error": "Fehler beim Schließen des zipWriter: %v", + "archive_add_error": "Fehler beim Hinzufügen von %s: %v", + "archive_close_file_error": "Fehler beim Schließen der Datei: %v", + "archive_close_gzip_error": "Fehler beim Schließen des gzip writer: %v", + "archive_bz2_unsupported": "Erstellung von tar.bz2 wird von der Go-Standardbibliothek nicht unterstützt", + "archive_create_xz_error": "xz writer konnte nicht erstellt werden: %v", + "archive_close_xz_error": "Fehler beim Schließen des xz writer: %v", + "archive_unknown_compression": "Unbekannter Kompressionstyp: %s", + "archive_close_tar_error": "Fehler beim Schließen des tar writer: %v", + "archive_open_error": "Archiv konnte nicht geöffnet werden: %v", + "archive_close_zipreader_error": "Fehler beim Schließen des zipReader: %v", + "archive_unsafe_path_error": "Archiv enthält unsicheren Pfad: %s", + "archive_path_traversal_error": "Path traversal: %s", + "archive_close_outfile_error": "Fehler beim Schließen von outFile: %v", + "archive_close_rc_error": "Fehler beim Schließen von rc: %v", + "archive_close_gzipreader_error": "Fehler beim Schließen des gzip reader: %v", + "permissions_invalid_format_error": "Ungültiges Berechtigungsformat: %v", + "permissions_chmod_error": "Berechtigungen für %s konnten nicht geändert werden: %v", + "permissions_stat_error": "Informationen für %s konnten nicht abgerufen werden: %v", + "permissions_chown_error": "Besitzer für %s konnte nicht geändert werden: %v", + "viewer_close_error": "Fehler beim Schließen der Datei %s: %v", + "viewer_binary_error": "Binärdatei kann nicht als Text angezeigt werden", + "viewer_seek_error": "Fehler beim Setzen der Dateiposition: %v", + "viewer_read_error": "Fehler beim Lesen der Datei: %v", + "viewer_count_error": "Fehler beim Zählen der Zeilen: %v" } \ No newline at end of file diff --git a/internal/i18n/en.json b/internal/i18n/en.json index 413b1e4..208d251 100644 --- a/internal/i18n/en.json +++ b/internal/i18n/en.json @@ -86,5 +86,67 @@ "viewer_open": "Failed to open file %s: %v", "log_home": "Failed to get home directory: %v", "log_dir": "Failed to create config directory: %v", - "log_load": "Failed to load log: %v" + "log_load": "Failed to load log: %v", + "args_expected_1": "Expected 1 argument, got %d", + "args_expected_2": "Expected 2 arguments, got %d", + "args_expected_1_to_3": "Expected 1 to 3 arguments, got %d", + "args_expected_min_3": "Expected at least 3 arguments, got %d", + "display_stat_error": "Failed to get info for %s: %v", + "path": "Path", + "type": "Type", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "Failed to create file %s: %v", + "fileops_close_file_error": "Error closing file %s: %v", + "fileops_create_dir_error": "Failed to create directory %s: %v", + "fileops_open_source_error": "Failed to open source file %s: %v", + "fileops_close_source_error": "Error closing source file %s: %v", + "fileops_create_dest_error": "Failed to create destination file %s: %v", + "fileops_close_dest_error": "Error closing destination file %s: %v", + "fileops_copy_content_error": "Failed to copy content: %v", + "fileops_stat_error": "Failed to get file info %s: %v", + "fileops_chmod_error": "Failed to copy file permissions: %v", + "fileops_stat_dir_error": "Failed to get directory info %s: %v", + "fileops_read_dir_error": "Failed to read directory %s: %v", + "fileops_move_error": "Failed to move %s to %s: %v", + "fileops_delete_dir_error": "Failed to delete directory %s: %v", + "softdelete_home_error": "Failed to determine home directory: %v", + "softdelete_trashdir_error": "Failed to create Trash directory: %v", + "softdelete_infodir_error": "Failed to create info directory: %v", + "softdelete_move_error": "Failed to move file to trash: %v", + "softdelete_trashinfo_error": "Failed to create trashinfo: %v", + "softdelete_read_trashinfo_error": "Failed to read trashinfo: %v", + "softdelete_origpath_not_found": "Original path not found in trashinfo", + "softdelete_restore_error": "Failed to restore file: %v", + "softdelete_restore_unsupported_mac": "Restore is not supported on macOS", + "softdelete_userprofile_error": "Failed to determine USERPROFILE", + "softdelete_restore_unsupported_win": "Restore is not supported on Windows", + "archive_format_error": "Only zip, tar, tar.gz, tar.bz2, tar.xz are supported", + "archive_create_error": "Failed to create archive: %v", + "archive_close_zip_error": "Error closing zip file: %v", + "archive_close_zipwriter_error": "Error closing zipWriter: %v", + "archive_add_error": "Error adding %s: %v", + "archive_close_file_error": "Error closing file: %v", + "archive_close_gzip_error": "Error closing gzip writer: %v", + "archive_bz2_unsupported": "Creating tar.bz2 is not supported by Go standard library", + "archive_create_xz_error": "Failed to create xz writer: %v", + "archive_close_xz_error": "Error closing xz writer: %v", + "archive_unknown_compression": "Unknown compression type: %s", + "archive_close_tar_error": "Error closing tar writer: %v", + "archive_open_error": "Failed to open archive: %v", + "archive_close_zipreader_error": "Error closing zipReader: %v", + "archive_unsafe_path_error": "Archive contains unsafe path: %s", + "archive_path_traversal_error": "Path traversal: %s", + "archive_close_outfile_error": "Error closing outFile: %v", + "archive_close_rc_error": "Error closing rc: %v", + "archive_close_gzipreader_error": "Error closing gzip reader: %v", + "permissions_invalid_format_error": "Invalid permissions format: %v", + "permissions_chmod_error": "Failed to change permissions for %s: %v", + "permissions_stat_error": "Failed to get info for %s: %v", + "permissions_chown_error": "Failed to change owner for %s: %v", + "viewer_close_error": "Error closing file %s: %v", + "viewer_binary_error": "Cannot display binary file as text", + "viewer_seek_error": "Error seeking file: %v", + "viewer_read_error": "Error reading file: %v", + "viewer_count_error": "Error counting lines: %v" } \ No newline at end of file diff --git a/internal/i18n/es.json b/internal/i18n/es.json index 956510f..c1ab142 100644 --- a/internal/i18n/es.json +++ b/internal/i18n/es.json @@ -86,5 +86,67 @@ "viewer_open": "No se pudo abrir el archivo %s: %v", "log_home": "No se pudo obtener el directorio de inicio: %v", "log_dir": "No se pudo crear el directorio de configuración: %v", - "log_load": "No se pudo cargar el registro: %v" + "log_load": "No se pudo cargar el registro: %v", + "args_expected_1": "Se esperaba 1 argumento, se recibieron %d", + "args_expected_2": "Se esperaban 2 argumentos, se recibieron %d", + "args_expected_1_to_3": "Se esperaban de 1 a 3 argumentos, se recibieron %d", + "args_expected_min_3": "Se esperaban al menos 3 argumentos, se recibieron %d", + "display_stat_error": "No se pudo obtener información de %s: %v", + "path": "Ruta", + "type": "Tipo", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "No se pudo crear el archivo %s: %v", + "fileops_close_file_error": "Error al cerrar el archivo %s: %v", + "fileops_create_dir_error": "No se pudo crear el directorio %s: %v", + "fileops_open_source_error": "No se pudo abrir el archivo fuente %s: %v", + "fileops_close_source_error": "Error al cerrar el archivo fuente %s: %v", + "fileops_create_dest_error": "No se pudo crear el archivo de destino %s: %v", + "fileops_close_dest_error": "Error al cerrar el archivo de destino %s: %v", + "fileops_copy_content_error": "No se pudo copiar el contenido: %v", + "fileops_stat_error": "No se pudo obtener información del archivo %s: %v", + "fileops_chmod_error": "No se pudieron copiar los permisos del archivo: %v", + "fileops_stat_dir_error": "No se pudo obtener información del directorio %s: %v", + "fileops_read_dir_error": "No se pudo leer el directorio %s: %v", + "fileops_move_error": "No se pudo mover %s a %s: %v", + "fileops_delete_dir_error": "No se pudo eliminar el directorio %s: %v", + "softdelete_home_error": "No se pudo determinar el directorio de inicio: %v", + "softdelete_trashdir_error": "No se pudo crear el directorio de la papelera: %v", + "softdelete_infodir_error": "No se pudo crear el directorio de información: %v", + "softdelete_move_error": "No se pudo mover el archivo a la papelera: %v", + "softdelete_trashinfo_error": "No se pudo crear el trashinfo: %v", + "softdelete_read_trashinfo_error": "No se pudo leer el trashinfo: %v", + "softdelete_origpath_not_found": "Ruta original no encontrada en trashinfo", + "softdelete_restore_error": "No se pudo restaurar el archivo: %v", + "softdelete_restore_unsupported_mac": "La restauración no es compatible con macOS", + "softdelete_userprofile_error": "No se pudo determinar USERPROFILE", + "softdelete_restore_unsupported_win": "La restauración no es compatible con Windows", + "archive_format_error": "Solo se admiten zip, tar, tar.gz, tar.bz2, tar.xz", + "archive_create_error": "No se pudo crear el archivo comprimido: %v", + "archive_close_zip_error": "Error al cerrar el archivo zip: %v", + "archive_close_zipwriter_error": "Error al cerrar zipWriter: %v", + "archive_add_error": "Error al agregar %s: %v", + "archive_close_file_error": "Error al cerrar el archivo: %v", + "archive_close_gzip_error": "Error al cerrar el gzip writer: %v", + "archive_bz2_unsupported": "La creación de tar.bz2 no es compatible con la biblioteca estándar de Go", + "archive_create_xz_error": "No se pudo crear el xz writer: %v", + "archive_close_xz_error": "Error al cerrar el xz writer: %v", + "archive_unknown_compression": "Tipo de compresión desconocido: %s", + "archive_close_tar_error": "Error al cerrar el tar writer: %v", + "archive_open_error": "No se pudo abrir el archivo comprimido: %v", + "archive_close_zipreader_error": "Error al cerrar zipReader: %v", + "archive_unsafe_path_error": "El archivo comprimido contiene una ruta no segura: %s", + "archive_path_traversal_error": "Path traversal: %s", + "archive_close_outfile_error": "Error al cerrar outFile: %v", + "archive_close_rc_error": "Error al cerrar rc: %v", + "archive_close_gzipreader_error": "Error al cerrar el gzip reader: %v", + "permissions_invalid_format_error": "Formato de permisos no válido: %v", + "permissions_chmod_error": "No se pudieron cambiar los permisos para %s: %v", + "permissions_stat_error": "No se pudo obtener información de %s: %v", + "permissions_chown_error": "No se pudo cambiar el propietario de %s: %v", + "viewer_close_error": "Error al cerrar el archivo %s: %v", + "viewer_binary_error": "No se puede mostrar un archivo binario como texto", + "viewer_seek_error": "Error al buscar en el archivo: %v", + "viewer_read_error": "Error al leer el archivo: %v", + "viewer_count_error": "Error al contar las líneas: %v" } \ No newline at end of file diff --git a/internal/i18n/fr.json b/internal/i18n/fr.json index 1d4acac..0e5161a 100644 --- a/internal/i18n/fr.json +++ b/internal/i18n/fr.json @@ -86,5 +86,67 @@ "viewer_open": "Impossible d'ouvrir le fichier %s : %v", "log_home": "Impossible d'obtenir le répertoire personnel : %v", "log_dir": "Impossible de créer le répertoire de configuration : %v", - "log_load": "Impossible de charger le journal : %v" + "log_load": "Impossible de charger le journal : %v", + "args_expected_1": "Un argument attendu, %d reçu(s)", + "args_expected_2": "Deux arguments attendus, %d reçu(s)", + "args_expected_1_to_3": "De 1 à 3 arguments attendus, %d reçu(s)", + "args_expected_min_3": "Au moins 3 arguments attendus, %d reçu(s)", + "display_stat_error": "Impossible d'obtenir les informations pour %s : %v", + "path": "Chemin", + "type": "Type", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "Impossible de créer le fichier %s : %v", + "fileops_close_file_error": "Erreur lors de la fermeture du fichier %s : %v", + "fileops_create_dir_error": "Impossible de créer le répertoire %s : %v", + "fileops_open_source_error": "Impossible d'ouvrir le fichier source %s : %v", + "fileops_close_source_error": "Erreur lors de la fermeture du fichier source %s : %v", + "fileops_create_dest_error": "Impossible de créer le fichier de destination %s : %v", + "fileops_close_dest_error": "Erreur lors de la fermeture du fichier de destination %s : %v", + "fileops_copy_content_error": "Impossible de copier le contenu : %v", + "fileops_stat_error": "Impossible d'obtenir les informations du fichier %s : %v", + "fileops_chmod_error": "Impossible de copier les permissions du fichier : %v", + "fileops_stat_dir_error": "Impossible d'obtenir les informations du répertoire %s : %v", + "fileops_read_dir_error": "Impossible de lire le répertoire %s : %v", + "fileops_move_error": "Impossible de déplacer %s vers %s : %v", + "fileops_delete_dir_error": "Impossible de supprimer le répertoire %s : %v", + "softdelete_home_error": "Impossible de déterminer le répertoire personnel : %v", + "softdelete_trashdir_error": "Impossible de créer le dossier Trash : %v", + "softdelete_infodir_error": "Impossible de créer le dossier info : %v", + "softdelete_move_error": "Impossible de déplacer le fichier dans la corbeille : %v", + "softdelete_trashinfo_error": "Impossible de créer le trashinfo : %v", + "softdelete_read_trashinfo_error": "Impossible de lire le trashinfo : %v", + "softdelete_origpath_not_found": "Chemin d'origine introuvable dans le trashinfo", + "softdelete_restore_error": "Impossible de restaurer le fichier : %v", + "softdelete_restore_unsupported_mac": "La restauration n'est pas prise en charge sur macOS", + "softdelete_userprofile_error": "Impossible de déterminer USERPROFILE", + "softdelete_restore_unsupported_win": "La restauration n'est pas prise en charge sur Windows", + "archive_format_error": "Seuls les formats zip, tar, tar.gz, tar.bz2, tar.xz sont pris en charge", + "archive_create_error": "Impossible de créer l'archive : %v", + "archive_close_zip_error": "Erreur lors de la fermeture du fichier zip : %v", + "archive_close_zipwriter_error": "Erreur lors de la fermeture du zipWriter : %v", + "archive_add_error": "Erreur lors de l'ajout de %s : %v", + "archive_close_file_error": "Erreur lors de la fermeture du fichier : %v", + "archive_close_gzip_error": "Erreur lors de la fermeture du gzip writer : %v", + "archive_bz2_unsupported": "La création de tar.bz2 n'est pas prise en charge par la bibliothèque standard Go", + "archive_create_xz_error": "Impossible de créer le xz writer : %v", + "archive_close_xz_error": "Erreur lors de la fermeture du xz writer : %v", + "archive_unknown_compression": "Type de compression inconnu : %s", + "archive_close_tar_error": "Erreur lors de la fermeture du tar writer : %v", + "archive_open_error": "Impossible d'ouvrir l'archive : %v", + "archive_close_zipreader_error": "Erreur lors de la fermeture du zipReader : %v", + "archive_unsafe_path_error": "L'archive contient un chemin non sécurisé : %s", + "archive_path_traversal_error": "Path traversal : %s", + "archive_close_outfile_error": "Erreur lors de la fermeture de outFile : %v", + "archive_close_rc_error": "Erreur lors de la fermeture de rc : %v", + "archive_close_gzipreader_error": "Erreur lors de la fermeture du gzip reader : %v", + "permissions_invalid_format_error": "Format des permissions invalide : %v", + "permissions_chmod_error": "Impossible de changer les permissions pour %s : %v", + "permissions_stat_error": "Impossible d'obtenir les informations pour %s : %v", + "permissions_chown_error": "Impossible de changer le propriétaire pour %s : %v", + "viewer_close_error": "Erreur lors de la fermeture du fichier %s : %v", + "viewer_binary_error": "Impossible d'afficher un fichier binaire en tant que texte", + "viewer_seek_error": "Erreur lors du repositionnement du fichier : %v", + "viewer_read_error": "Erreur lors de la lecture du fichier : %v", + "viewer_count_error": "Erreur lors du comptage des lignes : %v" } \ No newline at end of file diff --git a/internal/i18n/ru.json b/internal/i18n/ru.json index bce0b46..e8d81b6 100644 --- a/internal/i18n/ru.json +++ b/internal/i18n/ru.json @@ -86,5 +86,67 @@ "viewer_open": "Не удалось открыть файл %s: %v", "log_home": "Не удалось получить домашнюю директорию: %v", "log_dir": "Не удалось создать директорию конфигурации: %v", - "log_load": "Не удалось загрузить журнал: %v" + "log_load": "Не удалось загрузить журнал: %v", + "args_expected_1": "Ожидается 1 аргумент, получено %d", + "args_expected_2": "Ожидается 2 аргумента, получено %d", + "args_expected_1_to_3": "Ожидается от 1 до 3 аргументов, получено %d", + "args_expected_min_3": "Ожидается минимум 3 аргумента, получено %d", + "display_stat_error": "Не удалось получить информацию о %s: %v", + "path": "Путь", + "type": "Тип", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "Не удалось создать файл %s: %v", + "fileops_close_file_error": "Ошибка при закрытии файла %s: %v", + "fileops_create_dir_error": "Не удалось создать директорию %s: %v", + "fileops_open_source_error": "Не удалось открыть исходный файл %s: %v", + "fileops_close_source_error": "Ошибка при закрытии исходного файла %s: %v", + "fileops_create_dest_error": "Не удалось создать файл назначения %s: %v", + "fileops_close_dest_error": "Ошибка при закрытии файла назначения %s: %v", + "fileops_copy_content_error": "Не удалось скопировать содержимое: %v", + "fileops_stat_error": "Не удалось получить информацию о файле %s: %v", + "fileops_chmod_error": "Не удалось скопировать разрешения файла: %v", + "fileops_stat_dir_error": "Не удалось получить информацию о директории %s: %v", + "fileops_read_dir_error": "Не удалось прочитать директорию %s: %v", + "fileops_move_error": "Не удалось переместить %s в %s: %v", + "fileops_delete_dir_error": "Не удалось удалить директорию %s: %v", + "softdelete_home_error": "Не удалось определить домашнюю директорию: %v", + "softdelete_trashdir_error": "Не удалось создать папку Trash: %v", + "softdelete_infodir_error": "Не удалось создать папку info: %v", + "softdelete_move_error": "Не удалось переместить файл в корзину: %v", + "softdelete_trashinfo_error": "Не удалось создать trashinfo: %v", + "softdelete_read_trashinfo_error": "Не удалось прочитать trashinfo: %v", + "softdelete_origpath_not_found": "Оригинальный путь не найден в trashinfo", + "softdelete_restore_error": "Не удалось восстановить файл: %v", + "softdelete_restore_unsupported_mac": "Восстановление не поддерживается на macOS", + "softdelete_userprofile_error": "Не удалось определить USERPROFILE", + "softdelete_restore_unsupported_win": "Восстановление не поддерживается на Windows", + "archive_format_error": "Поддерживаются только zip, tar, tar.gz, tar.bz2, tar.xz", + "archive_create_error": "Не удалось создать архив: %v", + "archive_close_zip_error": "Ошибка при закрытии zip-файла: %v", + "archive_close_zipwriter_error": "Ошибка при закрытии zipWriter: %v", + "archive_add_error": "Ошибка при добавлении %s: %v", + "archive_close_file_error": "Ошибка при закрытии файла: %v", + "archive_close_gzip_error": "Ошибка при закрытии gzip writer: %v", + "archive_bz2_unsupported": "Создание tar.bz2 не поддерживается стандартной библиотекой Go", + "archive_create_xz_error": "Не удалось создать xz writer: %v", + "archive_close_xz_error": "Ошибка при закрытии xz writer: %v", + "archive_unknown_compression": "Неизвестный тип сжатия: %s", + "archive_close_tar_error": "Ошибка при закрытии tar writer: %v", + "archive_open_error": "Не удалось открыть архив: %v", + "archive_close_zipreader_error": "Ошибка при закрытии zipReader: %v", + "archive_unsafe_path_error": "Архив содержит небезопасный путь: %s", + "archive_path_traversal_error": "Path traversal: %s", + "archive_close_outfile_error": "Ошибка при закрытии outFile: %v", + "archive_close_rc_error": "Ошибка при закрытии rc: %v", + "archive_close_gzipreader_error": "Ошибка при закрытии gzip reader: %v", + "permissions_invalid_format_error": "Некорректный формат прав доступа: %v", + "permissions_chmod_error": "Не удалось изменить права доступа для %s: %v", + "permissions_stat_error": "Не удалось получить информацию о %s: %v", + "permissions_chown_error": "Не удалось изменить владельца для %s: %v", + "viewer_close_error": "Ошибка при закрытии файла %s: %v", + "viewer_binary_error": "Невозможно отобразить бинарный файл как текст", + "viewer_seek_error": "Ошибка сброса позиции файла: %v", + "viewer_read_error": "Ошибка при чтении файла: %v", + "viewer_count_error": "Ошибка при подсчете строк: %v" } \ No newline at end of file diff --git a/internal/i18n/zh.json b/internal/i18n/zh.json index 529fc7b..a399ce8 100644 --- a/internal/i18n/zh.json +++ b/internal/i18n/zh.json @@ -86,5 +86,62 @@ "viewer_open": "无法打开文件 %s:%v", "log_home": "无法获取主目录:%v", "log_dir": "无法创建配置目录:%v", - "log_load": "无法加载日志:%v" + "log_load": "无法加载日志:%v", + "args_expected_1": "期望 1 个参数,实际得到 %d 个", + "args_expected_2": "期望 2 个参数,实际得到 %d 个", + "args_expected_1_to_3": "期望 1 到 3 个参数,实际得到 %d 个", + "args_expected_min_3": "期望至少 3 个参数,实际得到 %d 个", + "display_stat_error": "无法获取 %s 的信息:%v", + "path": "路径", + "type": "类型", + "dir_prefix": "DIR ", + "file_prefix": "FILE ", + "fileops_create_file_error": "无法创建文件 %s:%v", + "fileops_close_file_error": "关闭文件 %s 时出错:%v", + "fileops_create_dir_error": "无法创建目录 %s:%v", + "fileops_open_source_error": "无法打开源文件 %s:%v", + "fileops_close_source_error": "关闭源文件 %s 时出错:%v", + "fileops_create_dest_error": "无法创建目标文件 %s:%v", + "fileops_close_dest_error": "关闭目标文件 %s 时出错:%v", + "fileops_copy_content_error": "无法复制内容:%v", + "fileops_stat_error": "无法获取文件信息 %s:%v", + "fileops_chmod_error": "无法复制文件权限:%v", + "fileops_stat_dir_error": "无法获取目录信息 %s:%v", + "fileops_read_dir_error": "无法读取目录 %s:%v", + "fileops_move_error": "无法将 %s 移动到 %s:%v", + "fileops_delete_dir_error": "无法删除目录 %s:%v", + "softdelete_home_error": "无法确定主目录:%v", + "softdelete_trashdir_error": "无法创建回收站目录:%v", + "softdelete_infodir_error": "无法创建信息目录:%v", + "softdelete_move_error": "无法将文件移至回收站:%v", + "softdelete_trashinfo_error": "无法创建 trashinfo:%v", + "softdelete_read_trashinfo_error": "无法读取 trashinfo:%v", + "softdelete_origpath_not_found": "trashinfo 中未找到原始路径", + "softdelete_restore_error": "无法恢复文件:%v", + "softdelete_restore_unsupported_mac": "macOS 不支持恢复操作", + "softdelete_userprofile_error": "无法确定 USERPROFILE", + "softdelete_restore_unsupported_win": "Windows 不支持恢复操作", + "archive_format_error": "仅支持 zip、tar、tar.gz、tar.bz2、tar.xz 格式", + "archive_create_error": "无法创建归档文件:%v", + "archive_close_zip_error": "关闭 zip 文件时出错:%v", + "archive_close_zipwriter_error": "关闭 zipWriter 时出错:%v", + "archive_add_error": "添加 %s 时出错:%v", + "archive_close_file_error": "关闭文件时出错:%v", + "archive_close_gzip_error": "关闭 gzip writer 时出错:%v", + "archive_bz2_unsupported": "Go 标准库不支持创建 tar.bz2", + "archive_create_xz_error": "无法创建 xz writer:%v", + "archive_close_xz_error": "关闭 xz writer 时出错:%v", + "archive_unknown_compression": "未知的压缩类型:%s", + "archive_close_tar_error": "关闭 tar writer 时出错:%v", + "archive_open_error": "无法打开归档文件:%v", + "archive_close_zipreader_error": "关闭 zipReader 时出错:%v", + "archive_unsafe_path_error": "归档文件包含不安全路径:%s", + "archive_path_traversal_error": "路径遍历:%s", + "archive_close_outfile_error": "关闭 outFile 时出错:%v", + "archive_close_rc_error": "关闭 rc 时出错:%v", + "archive_close_gzipreader_error": "关闭 gzip reader 时出错:%v", + "permissions_invalid_format_error": "权限格式无效:%v", + "permissions_chmod_error": "无法更改 %s 的权限:%v", + "permissions_stat_error": "无法获取 %s 的信息:%v", + "permissions_chown_error": "无法更改 %s 的所有者:%v" } \ No newline at end of file