dugway 1.0.14 → 1.2.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.github/workflows/main.yml +1 -1
  4. data/.gitignore +1 -0
  5. data/README.md +9 -0
  6. data/lib/dugway/application.rb +5 -3
  7. data/lib/dugway/assets/big_cartel_logo.svg +4 -0
  8. data/lib/dugway/cli/build.rb +18 -1
  9. data/lib/dugway/cli/server.rb +2 -2
  10. data/lib/dugway/cli/templates/source/settings.json +8 -0
  11. data/lib/dugway/cli/validate.rb +20 -2
  12. data/lib/dugway/controller.rb +5 -1
  13. data/lib/dugway/liquid/drops/account_drop.rb +4 -0
  14. data/lib/dugway/liquid/drops/features_drop.rb +144 -0
  15. data/lib/dugway/liquid/drops/product_drop.rb +8 -0
  16. data/lib/dugway/liquid/drops/products_drop.rb +1 -1
  17. data/lib/dugway/liquid/drops/related_products_drop.rb +88 -0
  18. data/lib/dugway/liquid/drops/theme_drop.rb +23 -0
  19. data/lib/dugway/liquid/drops/translations_drop.rb +122 -0
  20. data/lib/dugway/liquifier.rb +44 -8
  21. data/lib/dugway/store.rb +7 -2
  22. data/lib/dugway/theme.rb +169 -3
  23. data/lib/dugway/version.rb +1 -1
  24. data/lib/dugway.rb +31 -1
  25. data/locales/storefront.de.yml +79 -0
  26. data/locales/storefront.en-CA.yml +79 -0
  27. data/locales/storefront.en-GB.yml +79 -0
  28. data/locales/storefront.en-US.yml +79 -0
  29. data/locales/storefront.es-ES.yml +79 -0
  30. data/locales/storefront.es-MX.yml +79 -0
  31. data/locales/storefront.fr-CA.yml +79 -0
  32. data/locales/storefront.fr-FR.yml +79 -0
  33. data/locales/storefront.id.yml +79 -0
  34. data/locales/storefront.it.yml +79 -0
  35. data/locales/storefront.ja.yml +79 -0
  36. data/locales/storefront.ko.yml +79 -0
  37. data/locales/storefront.nl.yml +79 -0
  38. data/locales/storefront.pl.yml +79 -0
  39. data/locales/storefront.pt-BR.yml +79 -0
  40. data/locales/storefront.pt-PT.yml +79 -0
  41. data/locales/storefront.ro.yml +79 -0
  42. data/locales/storefront.sv.yml +79 -0
  43. data/locales/storefront.tr.yml +79 -0
  44. data/locales/storefront.zh-CN.yml +79 -0
  45. data/locales/storefront.zh-TW.yml +79 -0
  46. data/log/dugway.log +1 -0
  47. data/spec/features/page_rendering_spec.rb +4 -4
  48. data/spec/fixtures/theme/layout.html +2 -0
  49. data/spec/fixtures/theme/settings.json +6 -0
  50. data/spec/spec_helper.rb +4 -0
  51. data/spec/units/dugway/liquid/drops/features_drop_spec.rb +182 -0
  52. data/spec/units/dugway/liquid/drops/product_drop_spec.rb +36 -0
  53. data/spec/units/dugway/liquid/drops/related_products_drop_spec.rb +80 -0
  54. data/spec/units/dugway/liquid/drops/theme_drop_spec.rb +45 -0
  55. data/spec/units/dugway/liquid/drops/translations_drop_spec.rb +292 -0
  56. data/spec/units/dugway/store_spec.rb +37 -0
  57. data/spec/units/dugway/theme_spec.rb +456 -0
  58. metadata +35 -2
data/lib/dugway/theme.rb CHANGED
@@ -13,10 +13,12 @@ module Dugway
13
13
  File.join(__dir__, 'config', 'theme_color_attribute_mappings.yml')
14
14
  ).freeze
15
15
 
16
- attr_reader :errors
16
+ attr_reader :errors, :warnings
17
17
 
18
18
  def initialize(overridden_customization={})
19
19
  @overridden_customization = overridden_customization.stringify_keys
