dugway 1.1.0 → 1.3.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 (69) 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 +32 -7
  6. data/dugway.gemspec +11 -9
  7. data/lib/dugway/application.rb +5 -3
  8. data/lib/dugway/assets/big_cartel_logo.svg +4 -0
  9. data/lib/dugway/cli/build.rb +7 -1
  10. data/lib/dugway/cli/server.rb +69 -9
  11. data/lib/dugway/cli/templates/source/settings.json +8 -0
  12. data/lib/dugway/cli/validate.rb +9 -2
  13. data/lib/dugway/controller.rb +5 -1
  14. data/lib/dugway/liquid/drops/account_drop.rb +4 -0
  15. data/lib/dugway/liquid/drops/artists_drop.rb +12 -0
  16. data/lib/dugway/liquid/drops/base_drop.rb +27 -2
  17. data/lib/dugway/liquid/drops/categories_drop.rb +12 -0
  18. data/lib/dugway/liquid/drops/features_drop.rb +144 -0
  19. data/lib/dugway/liquid/drops/pages_drop.rb +30 -2
  20. data/lib/dugway/liquid/drops/product_drop.rb +11 -0
  21. data/lib/dugway/liquid/drops/products_drop.rb +39 -0
  22. data/lib/dugway/liquid/drops/theme_drop.rb +52 -6
  23. data/lib/dugway/liquid/drops/translations_drop.rb +122 -0
  24. data/lib/dugway/liquid/filters/core_filters.rb +169 -7
  25. data/lib/dugway/liquid/filters/font_filters.rb +1 -0
  26. data/lib/dugway/liquid/filters/util_filters.rb +1 -10
  27. data/lib/dugway/liquid/tags/get.rb +6 -6
  28. data/lib/dugway/liquid/tags/paginate.rb +61 -11
  29. data/lib/dugway/liquifier.rb +44 -8
  30. data/lib/dugway/store.rb +46 -3
  31. data/lib/dugway/theme.rb +151 -15
  32. data/lib/dugway/version.rb +1 -1
  33. data/lib/dugway.rb +55 -2
  34. data/locales/storefront.de.yml +81 -0
  35. data/locales/storefront.en-CA.yml +81 -0
  36. data/locales/storefront.en-GB.yml +81 -0
  37. data/locales/storefront.en-US.yml +81 -0
  38. data/locales/storefront.es-ES.yml +81 -0
  39. data/locales/storefront.es-MX.yml +81 -0
  40. data/locales/storefront.fr-CA.yml +81 -0
  41. data/locales/storefront.fr-FR.yml +81 -0
  42. data/locales/storefront.id.yml +81 -0
  43. data/locales/storefront.it.yml +81 -0
  44. data/locales/storefront.ja.yml +81 -0
  45. data/locales/storefront.ko.yml +81 -0
  46. data/locales/storefront.nl.yml +81 -0
  47. data/locales/storefront.pl.yml +81 -0
  48. data/locales/storefront.pt-BR.yml +81 -0
  49. data/locales/storefront.pt-PT.yml +81 -0
  50. data/locales/storefront.ro.yml +81 -0
  51. data/locales/storefront.sv.yml +81 -0
  52. data/locales/storefront.tr.yml +81 -0
  53. data/locales/storefront.zh-CN.yml +81 -0
  54. data/locales/storefront.zh-TW.yml +81 -0
  55. data/log/dugway.log +1 -0
  56. data/mise.toml +2 -0
  57. data/spec/features/page_rendering_spec.rb +4 -4
  58. data/spec/fixtures/theme/layout.html +2 -0
  59. data/spec/fixtures/theme/settings.json +6 -0
  60. data/spec/spec_helper.rb +7 -0
  61. data/spec/units/dugway/liquid/drops/features_drop_spec.rb +182 -0
  62. data/spec/units/dugway/liquid/drops/pages_drop_spec.rb +186 -7
  63. data/spec/units/dugway/liquid/drops/product_drop_spec.rb +17 -0
  64. data/spec/units/dugway/liquid/drops/theme_drop_spec.rb +45 -0
  65. data/spec/units/dugway/liquid/drops/translations_drop_spec.rb +292 -0
  66. data/spec/units/dugway/liquid/filters/core_filters_spec.rb +301 -3
  67. data/spec/units/dugway/store_spec.rb +55 -0
  68. data/spec/units/dugway/theme_spec.rb +543 -1
  69. metadata +84 -25
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
 
@@ -95,6 +101,7 @@ module Dugway
95
101
 
96
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?
@@ -117,7 +124,14 @@ module Dugway
117
124
 
118
125
  validate_required_color_settings if validate_colors
119
126
  validate_required_layout_attributes if validate_layout_attributes
120
- validate_options_settings if validate_options
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
121
135
 
