imap-backup 16.4.2 → 16.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc471fce2bd514e0eee4cec922e8f368df0b2fb99f8b89b2d852e07799a2be77
4
- data.tar.gz: fd563c2ee648d5e384a10cdfc79a7d1d5be128bdce5908711396d414c801173f
3
+ metadata.gz: 7d4ce42bf0d3de92eaa3355305f422bdccaf627e4d9cc31cb14c128417d2b87d
4
+ data.tar.gz: 513fc795468450786093972b7ad3fc5ec024a0ed86f11728443342dd41b04980
5
5
  SHA512:
6
- metadata.gz: 36088caab383a7ff2528d8ad83813f58918abda4d80a56b0f2b2f1f45d5fc840934d1bc31e1907de11cc665f5aecb5b12152bb8dee686fb656294eb55440897e
7
- data.tar.gz: 389a82b7755342dd591fd0e2d45d760eb8350f697d46414d8d90776624bd0ee7263acfe163ab1e8dd42c869d12a544c2d5faa4656d4ef5c802f4991b9f33c1b4
6
+ metadata.gz: 460cc300cb717a129a440a3d7c542475ed6122f6dfc55e1b643c707e6575450c35d5c331b328596763b87a5426a2ce5f15a27830d739d772dfd14ae1f7b75a8b
7
+ data.tar.gz: 20917771c877b133363f7d192df3293a9eb4fd234e23aee7f9018365e9caed492cc03c283526a069260f5091333a50f0b9c011a59de96d6027cb510e8cf5d6dc
data/bin/imap-backup CHANGED
@@ -11,6 +11,9 @@ end
11
11
 
12
12
  require "imap/backup/cli"
13
13
  require "imap/backup/logger"
14
+ require "imap/backup/translator"
15
+
16
+ Imap::Backup::Translator.new.setup
14
17
 
15
18
  Imap::Backup::Logger.sanitize_stderr do
16
19
  Imap::Backup::CLI.start(ARGV)