20
+ @errors = []
21
+ @warnings = []
20
22
  end
21
23
 
22
24
  def layout
@@ -39,9 +41,13 @@ module Dugway
39
41
  customization_for_type('image_sets')
40
42
  end
41
43
 
44
+ def features
45
+ customization_for_type('features')
46
+ end
47
+
42
48
  def customization
43
49
  Hash.new.tap do |customization|
44
- %w( fonts colors options images image_sets ).each do |type|
50
+ %w( fonts colors options images image_sets features).each do |type|
45
51
  customization.update(customization_for_type(type))
46
52
  end
47
53
 
@@ -93,8 +99,9 @@ module Dugway
93
99
  end
94
100
  end
95
101
 
96
- def valid?(validate_colors: true, validate_layout_attributes: true)
102
+ def valid?(validate_colors: true, validate_layout_attributes: true, validate_options: true)
97
103
  @errors = []
104
+ @warnings = []
98
105
 
99
106
  REQUIRED_FILES.each do |file|
100
107
  @errors << "Missing source/#{ file }" if read_source_file(file).nil?
@@ -118,11 +125,28 @@ module Dugway
118
125
  validate_required_color_settings if validate_colors
119
126
  validate_required_layout_attributes if validate_layout_attributes
120
127
 
128
+ if validate_options
129
+ validate_options_settings # Validate descriptions, requires, defaults
130
+ # Validate sections for options, images, and image_sets
131
+ validate_setting_sections(settings['options'], 'options') if settings['options']
132
+ validate_setting_sections(settings['images'], 'images') if settings['images']
133
+ validate_setting_sections(settings['image_sets'], 'image_sets') if settings['image_sets']
134
+ end
135
+
121
136
  @errors.empty?
122
137
  end
123
138
 
124
139
  def validate_required_color_settings
140
+ if !settings['colors']
141
+ @errors << "Missing colors section in theme settings"
142
+ return
143
+ end
144
+
125
145
  required_colors_attribute_names = THEME_COLOR_ATTRIBUTE_MAPPINGS['required_color_attributes']
146
+ if !required_colors_attribute_names
147
+ @errors << "Missing required color attributes configuration"
148
+ return
149
+ end
126
150
 
127
151
  theme_colors = settings['colors'].map { |c| c['variable'] }
128
152
  mappings = THEME_COLOR_ATTRIBUTE_MAPPINGS[name] || {}
@@ -154,8 +178,41 @@ module Dugway
154
178
  @errors << "layout.html must have exactly one `data-bc-hook=\"footer\"`" if footer_hooks != 1
155
179
  end
156
180
 
181
+ VALID_SECTIONS = %w(global_navigation homepage product_page general messaging social translations).freeze
182
+
183
+ def validate_options_settings
184
+ return unless settings['options']
185
+
186
+ validate_options_descriptions
187
+ validate_options_requires
188
+ validate_option_defaults
189
+ end
190
+
157
191
  private
158
192
 
193
+ # Generic method to validate the 'section' property for any array of settings
194
+ def validate_setting_sections(settings_array, setting_type_name)
195
+ return unless settings_array.is_a?(Array)
196
+
197
+ settings_array.each do |setting|
198
+ variable = setting['variable'] || 'unknown'
199
+
200
+ unless setting.key?('section')
201
+ warning_msg = "Warning: Theme setting '#{variable}' is missing the 'section' property."
202
+ @warnings << warning_msg
203
+ Kernel.warn(warning_msg)
204
+ next # Skip further checks if section is missing
205
+ end
206
+
207
+ section_value = setting['section']
208
+ unless VALID_SECTIONS.include?(section_value)
209
+ warning_msg = "Warning: Theme setting '#{variable}' has an invalid 'section' value: '#{section_value}'. Allowed values are: #{VALID_SECTIONS.join(', ')}."
210
+ @warnings << warning_msg
211
+ Kernel.warn(warning_msg)
212
+ end
213
+ end
214
+ end
215
+
159
216
  def validate_preview
160
217
  preview = settings['preset_styles']['preview']
161
218
  if preview