122
136
  @errors.empty?
123
137
  end
@@ -164,15 +178,41 @@ module Dugway
164
178
  @errors << "layout.html must have exactly one `data-bc-hook=\"footer\"`" if footer_hooks != 1
165
179
  end
166
180
 
181
+ VALID_SECTIONS = %w(global_navigation homepage product_page general messaging social translations).freeze
182
+
167
183
  def validate_options_settings
168
184
  return unless settings['options']
169
185
 
170
186
  validate_options_descriptions
171
187
  validate_options_requires
188
+ validate_option_defaults
172
189
  end
173
190
 
174
191
  private
175
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
+
176
216
  def validate_preview
177
217
  preview = settings['preset_styles']['preview']
178
218
  if preview
@@ -254,6 +294,10 @@ module Dugway
254
294
  def validate_options_requires
255
295
  return unless settings['options']
256
296
  all_variables = settings['options'].map { |o| o['variable'] }
297
+
298
+ # Include variables from images and image_sets settings
299
+ all_variables.concat(settings['images'].map { |i| i['variable'] }) if settings['images']
300
+ all_variables.concat(settings['image_sets'].map { |i| i['variable'] }) if settings['image_sets']
257
301
 
258
302
  settings['options'].each do |option|
259
303
  next unless option['requires']
@@ -275,24 +319,116 @@ module Dugway
275
319
 
276
320
  # Process each rule in the array
277
321
  option['requires'].each do |rule|
278
- next if rule == 'inventory'
322
+ validate_requires_rule(rule, option['variable'], all_variables)
323
+ end
324
+ end
325
+ end
279
326
 
280
- # Extract setting name from rule
281
- # Handle both simple cases ("show_search") and complex cases ("show_search eq true")
282
- setting_name = if rule.include?(' ')
283
- rule.split(/\s+/).first
284
- else
285
- rule
286
- end
327
+ # Validates individual requires rule - handles both string rules and object rules (like 'any')
328
+ def validate_requires_rule(rule, option_variable, all_variables)
329
+ # Handle object-based rules (like {"any": [...]})
330
+ if rule.is_a?(Hash)
331
+ if rule.has_key?('any')
332
+ validate_any_condition(rule['any'], option_variable, all_variables)
333
+ else
334
+ @errors << "Option '#{option_variable}' has unsupported requires object. Only 'any' is currently supported."
335
+ end
336
+ return
337
+ end
287
338
 
288
- # Verify the referenced setting exists
289
- unless all_variables.include?(setting_name)
290
- @errors << "Option '#{option['variable']}' requires unknown setting '#{setting_name}'"
291
- end
339
+ # Handle string-based rules (existing logic)
340
+ return if rule == 'inventory'
341
+
342
+ valid_operators = %w(eq neq gt lt gte lte present)
343
+
344
+ # Extract setting name and optional operator/value
345
+ parts = rule.split(/\s+/)
346
+ setting_name = parts[0]
347
+ operator = parts[1]
348
+ value = parts[2]
349
+
350
+ if setting_name.start_with?('feature:')
351
+ # Validate feature flag format
352
+ unless setting_name =~ /^feature:[a-z_]+$/
353
+ @errors << "Option '#{option_variable}' has invalid feature flag format"
354
+ return
292
355
  end
356
+
357
+ # Only validate operator/value if they exist
358
+ if operator
359
+ is_valid_operator = ['eq', 'neq'].include?(operator)
360
+
361
+ @errors << "Option '#{option_variable}' has invalid operator '#{operator}'. Feature flags can only use `eq` or `neq`." unless is_valid_operator
362
+
363
+ is_valid_value = ['visible'].include?(value)
364
+
365
+ @errors << "Option '#{option_variable}' has invalid comparison value '#{value}'. Feature flags can only check for `visible`." unless is_valid_value
366
+ end
367
+ return
368
+ end
369
+
370
+ # --- Non-feature rule validation ---
371
+ unless all_variables.include?(setting_name)
372
+ @errors << "Option '#{option_variable}' requires unknown setting '#{setting_name}'"
373
+ return # Skip further checks for this rule if setting is unknown
374
+ end
375
+
376
+ # Validate operator if present
377
+ if operator && !valid_operators.include?(operator)
378
+ @errors << "Option '#{option_variable}' has invalid operator '#{operator}'. Allowed operators are: #{valid_operators.join(', ')}."
379
+ end
380
+ end
381
+
382
+ # Validates 'any' condition - ensures it's an array and validates each nested rule
383
+ def validate_any_condition(any_rules, option_variable, all_variables)
384
+ unless any_rules.is_a?(Array)
385
+ @errors << "Option '#{option_variable}' has invalid 'any' condition - must be an array of rules"
386
+ return
387
+ end
388
+
389
+ if any_rules.empty?
390
+ @errors << "Option '#{option_variable}' has empty 'any' condition - must contain at least one rule"
391
+ return
392
+ end
393
+
394
+ # Validate each rule within the 'any' condition
395
+ any_rules.each do |nested_rule|
396
+ validate_requires_rule(nested_rule, option_variable, all_variables)
397
+ end
398
+ end
399
+
400
+ def validate_option_value(option, value_type)
401
+ value = option[value_type]
402
+ return unless value
403
+
404
+ case option['type']
405
+ when 'select'
406
+ validate_select_option(option, value, value_type)
407
+ when 'boolean'
408
+ @errors << "#{value_type.capitalize} '#{value}' is not a boolean for #{option['variable']}" unless [true, false].include?(value)
293
409
  end