data/docs/TODO.md ADDED
@@ -0,0 +1,16 @@
1
+ # Add Internationalization (i18n) Support
2
+
3
+ Status: [x]
4
+
5
+ ## Description
6
+
7
+ Add internationalization support to make imap-backup accessible to non-English-speaking users. The primary focus should be on the menu-driven setup interface, which contains the most user-facing text. This will allow the application to display messages and prompts in the user's preferred language based on locale environment variables (LANGUAGE, LANG, LC_*).
8
+
9
+ ## Technical Specifics
10
+
11
+ - Use the `locale` and `i18n` gems
12
+ - Override the `locale` gem's behaviour when LANG=C (use `:en`)
13
+ - Store translations under `lib/imap/backup/locales`
14
+ - Prioritize translating the menu-driven setup interface (`lib/imap/backup/setup.rb` and related files)
15
+ - Extract all user-facing strings into translation files with locale "en"
16
+ - Add Italian translations
data/docs/i18n.md ADDED
@@ -0,0 +1,153 @@
1
+ # Internationalization (i18n)
2
+
3
+ imap-backup supports multiple languages through internationalization (i18n). This document explains how the system works and how to contribute translations.
4
+
5
+ ## Overview
6
+
7
+ The application automatically detects the user's preferred language from environment variables and displays messages in that language. English is the default fallback language.
8
+
9
+ ## How Locale Detection Works
10
+
11
+ The i18n system uses the `locale` gem to detect the user's preferred language by checking these environment variables in order:
12
+
13
+ 1. `LANGUAGE`
14
+ 2. `LANG`
15
+ 3. `LC_ALL`
16
+ 4. `LC_MESSAGES`
17
+ 5. Other `LC_*` variables
18
+
19
+ Special case: When `LANG=C` is set (common in minimal environments), the system uses English (`:en`).
20
+
21
+ The system selects the first detected locale that has a translation file available.
22
+
23
+ ## Translation Files
24
+
25
+ Translation files are stored in YAML format at:
26
+
27
+ ```
28
+ lib/imap/backup/locales/
29
+ ```
30
+
31
+ Each language has its own file named with the ISO 639-1 language code:
32
+
33
+ - `en.yml` - English (default)
34
+ - `it.yml` - Italian
35
+
36
+ ## File Structure
37
+
38
+ Translation files use a hierarchical structure with nested keys. Here's an example:
39
+
40
+ ```yaml
41
+ ---
42
+ en:
43
+ setup:
44
+ main_menu:
45
+ title: "Main Menu"
46
+ choose_action: "Choose an action"
47
+ add_account: "add account"
48
+ ```
49
+
50
+ ### Using Translations in Code
51
+
52
+ Translations are accessed using the `I18n.t` method with dot-separated keys:
53
+
54
+ ```ruby
55
+ I18n.t("setup.main_menu.title") # => "Main Menu"
56
+ ```
57
+
58
+ ### Interpolation
59
+
60
+ Some strings include variables using the `%{variable}` syntax:
61
+
62
+ ```yaml
63
+ en:
64
+ setup:
65
+ account:
66
+ choose_folders: "choose folders to %{action}"
67
+ ```
68
+
69
+ In code:
70
+
71
+ ```ruby
72
+ I18n.t("setup.account.choose_folders", action: "backup")
73
+ # => "choose folders to backup"
74
+ ```
75
+
76
+ ## Contributing Translations
77
+
78
+ ### Adding a New Language
79
+
80
+ To add support for a new language:
81
+
82
+ 1. **Create a new locale file** in `lib/imap/backup/locales/` using the ISO 639-1 language code (e.g., `fr.yml` for French).
83
+
84
+ 2. **Copy the structure from `en.yml`** to ensure all keys are present:
85
+
86
+ ```yaml
87
+ ---
88
+ fr:
89
+ setup:
90
+ main_menu:
91
+ title: "Menu Principal"
92
+ choose_action: "Choisissez une action"
93
+ # ... continue translating all keys
94
+ ```
95
+
96
+ 3. **Translate all strings** while keeping:
97
+ - The YAML structure unchanged
98
+ - All interpolation variables like `%{action}` in their original form
99
+ - Special formatting like keyboard shortcuts in their original form (e.g., `(q)`)
100
+
101
+ 4. **Test your translations** by setting the appropriate locale:
102
+
103
+ ```sh
104
+ LANG=fr_FR.UTF-8 imap-backup setup
105
+ ```
106
+
107
+ 5. **Submit a pull request** with your new translation file.
108
+
109
+ ### Updating Existing Translations
110
+
111
+ When updating translations:
112
+
113
+ 1. Compare your language file with `en.yml` to identify missing or changed keys
114
+ 2. Add or update the necessary translations
115
+ 3. Ensure the structure matches the current `en.yml` structure
116
+ 4. Test with your locale settings
117
+ 5. Submit a pull request
118
+
119
+ ## Translation Guidelines
120
+
121
+ - **Maintain context**: Keep in mind where text appears (menus, prompts, error messages)
122
+ - **Preserve formatting**: Keep special characters like `(q)` for keyboard shortcuts
123
+ - **Keep variables**: Don't translate interpolation variables like `%{action}`
124
+ - **Test thoroughly**: Run the setup interface to ensure translations fit properly
125
+ - **Be consistent**: Use consistent terminology throughout the translation
126
+ - **Follow conventions**: Respect language-specific conventions for punctuation and spacing
127
+
128
+ ## Current Language Support
129
+
130
+ - **English (en)**: Complete (reference implementation)
131
+ - **Italian (it)**: Complete
132
+
133
+ ## Testing Translations
134
+
135
+ To test translations in your local environment:
136
+
137
+ ```sh
138
+ # Test with a specific locale
139
+ LANG=it_IT.UTF-8 imap-backup setup
140
+
141
+ # Test with fallback to English
142
+ LANG=C imap-backup setup
143
+ ```
144
+
145
+ ## Need Help?
146
+
147
+ If you have questions about contributing translations:
148
+
149
+ - Check existing translation files (`en.yml` and `it.yml`) for examples
150
+ - Open an issue on GitHub to discuss translation conventions
151
+ - Ask for clarification on ambiguous strings
152
+
153
+ Thank you for helping make imap-backup accessible to users worldwide!
data/imap-backup.gemspec CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.files = %w[bin/imap-backup]
14
14
  gem.files += Dir.glob("docs/*.md")
15
15
  gem.files += Dir.glob("lib/**/*.rb")