@@ -225,6 +282,115 @@ module Dugway
225
282
  @errors << "Duplicate style names found: #{duplicates.join(', ')}" if duplicates.any?
226
283
  end
227
284
 
285
+ def validate_options_descriptions
286
+ missing_descriptions = settings['options'].select { |option|
287
+ option['description'].nil? || option['description'].strip.empty?
288
+ }.map { |option| option['variable'] }
289
+
290
+ @errors << "Missing descriptions for settings: #{missing_descriptions.join(', ')}" unless missing_descriptions.empty?
291
+ end
292
+
293
+ # Validate that any dependent settings are present in the theme settings
294
+ def validate_options_requires
295
+ return unless settings['options']
296
+ all_variables = settings['options'].map { |o| o['variable'] }
297
+ valid_operators = %w(eq neq gt lt gte lte present)
298
+
299
+ settings['options'].each do |option|
300
+ next unless option['requires']
301
+
302
+ # Handle case where requires is a string
303
+ if option['requires'].is_a?(String)
304
+ next if option['requires'] == 'inventory'
305
+ unless all_variables.include?(option['requires'])
306
+ @errors << "Option '#{option['variable']}' requires unknown setting '#{option['requires']}'"
307
+ end
308
+ next
309
+ end
310
+
311
+ # Validate requires is either a string or array
312
+ unless option['requires'].is_a?(Array)
313
+ @errors << "Option '#{option['variable']}' requires must be string 'inventory' or array of rules"
314
+ next
315
+ end
316
+
317
+ # Process each rule in the array
318
+ option['requires'].each do |rule|
319
+ next if rule == 'inventory'
320
+
321
+ # Extract setting name and optional operator/value
322
+ parts = rule.split(/\s+/)
323
+ setting_name = parts[0]
324
+ operator = parts[1]
325
+ value = parts[2]
326
+
327
+ if setting_name.start_with?('feature:')
328
+ # Validate feature flag format
329
+ unless setting_name =~ /^feature:[a-z_]+$/
330
+ @errors << "Option '#{option['variable']}' has invalid feature flag format"
331
+ next
332
+ end
333
+
334
+ # Only validate operator/value if they exist
335
+ if operator
336
+ is_valid_operator = ['eq', 'neq'].include?(operator)
337
+
338
+ @errors << "Option '#{option['variable']}' has invalid operator '#{operator}'. Feature flags can only use `eq` or `neq`." unless is_valid_operator
339
+
340
+ is_valid_value = ['visible'].include?(value)
341
+
342
+ @errors << "Option '#{option['variable']}' has invalid comparison value '#{value}'. Feature flags can only check for `visible`." unless is_valid_value
343
+ end
344
+ next
345
+ end
346
+
347
+ # --- Non-feature rule validation ---
348
+ unless all_variables.include?(setting_name)
349
+ @errors << "Option '#{option['variable']}' requires unknown setting '#{setting_name}'"
350
+ next # Skip further checks for this rule if setting is unknown
351
+ end
352
+
353
+ # Validate operator if present
354
+ if operator && !valid_operators.include?(operator)
355
+ @errors << "Option '#{option['variable']}' has invalid operator '#{operator}'. Allowed operators are: #{valid_operators.join(', ')}."
356
+ end
357
+ end
358
+ end
359
+ end
360
+
361
+ def validate_option_value(option, value_type)
362
+ value = option[value_type]
363
+ return unless value
364
+
365
+ case option['type']
366
+ when 'select'
367
+ validate_select_option(option, value, value_type)
368
+ when 'boolean'
369
+ @errors << "#{value_type.capitalize} '#{value}' is not a boolean for #{option['variable']}" unless [true, false].include?(value)
370
+ end
371
+ end
372
+
373
+ def validate_option_defaults
374
+ return unless settings['options']
375
+ settings['options'].each do |option|
376
+ validate_option_value(option, 'default')
377
+ validate_option_value(option, 'upgrade_default')
378
+ end
379
+ end
380
+
381
+ def validate_select_option(option, value, value_type)
382
+ if option['options'] == "product_orders"
383
+ valid_values = %w[position top-selling newest views updated_new_to_old]
384
+ @errors << "#{value_type.capitalize} '#{value}' is not a valid option for #{option['variable']}" unless valid_values.include?(value)
385
+ elsif option['options'].is_a?(String) && option['options'] =~ /^\d+\.\.\d+$/
386
+ range = eval(option['options'])
387
+ @errors << "#{value_type.capitalize} '#{value}' is out of range for #{option['variable']}" unless range.include?(value.to_i)
388
+ elsif option['options'].is_a?(Array)
389
+ valid_values = option['options'].map { |o| o.is_a?(Array) ? o[1] : o }
390
+ @errors << "#{value_type.capitalize} '#{value}' is not a valid option for #{option['variable']}" unless valid_values.include?(value)
391
+ end
392
+ end
393
+
228
394
  def source_dir