294
410
  end
295
411
 
412
+ def validate_option_defaults
413
+ return unless settings['options']
414
+ settings['options'].each do |option|
415
+ validate_option_value(option, 'default')
416
+ validate_option_value(option, 'upgrade_default')
417
+ end
418
+ end
419
+
420
+ def validate_select_option(option, value, value_type)
421
+ if option['options'] == "product_orders"
422
+ valid_values = %w[position top-selling newest views updated_new_to_old]
423
+ @errors << "#{value_type.capitalize} '#{value}' is not a valid option for #{option['variable']}" unless valid_values.include?(value)
424
+ elsif option['options'].is_a?(String) && option['options'] =~ /^\d+\.\.\d+$/
425
+ range = eval(option['options'])
426
+ @errors << "#{value_type.capitalize} '#{value}' is out of range for #{option['variable']}" unless range.include?(value.to_i)
427
+ elsif option['options'].is_a?(Array)
428
+ valid_values = option['options'].map { |o| o.is_a?(Array) ? o[1] : o }
429
+ @errors << "#{value_type.capitalize} '#{value}' is not a valid option for #{option['variable']}" unless valid_values.include?(value)
430
+ end
431
+ end
296
432
 
297
433
  def source_dir
298
434
  Dugway.source_dir
@@ -1,3 +1,3 @@
1
1
  module Dugway
2
- VERSION = "1.1.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/dugway.rb CHANGED
@@ -11,9 +11,45 @@ end
11
11
 
12
12
  require 'active_support/all'
13
13
  require 'i18n'
14
+ require 'i18n/backend/fallbacks'
14
15
  require 'bigcartel-currency-locales'
15
16
  require 'bigcartel/theme/fonts'
16
17
 
18
+ # Configure I18n to load locale files from the gem's locales directory.
19
+ # This allows for easy addition and management of translations.
20
+ gem_locales_pattern = File.expand_path('../locales/storefront.*.yml', File.dirname(__FILE__))
21
+ gem_locale_files = Dir.glob(gem_locales_pattern)
22
+
23
+ # Add these locale files to the I18n load path, ensuring no duplicates and preserving existing paths.
24
+ I18n.load_path = (I18n.load_path + gem_locale_files).uniq
25
+
26
+ # Determine available locales from the filenames (e.g., 'storefront.en-US.yml' -> :'en-US').
27
+ available_locales = gem_locale_files.map do |file|
28
+ # Extracts 'en-US' from 'storefront.en-US.yml' or 'de' from 'storefront.de.yml'
29
+ locale_string = File.basename(file, '.yml').split('storefront.').last
30
+ locale_string.to_sym if locale_string
31
+ end.compact.uniq
32
+
33
+ # Ensure :en is an available locale if any 'en-XX' variant exists (e.g., :en-US, :en-GB),
34
+ # as I18n can use :en as a fallback.
35
+ if available_locales.any? { |loc| loc.to_s.start_with?('en-') } && !available_locales.include?(:en)
36
+ available_locales << :en
37
+ end
38
+
39
+ # Set available locales. Provide a default if no locale files are found (e.g., during initial setup or in case of an error).
40
+ I18n.config.available_locales = available_locales.empty? ? [:'en-US', :en] : available_locales
41
+
42
+ # Enable fallbacks for I18n
43
+ # This allows sv-SE to fallback to sv, en-GB to en, etc.
44
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
45
+
46
+ # Disable enforce_available_locales to allow invalid locales like 'eu'
47
+ # We'll handle them gracefully with fallbacks
48
+ I18n.enforce_available_locales = false
49
+
50
+ # Reload I18n to apply changes and load the translations from the files.
51
+ I18n.backend.reload!
52
+
17
53
  require 'rack/builder'
18
54
  require 'rack/commonlogger'
19
55
  require 'better_errors'
@@ -36,7 +72,21 @@ module Dugway
36
72
 
37
73
  BigCartel::CurrencyLocales.insert
38
74
  I18n.default_locale = 'en-US'
