mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-04-24 03:00:15 -04:00
feat: add internationalization support for chatter and template file operations
- Replace hardcoded strings with i18n keys in chatter.go - Add translation keys for errors, warnings, and metadata in locale files - Update file.go to use i18n for operation messages and errors - Provide translations in German, English, Spanish, Persian, French, Italian, Japanese, Portuguese, and Chinese - Enable localized output for stream updates and file plugin operations - Ensure consistent error handling across supported languages - Maintain backward compatibility with existing functionality
This commit is contained in:
@@ -11,6 +11,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
)
|
||||
|
||||
// MaxFileSize defines the maximum file size that can be read (1MB)
|
||||
@@ -24,25 +26,25 @@ type FilePlugin struct{}
|
||||
|
||||
// safePath validates and normalizes file paths
|
||||
func (p *FilePlugin) safePath(path string) (string, error) {
|
||||
debugf("File: validating path %q", path)
|
||||
debugf(i18n.T("template_file_log_validating_path"), path)
|
||||
|
||||
// Basic security check - no path traversal
|
||||
if strings.Contains(path, "..") {
|
||||
return "", fmt.Errorf("file: path cannot contain '..'")
|
||||
return "", fmt.Errorf("%s", i18n.T("template_file_error_path_contains_parent_ref"))
|
||||
}
|
||||
|
||||
// Expand home directory if needed
|
||||
if strings.HasPrefix(path, "~/") {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: could not expand home directory: %v", err)
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_expand_home_dir"), err)
|
||||
}
|
||||
path = filepath.Join(home, path[2:])
|
||||
}
|
||||
|
||||
// Clean the path
|
||||
cleaned := filepath.Clean(path)
|
||||
debugf("File: cleaned path %q", cleaned)
|
||||
debugf(i18n.T("template_file_log_cleaned_path"), cleaned)
|
||||
return cleaned, nil
|
||||
}
|
||||
|
||||
@@ -53,13 +55,13 @@ func (p *FilePlugin) safePath(path string) (string, error) {
|
||||
// - size:PATH - Get file size in bytes
|
||||
// - modified:PATH - Get last modified time
|
||||
func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
debugf("File: operation=%q value=%q", operation, value)
|
||||
debugf(i18n.T("template_file_log_operation_value"), operation, value)
|
||||
|
||||
switch operation {
|
||||
case "tail":
|
||||
parts := strings.Split(value, "|")
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("file: tail requires format path|lines")
|
||||
return "", fmt.Errorf("%s", i18n.T("template_file_error_tail_requires_path_lines"))
|
||||
}
|
||||
|
||||
path, err := p.safePath(parts[0])
|
||||
@@ -69,11 +71,11 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
|
||||
n, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: invalid line count %q", parts[1])
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_invalid_line_count"), parts[1])
|
||||
}
|
||||
|
||||
if n < 1 {
|
||||
return "", fmt.Errorf("file: line count must be positive")
|
||||
return "", fmt.Errorf("%s", i18n.T("template_file_error_line_count_positive"))
|
||||
}
|
||||
|
||||
lines, err := p.lastNLines(path, n)
|
||||
@@ -82,7 +84,7 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
}
|
||||
|
||||
result := strings.Join(lines, "\n")
|
||||
debugf("File: tail returning %d lines", len(lines))
|
||||
debugf(i18n.T("template_file_log_tail_returning_lines"), len(lines))
|
||||
return result, nil
|
||||
|
||||
case "read":
|
||||
@@ -93,20 +95,20 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: could not stat file: %v", err)
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_stat_file"), err)
|
||||
}
|
||||
|
||||
if info.Size() > MaxFileSize {
|
||||
return "", fmt.Errorf("file: size %d exceeds limit of %d bytes",
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_size_exceeds_limit"),
|
||||
info.Size(), MaxFileSize)
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: could not read: %v", err)
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_read_file"), err)
|
||||
}
|
||||
|
||||
debugf("File: read %d bytes", len(content))
|
||||
debugf(i18n.T("template_file_log_read_bytes"), len(content))
|
||||
return string(content), nil
|
||||
|
||||
case "exists":
|
||||
@@ -117,7 +119,7 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
|
||||
_, err = os.Stat(path)
|
||||
exists := err == nil
|
||||
debugf("File: exists=%v for path %q", exists, path)
|
||||
debugf(i18n.T("template_file_log_exists_for_path"), exists, path)
|
||||
return fmt.Sprintf("%t", exists), nil
|
||||
|
||||
case "size":
|
||||
@@ -128,11 +130,11 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: could not stat file: %v", err)
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_stat_file"), err)
|
||||
}
|
||||
|
||||
size := info.Size()
|
||||
debugf("File: size=%d for path %q", size, path)
|
||||
debugf(i18n.T("template_file_log_size_for_path"), size, path)
|
||||
return fmt.Sprintf("%d", size), nil
|
||||
|
||||
case "modified":
|
||||
@@ -143,36 +145,36 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) {
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file: could not stat file: %v", err)
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_stat_file"), err)
|
||||
}
|
||||
|
||||
mtime := info.ModTime().Format(time.RFC3339)
|
||||
debugf("File: modified=%q for path %q", mtime, path)
|
||||
debugf(i18n.T("template_file_log_modified_for_path"), mtime, path)
|
||||
return mtime, nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("file: unknown operation %q (supported: read, tail, exists, size, modified)",
|
||||
return "", fmt.Errorf(i18n.T("template_file_error_unknown_operation"),
|
||||
operation)
|
||||
}
|
||||
}
|
||||
|
||||
// lastNLines returns the last n lines from a file
|
||||
func (p *FilePlugin) lastNLines(path string, n int) ([]string, error) {
|
||||
debugf("File: reading last %d lines from %q", n, path)
|
||||
debugf(i18n.T("template_file_log_reading_last_lines"), n, path)
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("file: could not open: %v", err)
|
||||
return nil, fmt.Errorf(i18n.T("template_file_error_open_file"), err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("file: could not stat: %v", err)
|
||||
return nil, fmt.Errorf(i18n.T("template_file_error_stat_open_file"), err)
|
||||
}
|
||||
|
||||
if info.Size() > MaxFileSize {
|
||||
return nil, fmt.Errorf("file: size %d exceeds limit of %d bytes",
|
||||
return nil, fmt.Errorf(i18n.T("template_file_error_size_exceeds_limit"),
|
||||
info.Size(), MaxFileSize)
|
||||
}
|
||||
|
||||
@@ -189,9 +191,9 @@ func (p *FilePlugin) lastNLines(path string, n int) ([]string, error) {
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("file: error reading: %v", err)
|
||||
return nil, fmt.Errorf(i18n.T("template_file_error_scanner_read"), err)
|
||||
}
|
||||
|
||||
debugf("File: read %d lines total, returning last %d", lineCount, len(lines))
|
||||
debugf(i18n.T("template_file_log_read_total_return_last"), lineCount, len(lines))
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user