16
+ gem.files += Dir.glob("lib/imap/backup/locales/*.yml")
16
17
  gem.files += %w[imap-backup.gemspec]
17
18
  gem.files += %w[LICENSE README.md]
18
19
 
@@ -21,6 +22,8 @@ Gem::Specification.new do |gem|
21
22
  gem.required_ruby_version = ">= 3.2"
22
23
 
23
24
  gem.add_dependency "highline"
25
+ gem.add_dependency "i18n"
26
+ gem.add_dependency "locale"
24
27
  gem.add_dependency "logger"
25
28
  gem.add_dependency "mail", "2.7.1"
26
29
  gem.add_dependency "net-imap", ">= 0.3.2"
@@ -72,9 +72,7 @@ module Imap::Backup
72
72
  erb_config_path = options[:erb_configuration]
73
73
 
74
74
  # Check mutual exclusivity
75
- if config_path && erb_config_path
76
- raise "Cannot specify both --config and --erb-configuration options"
77
- end
75
+ raise I18n.t("cli.helpers.config_mutual_exclusivity") if config_path && erb_config_path
78
76
 
79
77
  # Handle ERB configuration
80
78
  return load_erb_config(erb_config_path, options) if erb_config_path
@@ -86,7 +84,7 @@ module Imap::Backup
86
84
  exists = Configuration.exist?(path: path)
87
85
  if !exists
88
86
  expected = path || Configuration.default_pathname
89
- raise ConfigurationNotFound, "Configuration file '#{expected}' not found"
87
+ raise ConfigurationNotFound, I18n.t("cli.helpers.config_not_found", path: expected)
90
88
  end
91
89
  end
92
90
  Configuration.new(path: path)
@@ -96,7 +94,7 @@ module Imap::Backup
96
94
  # @return [Account] the Account information for the email address
97
95
  def account(config, email)
98
96
  account = config.accounts.find { |a| a.username == email }
99
- raise "#{email} is not a configured account" if !account
97
+ raise I18n.t("cli.helpers.account_not_configured", email: email) if !account
100
98
 
101
99
  account
102
100
  end
@@ -123,7 +121,7 @@ module Imap::Backup
123
121
  def load_erb_config(erb_path, _options)
124
122
  # Check if file exists
125
123
  unless File.exist?(erb_path)
126
- raise ConfigurationNotFound, "ERB configuration file '#{erb_path}' not found"
124
+ raise ConfigurationNotFound, I18n.t("cli.helpers.erb_config_not_found", path: erb_path)
127
125
  end
128
126
 
129
127
  begin
@@ -132,16 +130,16 @@ module Imap::Backup
132
130
  erb = ERB.new(erb_content)
133
131
  rendered_json = erb.result
134
132
  rescue SyntaxError => e
135
- raise "ERB template has syntax error: #{e.message}"
133
+ raise I18n.t("cli.helpers.erb_syntax_error", message: e.message)
136
134
  rescue StandardError => e
137
- raise "Error processing ERB template: #{e.message}"
135
+ raise I18n.t("cli.helpers.erb_processing_error", message: e.message)
138
136
  end
139
137
 
140
138
  # Validate rendered JSON
141
139
  begin
142
140
  JSON.parse(rendered_json)
143
141
  rescue JSON::ParserError => e
144
- raise "ERB template rendered invalid JSON: #{e.message}"
142
+ raise I18n.t("cli.helpers.erb_invalid_json", message: e.message)
145
143
  end
146
144
 
147
145
  # Create temporary file with rendered JSON
@@ -64,7 +64,7 @@ module Imap::Backup
64
64
 
65
65
  def print_check_results_as_text(results)
66
66
  results.each do |account_results|
67
- Kernel.puts "Account: #{account_results[:account]}"
67
+ Kernel.puts I18n.t("cli.local.check.account", account: account_results[:account])
68
68
  account_results[:folders].each do |folder_results|
69
69
  Kernel.puts "\t#{folder_results[:name]}: #{folder_results[:result]}"
70
70
  end
@@ -95,7 +95,7 @@ module Imap::Backup
95
95
  serializer, _folder = serialized_folders.find do |_s, f|
96
96
  f.name == folder_name
97
97
  end
98
- raise "Folder '#{folder_name}' not found" if !serializer
98
+ raise I18n.t("cli.local.folder_not_found", folder: folder_name) if !serializer
99
99
 