39
- I18n.locale = Dugway.store.locale
75
+
76
+ # Set locale with fallback handling for invalid or missing locales
77
+ begin
78
+ requested_locale = Dugway.store.locale
79
+ if requested_locale
80
+ # Try to set the locale - if it's completely invalid (like 'eu'),
81
+ # this will fall back to default_locale due to enforce_available_locales = false
82
+ I18n.locale = requested_locale
83
+ else
84
+ I18n.locale = I18n.default_locale
85
+ end
86
+ rescue I18n::InvalidLocale
87
+ # If somehow we still get an invalid locale error, fall back to default
88
+ I18n.locale = I18n.default_locale
89
+ end
40
90
 
41
91
  Rack::Builder.app do
42
92
  use Rack::Session::Cookie, :secret => 'stopwarningmeaboutnothavingasecret'
@@ -52,7 +102,9 @@ module Dugway
52
102
  end
53
103
 
54
104
  def store
55
- @store ||= Store.new(options && options[:store] && options[:store][:subdomain] || 'dugway')
105
+ store_options = options && options[:store] ? options[:store] : {}
106
+ subdomain = store_options[:subdomain] || 'dugway'
107
+ @store ||= Store.new(subdomain, store_options)
56
108
  end
57
109
 
58
110
  def theme
@@ -74,5 +126,6 @@ module Dugway
74
126
  def options
75
127
  @options
76
128
  end
129
+
77
130
  end
78
131
  end
@@ -0,0 +1,81 @@
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
+ variant: "Option"
63
+ variants: "Optionen"
64
+ cart:
65
+ checkout: "Zur Kasse"
66
+ continue_shopping: "Weiter einkaufen"
67
+ empty_cart: "Dein Warenkorb ist leer"
68
+ quantity_abbreviated: "Anz."
69
+ quantity: "Anzahl"
70
+ remove: "Entfernen"
71
+ share_this_cart: "Diesen Warenkorb teilen"
72
+ share_this_cart_link_copy_success: "Link kopiert!"
73
+ subtotal: "Zwischensumme"
74
+ view_cart: "Warenkorb ansehen"
75
+ contact:
76
+ email: "E-Mail"
77
+ form_success: "Danke! Deine Nachricht wurde gesendet und wir werden uns bald bei dir melden."
78
+ message: "Nachricht"
79
+ name: "Name"
80
+ send_button: "Nachricht senden"
81
+ subject: "Betreff"
@@ -0,0 +1,81 @@
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
+ variant: "Option"
63
+ variants: "Options"
64
+ cart:
65
+ checkout: "Checkout"
66
+ continue_shopping: "Continue shopping"
67
+ empty_cart: "Your cart is empty"
68
+ quantity_abbreviated: "Qty"
69
+ quantity: "Quantity"
70
+ remove: "Remove"
71
+ share_this_cart: "Share this cart"
72
+ share_this_cart_link_copy_success: "Link copied!"
73
+ subtotal: "Subtotal"
74
+ view_cart: "View cart"
75
+ contact:
76
+ email: "Email"
77
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
78
+ message: "Message"
79
+ name: "Name"
80
+ send_button: "Send message"
81
+ subject: "Subject"
@@ -0,0 +1,81 @@
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
+ variant: "Option"
63
+ variants: "Options"
64
+ cart:
65
+ checkout: "Checkout"
66
+ continue_shopping: "Continue shopping"
67
+ empty_cart: "Your basket is empty"
68
+ quantity_abbreviated: "Qty"
69
+ quantity: "Quantity"
70
+ remove: "Remove"
71
+ share_this_cart: "Share this basket"
72
+ share_this_cart_link_copy_success: "Link copied!"
73
+ subtotal: "Subtotal"
74
+ view_cart: "View basket"
75
+ contact:
76
+ email: "Email"
77
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
78
+ message: "Message"
79
+ name: "Name"
80
+ send_button: "Send message"
81
+ subject: "Subject"
@@ -0,0 +1,81 @@
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
+ variant: "Option"
63
+ variants: "Options"
64
+ cart:
65
+ checkout: "Checkout"
66
+ continue_shopping: "Continue shopping"
67
+ empty_cart: "Your cart is empty"
68
+ quantity_abbreviated: "Qty"
69
+ quantity: "Quantity"
70
+ remove: "Remove"
71
+ share_this_cart: "Share this cart"
72
+ share_this_cart_link_copy_success: "Link copied!"
73
+ subtotal: "Subtotal"
74
+ view_cart: "View cart"
75
+ contact:
76
+ email: "Email"
77
+ form_success: "Thanks! Your message has been sent and we'll get back to you soon."
78
+ message: "Message"
79
+ name: "Name"
80
+ send_button: "Send message"
81
+ subject: "Subject"