229
395
  Dugway.source_dir
230
396
  end
@@ -1,3 +1,3 @@
1
1
  module Dugway
2
- VERSION = "1.0.14"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/dugway.rb CHANGED
@@ -14,6 +14,33 @@ require 'i18n'
14
14
  require 'bigcartel-currency-locales'
15
15
  require 'bigcartel/theme/fonts'
16
16
 
17
+ # Configure I18n to load locale files from the gem's locales directory.
18
+ # This allows for easy addition and management of translations.
19
+ gem_locales_pattern = File.expand_path('../locales/storefront.*.yml', File.dirname(__FILE__))
20
+ gem_locale_files = Dir.glob(gem_locales_pattern)
21
+
22
+ # Add these locale files to the I18n load path, ensuring no duplicates and preserving existing paths.
23
+ I18n.load_path = (I18n.load_path + gem_locale_files).uniq
24
+
25
+ # Determine available locales from the filenames (e.g., 'storefront.en-US.yml' -> :'en-US').
26
+ available_locales = gem_locale_files.map do |file|
27
+ # Extracts 'en-US' from 'storefront.en-US.yml' or 'de' from 'storefront.de.yml'
28
+ locale_string = File.basename(file, '.yml').split('storefront.').last
29
+ locale_string.to_sym if locale_string
30
+ end.compact.uniq
31
+
32
+ # Ensure :en is an available locale if any 'en-XX' variant exists (e.g., :en-US, :en-GB),
33
+ # as I18n can use :en as a fallback.
34
+ if available_locales.any? { |loc| loc.to_s.start_with?('en-') } && !available_locales.include?(:en)
35
+ available_locales << :en
36
+ end
37
+
38
+ # Set available locales. Provide a default if no locale files are found (e.g., during initial setup or in case of an error).
39
+ I18n.config.available_locales = available_locales.empty? ? [:'en-US', :en] : available_locales
40
+
41
+ # Reload I18n to apply changes and load the translations from the files.
42
+ I18n.backend.reload!
43
+
17
44
  require 'rack/builder'
18
45
  require 'rack/commonlogger'
19
46
  require 'better_errors'
@@ -52,7 +79,9 @@ module Dugway
52
79
  end
53
80
 
54
81
  def store
55
- @store ||= Store.new(options && options[:store] && options[:store][:subdomain] || 'dugway')
82
+ store_options = options && options[:store] ? options[:store] : {}
83
+ subdomain = store_options[:subdomain] || 'dugway'
84
+ @store ||= Store.new(subdomain, store_options)
56
85
  end
57
86
 
58
87
  def theme
@@ -74,5 +103,6 @@ module Dugway
74
103
  def options
75
104
  @options
76
105
  end
106
+
77
107
  end
78
108
  end