100
100
  case options[:format]
101
101
  when "json"
@@ -126,7 +126,7 @@ module Imap::Backup
126
126
  serializer, _folder = serialized_folders.find do |_s, f|
127
127
  f.name == folder_name
128
128
  end
129
- raise "Folder '#{folder_name}' not found" if !serializer
129
+ raise I18n.t("cli.local.folder_not_found", folder: folder_name) if !serializer
130
130
 
131
131
  uid_list = uids.split(",")
132
132
 
@@ -115,7 +115,9 @@ module Imap::Backup
115
115
  def list_namespaces(namespaces)
116
116
  Kernel.puts format(
117
117
  NAMESPACE_TEMPLATE,
118
- {name: "Name", prefix: "Prefix", delim: "Delimiter"}
118
+ {name: I18n.t("cli.remote.namespaces.name"),
119
+ prefix: I18n.t("cli.remote.namespaces.prefix"),
120
+ delim: I18n.t("cli.remote.namespaces.delimiter")}
119
121
  )
120
122
  list_namespace namespaces, :personal
121
123
  list_namespace namespaces, :other
@@ -127,7 +129,8 @@ module Imap::Backup
127
129
  if info
128
130
  Kernel.puts format(NAMESPACE_TEMPLATE, name: name, **info)
129
131
  else
130
- Kernel.puts format("%-10<name>s (Not defined)", name: name)
132
+ Kernel.puts format("%-10<name>s %<not_defined>s",
133
+ name: name, not_defined: I18n.t("cli.remote.namespaces.not_defined"))
131
134
  end
132
135
  end
133
136
 
@@ -38,7 +38,7 @@ module Imap::Backup
38
38
  account = account(config, email)
39
39
  restore(account, **restore_options)
40
40
  when email && options.key?(:accounts)
41
- raise "Missing EMAIL parameter"
41
+ raise I18n.t("cli.restore.missing_email_parameter")
42
42
  when !email && options.key?(:accounts)
43
43
  Logger.logger.info(
44
44
  "Calling restore with the --account option is deprecated, " \
@@ -42,10 +42,10 @@ module Imap::Backup
42
42
  private
43
43
 
44
44
  TEXT_COLUMNS = [
45
- {name: :folder, width: 20, alignment: :left},
46
- {name: :remote, width: 8, alignment: :right},
47
- {name: :both, width: 8, alignment: :right},
48
- {name: :local, width: 8, alignment: :right}
45
+ {name: :folder, i18n_key: "cli.stats.folder", width: 20, alignment: :left},
46
+ {name: :remote, i18n_key: "cli.stats.remote", width: 8, alignment: :right},
47
+ {name: :both, i18n_key: "cli.stats.both", width: 8, alignment: :right},
48
+ {name: :local, i18n_key: "cli.stats.local", width: 8, alignment: :right}
49
49
  ].freeze
50
50
  ALIGNMENT_FORMAT_SYMBOL = {left: "-", right: " "}.freeze
51
51
  private_constant :TEXT_COLUMNS, :ALIGNMENT_FORMAT_SYMBOL
@@ -96,7 +96,7 @@ module Imap::Backup
96
96
 
97
97
  def text_header
98
98
  titles = TEXT_COLUMNS.map do |column|
99
- format("%-#{column[:width]}s", column[:name])
99
+ format("%-#{column[:width]}s", I18n.t(column[:i18n_key]))
100
100
  end.join("|")
101
101
 
102
102
  underline = TEXT_COLUMNS.map do |column|
@@ -97,23 +97,24 @@ module Imap::Backup
97
97
  end
98
98
 
99
99
  def check_accounts!
100
- if destination_email == source_email
101
- raise "Source and destination accounts cannot be the same!"
102
- end
100
+ raise I18n.t("cli.transfer.same_account_error") if destination_email == source_email
103
101
 
104
- raise "Account '#{destination_email}' does not exist" if !destination_account
102
+ if !destination_account
103
+ raise I18n.t("cli.transfer.destination_not_found",
104
+ email: destination_email)
105
+ end
105
106
 
106
- raise "Account '#{source_email}' does not exist" if !source_account
107
+ raise I18n.t("cli.transfer.source_not_found", email: source_email) if !source_account
107
108
 
108
109
  if !source_account.available_for_migration?
109
- raise "Account '#{source_email}' is not available for migration " \
110
- "(status: #{source_account.status})"
110
+ raise I18n.t("cli.transfer.source_not_available",
111
+ email: source_email, status: source_account.status)
111
112
  end
112
113
 
113
114
  return if destination_account.available_for_migration?
114
115
 
115
- raise "Account '#{destination_email}' is not available for migration " \
116
- "(status: #{destination_account.status})"
116
+ raise I18n.t("cli.transfer.destination_not_available",
117
+ email: destination_email, status: destination_account.status)
117
118
  end
118
119
 
119
120
  def choose_prefixes_and_delimiters!
@@ -126,12 +127,10 @@ module Imap::Backup
126
127
  end
127
128
 
128
129
  def ensure_no_prefix_or_delimiter_parameters!
129
- if destination_delimiter
130
- raise "--automatic-namespaces is incompatible with --destination-delimiter"
131
- end
132
- raise "--automatic-namespaces is incompatible with --destination-prefix" if destination_prefix
133
- raise "--automatic-namespaces is incompatible with --source-delimiter" if source_delimiter
134
- raise "--automatic-namespaces is incompatible with --source-prefix" if source_prefix
130
+ raise I18n.t("cli.transfer.incompatible_destination_delimiter") if destination_delimiter
131
+ raise I18n.t("cli.transfer.incompatible_destination_prefix") if destination_prefix
132
+ raise I18n.t("cli.transfer.incompatible_source_delimiter") if source_delimiter
133
+ raise I18n.t("cli.transfer.incompatible_source_prefix") if source_prefix
135
134
  end
136
135
 
137
136
  def query_servers_for_settings
@@ -76,14 +76,17 @@ module Imap::Backup
76
76
  profile = thunderbird_profile(profile_name)
77
77
 
78
78
  if !profile
79
- raise "Thunderbird profile '#{profile_name}' not found" if profile_name
79
+ if profile_name
80
+ raise I18n.t("cli.utils.thunderbird_profile_not_found",
81
+ profile: profile_name)
82
+ end
80
83
 
81
- raise "Default Thunderbird profile not found"
84
+ raise I18n.t("cli.utils.default_thunderbird_profile_not_found")
82
85
  end
83
86
 
84
87
  serialized_folders = Account::SerializedFolders.new(account: account)
85
88
 
86
- raise "No serialized folders were found for account '#{email}'" if serialized_folders.none?
89
+ raise I18n.t("cli.utils.no_serialized_folders", email: email) if serialized_folders.none?
87
90
 
88
91
  serialized_folders.each_key do |serializer|
89
92
  Thunderbird::MailboxExporter.new(
@@ -224,7 +224,7 @@ module Imap::Backup
224
224
  # Prints the program version
225
225
  # @return [void]
226
226
  def version
227
- Kernel.puts "imap-backup #{Imap::Backup::VERSION}"
227
+ Kernel.puts I18n.t("cli.version", version: Imap::Backup::VERSION)
228
228
  end
229
229
  end
230
230
  end
@@ -16,10 +16,7 @@ module Imap::Backup
16
16
  # The default download strategy key
17
17
  DEFAULT_STRATEGY = "delay_metadata".freeze
18
18
  # The available download strategies
19
- DOWNLOAD_STRATEGIES = [
20
- {key: "direct", description: "write straight to disk"},
21
- {key: DEFAULT_STRATEGY, description: "delay writing metadata"}
22
- ].freeze
19
+ DOWNLOAD_STRATEGIES = ["direct", DEFAULT_STRATEGY].freeze
23
20
  # The current file version
24
21
  VERSION = "2.2".freeze
25
22
 
@@ -77,7 +74,7 @@ module Imap::Backup
77
74
  end
78
75
  end
79
76
 
80
- # @return [String] the cofigured download strategy
77
+ # @return [String] the configured download strategy
81
78
  def download_strategy
82
79
  ensure_loaded!
83
80
 
@@ -87,7 +84,7 @@ module Imap::Backup
87
84
  # @param value [String] the new strategy
88
85
  # @return [void]
89
86
  def download_strategy=(value)
90
- raise "Unknown strategy '#{value}'" if !DOWNLOAD_STRATEGIES.find { |s| s[:key] == value }
87
+ raise "Unknown strategy '#{value}'" if !DOWNLOAD_STRATEGIES.include?(value)
91
88
 
92
89
  ensure_loaded!
93
90
 
@@ -133,7 +130,7 @@ module Imap::Backup
133
130
  contents = File.read(pathname)
134
131
  data = JSON.parse(contents, symbolize_names: true)
135
132
  data[:download_strategy] =
136
- if DOWNLOAD_STRATEGIES.find { |s| s[:key] == data[:download_strategy] }
133
+ if DOWNLOAD_STRATEGIES.include?(data[:download_strategy])
137
134
  data[:download_strategy]
138
135
  else
139
136
  DEFAULT_STRATEGY
@@ -0,0 +1,166 @@
1
+ ---
2
+ en:
3
+ setup:
4
+ main_menu:
5
+ title: "Main Menu"
6
+ choose_action: "Choose an action"
7
+ add_account: "add account"
8
+ modify_global_options: "modify global options"
9
+ save_and_exit: "save and exit"
10
+ exit_without_saving: "exit without saving changes"
11
+ quit: "(q) quit"
12
+ account:
13
+ title: "Account"
14
+ choose_action: "Choose an action"
15
+ modify_email: "modify email"
16
+ modify_password: "modify password"
17
+ modify_backup_path: "modify backup path"
18
+ toggle_folder_blacklist: "toggle folder inclusion mode (whitelist/blacklist)"
19
+ choose_folders: "choose folders to %{action}"
20
+ exclude_from_backups: "exclude from backups"
21
+ include_in_backups: "include in backups"
22
+ toggle_mirror_mode: "toggle mode ('keep'/'mirror')"
23
+ modify_multi_fetch_size: "modify multi-fetch size (number of emails to fetch at a time)"
24
+ size_prompt: "size: "
25
+ modify_server: "modify server"
26
+ server_prompt: "server: "
27
+ modify_connection_options: "modify connection options"
28
+ connection_options_prompt: "connection options (as JSON): "
29
+ malformed_json: "Malformed JSON, please try again"
30
+ press_key: "Press a key "
31
+ dont_fix_unread_flags: "don't fix changes to 'UNSEEN' flags during download"
32
+ fix_unread_flags: "fix changes to 'UNSEEN' flags during download"
33
+ change_status: "change status (%{current} -> %{next})"
34
+ test_connection: "test connection"
35
+ delete: "delete"
36
+ delete_confirm: "Are you sure? (y/n) "
37
+ return_to_main_menu: "(q) return to main menu"
38
+ email: "email"
39
+ password: "password"
40
+ unset: "(unset)"
41
+ path: "path"
42
+ exclude: "exclude"
43
+ include: "include"
44
+ all_folders: "(all folders)"
45
+ no_folders_warning: "(all folders) <- you have opted to not backup any folders!"
46
+ mirror_emails: "mirror emails"
47
+ keep_all_emails: "keep all emails"
48
+ mode: "mode"
49
+ multi_fetch: "multi-fetch"
50
+ server: "server"
51
+ connection_options: "connection options"
52
+ reset_seen_flags_message: "changes to 'UNSEEN' flags will be reset during download"
53
+ status: "status"
54
+ global_options:
55
+ title: "Global Options"
56
+ description: "These settings affect all accounts."
57
+ choose_action: "Choose an action"
58
+ change_download_strategy: "change download strategy (currently: '%{current}')"
59
+ return_to_main_menu: "(q) return to main menu"
60
+ download_strategy_chooser:
61
+ title: "Choose a Download Strategy"
62
+ current_marker: " <- current"
63
+ help: "help"
64
+ help_text: |
65
+ This setting changes how often data is written to disk during backups.
66
+
67
+ imap-backup uses two files per folder, a .mbox file with the actual
68
+ messages and a .imap file with metadata like message lengths and their
69
+ offsets within the .mbox file.
70
+
71
+ # write straight to disk
72
+
73
+ With this setting, each message and its metadata are written to disk
74
+ as they are downloaded.
75
+
76
+ This choice uses least memory and so is suitable for backing up onto
77
+ devices with limited memory, like Raspberry Pis.
78
+
79
+ # delay writing metadata
80
+
81
+ This is the default setting.
82
+
83
+ Here, messages (which are potentially very large) are appended to the
84
+ .mbox file as they are received, but the metadata is only written to
85
+ the .imap file once all the folder's messages have been downloaded.
86
+
87
+ This choice uses a little more memory than the previous setting, but
88
+ is **much** faster for large folders (potentially >30 times for
89
+ folders with >100k messages) and is less wearing on the disk.
90
+
91
+ # Other Performance Settings
92
+
93
+ Another configuration which affects backup performance is the
94
+ `multi_fetch_size` account-level setting.
95
+ press_key: "Press a key "
96
+ return_to_main_menu: "(q) return to main menu"
97
+ asker:
98
+ email_prompt: "email address: "
99
+ email_validation_error: "Enter a valid email address "
100
+ password_prompt: "password: "
101
+ password_confirmation_prompt: "repeat password: "
102
+ password_mismatch: "the password and confirmation did not match.\nContinue? (y/n) "
103
+ folder_chooser:
104
+ title: "Add/remove folders"
105
+ select_folder: "Select a folder (toggles)"
106
+ return_to_account_menu: "(q) return to the account menu"
107
+ press_key: "Press a key "
108
+ unable_to_get_folder_list: "Unable to get folder list"
109
+ folders_removed: "The following folders have been removed: %{folders}"
110
+ connection_failed: "Connection failed"
111
+ backup_path:
112
+ prompt: "backup directory: "
113
+ validation_error: "Choose a different directory "
114
+ path_in_use: "The path '%{path}' is used to backup the account '%{username}'"
115
+ cli:
116
+ version: "imap-backup %{version}"
117
+ stats:
118
+ folder: "folder"
119
+ remote: "remote"
120
+ both: "both"
121
+ local: "local"
122
+ local:
123
+ check:
124
+ account: "Account: %{account}"
125
+ remote:
126
+ namespaces:
127
+ name: "Name"
128
+ prefix: "Prefix"
129
+ delimiter: "Delimiter"
130
+ not_defined: "(Not defined)"
131
+ restore:
132
+ missing_email_parameter: "Missing EMAIL parameter"
133
+ transfer:
134
+ same_account_error: "Source and destination accounts cannot be the same!"
135
+ destination_not_found: "Account '%{email}' does not exist"
136
+ source_not_found: "Account '%{email}' does not exist"
137
+ source_not_available: "Account '%{email}' is not available for migration (status: %{status})"
138
+ destination_not_available: "Account '%{email}' is not available for migration (status: %{status})"
139
+ incompatible_destination_delimiter: "--automatic-namespaces is incompatible with --destination-delimiter"
140
+ incompatible_destination_prefix: "--automatic-namespaces is incompatible with --destination-prefix"
141
+ incompatible_source_delimiter: "--automatic-namespaces is incompatible with --source-delimiter"
142
+ incompatible_source_prefix: "--automatic-namespaces is incompatible with --source-prefix"
143
+ helpers:
144
+ config_mutual_exclusivity: "Cannot specify both --config and --erb-configuration options"
145
+ config_not_found: "Configuration file '%{path}' not found"
146
+ account_not_configured: "%{email} is not a configured account"
147
+ erb_config_not_found: "ERB configuration file '%{path}' not found"
148
+ erb_syntax_error: "ERB template has syntax error: %{message}"
149
+ erb_processing_error: "Error processing ERB template: %{message}"
150
+ erb_invalid_json: "ERB template rendered invalid JSON: %{message}"
151
+ local:
152
+ check:
153
+ account: "Account: %{account}"
154
+ folder_not_found: "Folder '%{folder}' not found"
155
+ utils:
156
+ thunderbird_profile_not_found: "Thunderbird profile '%{profile}' not found"
157
+ default_thunderbird_profile_not_found: "Default Thunderbird profile not found"
158
+ no_serialized_folders: "No serialized folders were found for account '%{email}'"
159
+ configuration:
160
+ download_strategy:
161
+ direct:
162
+ short: "write straight to disk"
163
+ long: "write to disk as messages are downloaded"
164
+ delay_metadata:
165
+ short: "delay writing metadata"
166
+ long: "delay writing metadata until all messages are downloaded"