@@ -0,0 +1,79 @@
1
+ de:
2
+ storefront:
3
+ navigation:
4
+ all: "Alle"
5
+ all_products: "Alle Produkte"
6
+ back_to_site: "Zurück zur Seite"
7
+ cart: "Warenkorb"
8
+ categories: "Kategorien"
9
+ contact: "Kontakt"
10
+ home: "Startseite"
11
+ item: "Artikel" # capitalized as it is proper grammar in German
12
+ items: "Artikel" # capitalized as it is proper grammar in German
13
+ more: "Mehr"
14
+ next: "Weiter"
15
+ newest: "Neueste"
16
+ pages: "Seiten"
17
+ previous: "Zurück"
18
+ products: "Produkte"
19
+ quick_view: "Schnellansicht"
20
+ search: "Suche"
21
+ shop: "Shop"
22
+ social: "Folge uns"
23
+ subscribe: "Abonnieren"
24
+ top_selling: "Meistverkauft"
25
+ view: "Ansehen"
26
+ view_all: "Alle anzeigen"
27
+ # "Sort By" translations are used in very few themes, and are not exposed in theme settings for sellers to configure
28
+ sort_by: Sortieren nach
29
+ sort_by_featured: Empfohlen
30
+ sort_by_on_sale: Im Angebot
31
+ sort_by_top_selling: Bestseller
32
+ sort_by_alphabetically_a_to_z: Alphabetisch (A bis Z)
33
+ sort_by_alphabetically_z_to_a: Alphabetisch (Z bis A)
34
+ sort_by_date_new_to_old: Datum (neu zu alt)
35
+ sort_by_date_old_to_new: Datum (alt zu neu)
36
+ sort_by_price_low_to_high: Preis (niedrig zu hoch)
37
+ sort_by_price_high_to_low: Preis (hoch zu niedrig)
38
+ home:
39
+ all_products: "Alle Produkte"
40
+ featured: "Empfohlen"
41
+ featured_categories: "Empfohlene Kategorien"
42
+ featured_products: "Empfohlene Produkte"
43
+ featured_video: ""
44
+ products:
45
+ add_to_cart: "Hinzufügen"
46
+ added: "Hinzugefügt!" # Used in few themes, intentionally not exposed in theme settings
47
+ adding: "Hinzufügen..." # Used in few themes, intentionally not exposed in theme settings
48
+ almost_sold_out: "Nur noch wenige!"
49
+ coming_soon: "Demnächst verfügbar"
50
+ in_stock: "auf Lager"
51
+ description: "Beschreibung"
52
+ inventory: "Lagerbestand"
53
+ low_inventory: "Begrenzte Menge"
54
+ no_products: "Keine Produkte gefunden"
55
+ on_sale: "Im Angebot"
56
+ related_products: "Das gefällt dir vielleicht"
57
+ reset: "Zurücksetzen"
58
+ search_results: "Suchergebnisse"
59
+ select: "Wählen"
60
+ select_variant: "Option wählen"
61
+ sold_out: "Ausverkauft"
62
+ cart:
63
+ checkout: "Zur Kasse"
64
+ continue_shopping: "Weiter einkaufen"
65
+ empty_cart: "Dein Warenkorb ist leer"
66
+ quantity_abbreviated: "Anz."
67
+ quantity: "Anzahl"
68
+ remove: "Entfernen"
69
+ share_this_cart: "Diesen Warenkorb teilen"
70
+ share_this_cart_link_copy_success: "Link kopiert!"
71
+ subtotal: "Zwischensumme"
72
+ view_cart: "Warenkorb ansehen"
73
+ contact:
74
+ email: "E-Mail"
75
+ form_success: "Danke! Deine Nachricht wurde gesendet und wir werden uns bald bei dir melden."
76
+ message: "Nachricht"
77
+ name: "Name"
78
+ send_button: "Nachricht senden"
79
+ subject: "Betreff"
@@ -0,0 +1,79 @@
1
+ en-CA:
2
+ storefront:
3
+ navigation:
4
+ all: "All"
5
+ all_products: "All products"
6
+ back_to_site: "Back to site"
7
+ cart: "Cart"
8
+ categories: "Categories"
9
+ contact: "Contact"
10
+ home: "Home"
11
+ item: "item"
12
+ items: "items"
13
+ more: "More"
14
+ next: "Next"
15
+ newest: "Newest"
16
+ pages: "Pages"
17
+ previous: "Previous"
18
+ products: "Products"
19
+ quick_view: "Quick view"
20
+ search: "Search"
21
+ shop: "Shop"
22
+ social: "Follow us"
23
+ subscribe: "Subscribe"
24
+ top_selling: "Best-selling"
25
+ view: "View"
26
+ view_all: "View all"
27
+ # "Sort By" translations are used in very few themes, and are not exposed in theme settings for sellers to configure
28
+ sort_by: Sort by
29
+ sort_by_featured: Featured
30
+ sort_by_on_sale: On Sale
31
+ sort_by_top_selling: Top Selling
32
+ sort_by_alphabetically_a_to_z: Alphabetically (A to Z)
33
+ sort_by_alphabetically_z_to_a: Alphabetically (Z to A)
34
+ sort_by_date_new_to_old: Date (New to Old)
35
+ sort_by_date_old_to_new: Date (Old to New)
36
+ sort_by_price_low_to_high: Price (Low to High)
37
+ sort_by_price_high_to_low: Price (High to Low)
38
+ home:
39
+ all_products: "All products"
40
+ featured: "Featured"
41
+ featured_categories: "Featured categories"
42
+ featured_products: "Featured products"
43
+ featured_video: ""
44
+ products:
45
+ add_to_cart: "Add to cart"
46
+ adding: "Adding..." # Used in few themes, intentionally not exposed in theme settings
47
+ added: "Added!" # Used in few themes, intentionally not exposed in theme settings
48
+ almost_sold_out: "Only a few left!"
49
+ coming_soon: "Coming soon"
50
+ description: "Description"
51
+ in_stock: "in stock"
52
+ inventory: "Inventory"
53
+ low_inventory: "Limited quantities available"
54
+ no_products: "No products found"
55
+ on_sale: "On sale"
56
+ related_products: "You might also like"
57
+ reset: "Reset"
58
+ search_results: "Search results"
59
+ select: "Select"
60
+ select_variant: "Select option"
61
+ sold_out: "Sold out"
62
+ cart:
63
+ checkout: "Checkout"
64
+ continue_shopping: "Continue shopping"
65
+ empty_cart: "Your cart is empty"
66
+ quantity_abbreviated: "Qty"
67
+ quantity: "Quantity"
68
+ remove: "Remove"
69
+ share_this_cart: "Share this cart"
70
+ share_this_cart_link_copy_success: "Link copied!"
71
+ subtotal: "Subtotal"
72
+ view_cart: "View cart"
73
+ contact:
74
+ email: "Email"
75
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
76
+ message: "Message"
77
+ name: "Name"
78
+ send_button: "Send message"
79
+ subject: "Subject"
@@ -0,0 +1,79 @@
1
+ en-GB:
2
+ storefront:
3
+ navigation:
4
+ all: "All"
5
+ all_products: "All products"
6
+ back_to_site: "Back to site"
7
+ cart: "Basket"
8
+ categories: "Categories"
9
+ contact: "Contact"
10
+ home: "Home"
11
+ item: "item"
12
+ items: "items"
13
+ more: "More"
14
+ next: "Next"
15
+ newest: "Newest"
16
+ pages: "Pages"
17
+ previous: "Previous"
18
+ products: "Products"
19
+ quick_view: "Quick view"
20
+ search: "Search"
21
+ shop: "Shop"
22
+ social: "Follow us"
23
+ subscribe: "Subscribe"
24
+ top_selling: "Top-selling"
25
+ view: "View"
26
+ view_all: "View all"
27
+ # "Sort By" translations are used in very few themes, and are not exposed in theme settings for sellers to configure
28
+ sort_by: Sort by
29
+ sort_by_featured: Featured
30
+ sort_by_on_sale: On Sale
31
+ sort_by_top_selling: Top Selling
32
+ sort_by_alphabetically_a_to_z: Alphabetically (A to Z)
33
+ sort_by_alphabetically_z_to_a: Alphabetically (Z to A)
34
+ sort_by_date_new_to_old: Date (New to Old)
35
+ sort_by_date_old_to_new: Date (Old to New)
36
+ sort_by_price_low_to_high: Price (Low to High)
37
+ sort_by_price_high_to_low: Price (High to Low)
38
+ home:
39
+ all_products: "All products"
40
+ featured: "Featured"
41
+ featured_categories: "Featured categories"
42
+ featured_products: "Featured products"
43
+ featured_video: ""
44
+ products:
45
+ add_to_cart: "Add to basket"
46
+ adding: "Adding..." # Used in few themes, intentionally not exposed in theme settings
47
+ added: "Added!" # Used in few themes, intentionally not exposed in theme settings
48
+ almost_sold_out: "Only a few left!"
49
+ coming_soon: "Coming soon"
50
+ description: "Description"
51
+ in_stock: "in stock"
52
+ inventory: "Stock"
53
+ low_inventory: "Limited quantities available"
54
+ no_products: "No products found"
55
+ on_sale: "On offer"
56
+ related_products: "You might also like"
57
+ reset: "Reset"
58
+ search_results: "Search results"
59
+ select: "Select"
60
+ select_variant: "Select option"
61
+ sold_out: "Sold out"
62
+ cart:
63
+ checkout: "Checkout"
64
+ continue_shopping: "Continue shopping"
65
+ empty_cart: "Your basket is empty"
66
+ quantity_abbreviated: "Qty"
67
+ quantity: "Quantity"
68
+ remove: "Remove"
69
+ share_this_cart: "Share this basket"
70
+ share_this_cart_link_copy_success: "Link copied!"
71
+ subtotal: "Subtotal"
72
+ view_cart: "View basket"
73
+ contact:
74
+ email: "Email"
75
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
76
+ message: "Message"
77
+ name: "Name"
78
+ send_button: "Send message"
79
+ subject: "Subject"
@@ -0,0 +1,79 @@
1
+ en-US:
2
+ storefront:
3
+ navigation:
4
+ all: "All"
5
+ all_products: "All products"
6
+ back_to_site: "Back to site"
7
+ cart: "Cart"
8
+ categories: "Categories"
9
+ contact: "Contact"
10
+ home: "Home"
11
+ item: "item"
12
+ items: "items"
13
+ more: "More"
14
+ next: "Next"
15
+ newest: "Newest"
16
+ pages: "Pages"
17
+ previous: "Previous"
18
+ products: "Products"
19
+ quick_view: "Quick view"
20
+ search: "Search"
21
+ shop: "Shop"
22
+ social: "Follow us"
23
+ subscribe: "Subscribe"
24
+ top_selling: "Top-selling"
25
+ view: "View"
26
+ view_all: "View all"
27
+ # "Sort By" translations are used in very few themes, and are not exposed in theme settings for sellers to configure
28
+ sort_by: Sort by
29
+ sort_by_featured: Featured
30
+ sort_by_on_sale: On Sale
31
+ sort_by_top_selling: Top Selling
32
+ sort_by_alphabetically_a_to_z: Alphabetically (A to Z)
33
+ sort_by_alphabetically_z_to_a: Alphabetically (Z to A)
34
+ sort_by_date_new_to_old: Date (New to Old)
35
+ sort_by_date_old_to_new: Date (Old to New)
36
+ sort_by_price_low_to_high: Price (Low to High)
37
+ sort_by_price_high_to_low: Price (High to Low)
38
+ home:
39
+ all_products: "All products"
40
+ featured: "Featured"
41
+ featured_categories: "Featured categories"
42
+ featured_products: "Featured products"
43
+ featured_video: ""
44
+ products:
45
+ add_to_cart: "Add to cart"
46
+ adding: "Adding..." # Used in few themes, intentionally not exposed in theme settings
47
+ added: "Added!" # Used in few themes, intentionally not exposed in theme settings
48
+ almost_sold_out: "Only a few left!"
49
+ coming_soon: "Coming soon"
50
+ description: "Description"
51
+ in_stock: "in stock"
52
+ inventory: "Inventory"
53
+ low_inventory: "Limited quantities available"
54
+ no_products: "No products found"
55
+ on_sale: "On sale"
56
+ related_products: "You might also like"
57
+ reset: "Reset"
58
+ search_results: "Search results"
59
+ select: "Select"
60
+ select_variant: "Select option"
61
+ sold_out: "Sold out"
62
+ cart:
63
+ checkout: "Checkout"
64
+ continue_shopping: "Continue shopping"
65
+ empty_cart: "Your cart is empty"
66
+ quantity_abbreviated: "Qty"
67
+ quantity: "Quantity"
68
+ remove: "Remove"
69
+ share_this_cart: "Share this cart"
70
+ share_this_cart_link_copy_success: "Link copied!"
71
+ subtotal: "Subtotal"
72
+ view_cart: "View cart"
73
+ contact:
74
+ email: "Email"
75
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
76
+ message: "Message"
77
+ name: "Name"
78
+ send_button: "Send message"
79
+ subject: "Subject"
@@ -0,0 +1,79 @@
1
+ es-ES:
2
+ storefront:
3
+ navigation:
4
+ all: "Todo"
5
+ all_products: "Todos los productos"
6
+ back_to_site: "Volver al sitio"
7
+ cart: "Carrito"
8
+ categories: "Categorías"
9
+ contact: "Contáctanos"
10
+ home: "Inicio"
11
+ item: "artículo"
12
+ items: "artículos"
13
+ more: "Más"
14
+ next: "Siguiente"
15
+ newest: "Novedades"
16
+ pages: "Páginas"
17
+ previous: "Anterior"
18
+ products: "Productos"
19
+ quick_view: "Vista rápida"
20
+ search: "Buscar"
21
+ shop: "Tienda"
22
+ social: "Síguenos"
23
+ subscribe: "Suscribirse"
24
+ top_selling: "Más vendidos"
25
+ view: "Ver"
26
+ view_all: "Ver todo"
27
+ # "Sort By" translations are used in very few themes, and are not exposed in theme settings for sellers to configure
28
+ sort_by: Ordenar por
29
+ sort_by_featured: Destacados
30
+ sort_by_on_sale: En oferta
31
+ sort_by_top_selling: Más vendidos
32
+ sort_by_alphabetically_a_to_z: Alfabético (A a Z)
33
+ sort_by_alphabetically_z_to_a: Alfabético (Z a A)
34
+ sort_by_date_new_to_old: Fecha (más reciente)
35
+ sort_by_date_old_to_new: Fecha (más antiguo)
36
+ sort_by_price_low_to_high: Precio (menor a mayor)
37
+ sort_by_price_high_to_low: Precio (mayor a menor)
38
+ home:
39
+ all_products: "Todos los productos"
40
+ featured: "Destacado"
41
+ featured_categories: "Categorías destacadas"
42
+ featured_products: "Productos destacados"
43
+ featured_video: ""
44
+ products:
45
+ add_to_cart: "Añadir"
46
+ added: "¡Añadido!" # Used in few themes, intentionally not exposed in theme settings
47
+ adding: "Añadiendo..." # Used in few themes, intentionally not exposed in theme settings
48
+ almost_sold_out: "¡Quedan pocos!"
49
+ coming_soon: "Próximamente"
50
+ in_stock: "en stock"
51
+ description: "Descripción"
52
+ inventory: "Existencias"
53
+ low_inventory: "Cantidades limitadas"
54
+ no_products: "No se encuentran productos"
55
+ on_sale: "En oferta"
56
+ related_products: "Te podría gustar"
57
+ reset: "Restablecer"
58
+ search_results: "Resultados de búsqueda"
59
+ select: "Selecciona"
60
+ select_variant: "Seleccionar opción"
61
+ sold_out: "Agotado"
62
+ cart:
63
+ checkout: "Comprar"
64
+ continue_shopping: "Continuar comprando"
65
+ empty_cart: "Tu carrito está vacío"
66
+ quantity_abbreviated: "Cant."
67
+ quantity: "Cantidad"
68
+ remove: "Eliminar"
69
+ share_this_cart: "Compartir este carrito"
70
+ share_this_cart_link_copy_success: "¡Enlace copiado!"
71
+ subtotal: "Subtotal"
72
+ view_cart: "Ver carrito"
73
+ contact:
74
+ email: "Correo electrónico"
75
+ form_success: "¡Gracias! Tu mensaje ha sido enviado y te responderemos pronto."
76
+ message: "Mensaje"
77
+ name: "Nombre"
78
+ send_button: "Enviar mensaje"
79
+ subject: "Asunto"