dugway 1.1.0 → 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.
- checksums.yaml +4 -4
- data/.bundle/config +2 -0
- data/.gitignore +1 -0
- data/lib/dugway/application.rb +5 -3
- data/lib/dugway/assets/big_cartel_logo.svg +4 -0
- data/lib/dugway/cli/build.rb +7 -1
- data/lib/dugway/cli/server.rb +2 -2
- data/lib/dugway/cli/templates/source/settings.json +8 -0
- data/lib/dugway/cli/validate.rb +9 -2
- data/lib/dugway/controller.rb +5 -1
- data/lib/dugway/liquid/drops/account_drop.rb +4 -0
- data/lib/dugway/liquid/drops/features_drop.rb +144 -0
- data/lib/dugway/liquid/drops/theme_drop.rb +23 -0
- data/lib/dugway/liquid/drops/translations_drop.rb +122 -0
- data/lib/dugway/liquifier.rb +44 -8
- data/lib/dugway/store.rb +7 -2
- data/lib/dugway/theme.rb +107 -10
- data/lib/dugway/version.rb +1 -1
- data/lib/dugway.rb +31 -1
- data/locales/storefront.de.yml +79 -0
- data/locales/storefront.en-CA.yml +79 -0
- data/locales/storefront.en-GB.yml +79 -0
- data/locales/storefront.en-US.yml +79 -0
- data/locales/storefront.es-ES.yml +79 -0
- data/locales/storefront.es-MX.yml +79 -0
- data/locales/storefront.fr-CA.yml +79 -0
- data/locales/storefront.fr-FR.yml +79 -0
- data/locales/storefront.id.yml +79 -0
- data/locales/storefront.it.yml +79 -0
- data/locales/storefront.ja.yml +79 -0
- data/locales/storefront.ko.yml +79 -0
- data/locales/storefront.nl.yml +79 -0
- data/locales/storefront.pl.yml +79 -0
- data/locales/storefront.pt-BR.yml +79 -0
- data/locales/storefront.pt-PT.yml +79 -0
- data/locales/storefront.ro.yml +79 -0
- data/locales/storefront.sv.yml +79 -0
- data/locales/storefront.tr.yml +79 -0
- data/locales/storefront.zh-CN.yml +79 -0
- data/locales/storefront.zh-TW.yml +79 -0
- data/log/dugway.log +1 -0
- data/spec/features/page_rendering_spec.rb +4 -4
- data/spec/fixtures/theme/layout.html +2 -0
- data/spec/fixtures/theme/settings.json +6 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/dugway/liquid/drops/features_drop_spec.rb +182 -0
- data/spec/units/dugway/liquid/drops/theme_drop_spec.rb +45 -0
- data/spec/units/dugway/liquid/drops/translations_drop_spec.rb +292 -0
- data/spec/units/dugway/store_spec.rb +37 -0
- data/spec/units/dugway/theme_spec.rb +297 -1
- metadata +32 -2
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
module Dugway
|
4
|
+
module Drops
|
5
|
+
class TranslationsDrop < BaseDrop
|
6
|
+
class MissingTranslationError < StandardError; end
|
7
|
+
|
8
|
+
# @param settings [Hash] Theme customization values (resolved settings).
|
9
|
+
# @param definitions [Hash] Raw theme setting definitions (from settings.json).
|
10
|
+
def initialize(settings, definitions = {})
|
11
|
+
@settings = settings || {}
|
12
|
+
@definitions = definitions || {} # Store definitions
|
13
|
+
# Read settings using both symbol and string keys for flexibility.
|
14
|
+
@translation_mode = (@settings[:translation_mode] || @settings['translation_mode'])&.to_s
|
15
|
+
# Use explicit theme setting for locale if present, otherwise default to current I18n.locale
|
16
|
+
@translation_locale = (@settings[:translation_locale] || @settings['translation_locale'])&.to_s.presence || I18n.locale.to_s
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
# Liquid access: `{{ translations['key.name'] }}`
|
21
|
+
# Returns the translation string (which could be an empty string),
|
22
|
+
# or a "[MISSING TRANSLATION: ...]" string if the key is not found or its value is nil.
|
23
|
+
#
|
24
|
+
# @param key [String, Symbol] The translation key.
|
25
|
+
# @return [String, nil] The translation or missing translation indicator.
|
26
|
+
def [](key)
|
27
|
+
key_str = key.to_s
|
28
|
+
translation = find_translation(key_str)
|
29
|
+
|
30
|
+
if translation.nil?
|
31
|
+
# Key is missing, or explicitly nil in manual mode - treat both as missing.
|
32
|
+
log_missing_translation(key_str)
|
33
|
+
return missing_translation_message(key_str)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Key found, return its value.
|
37
|
+
translation
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def manual_mode?
|
43
|
+
@translation_mode == 'manual'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Looks up translation using I18n.
|
47
|
+
# Returns nil if the key is not found or the locale is invalid.
|
48
|
+
def lookup_automatic_translation(key)
|
49
|
+
locale_to_use = @translation_locale
|
50
|
+
# `default: nil` prevents I18n from raising an error on missing keys.
|
51
|
+
I18n.t("storefront.#{key}", locale: locale_to_use, default: nil)
|
52
|
+
rescue I18n::InvalidLocale
|
53
|
+
nil # Treat invalid locale as a missing translation.
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts a canonical key ('a.b.c') to the theme setting key format (:a_b_c_tr_text).
|
57
|
+
def derive_manual_setting_key(key)
|
58
|
+
"#{key.tr('.', '_')}_tr_text".to_sym
|
59
|
+
end
|
60
|
+
|
61
|
+
# Finds the translation based on the current mode.
|
62
|
+
# In manual mode, respects the 'allow_blank' setting definition.
|
63
|
+
# Returns the translation string, or nil if not found or disallowed blank.
|
64
|
+
def find_translation(key_str)
|
65
|
+
if manual_mode?
|
66
|
+
manual_setting_key_sym = derive_manual_setting_key(key_str)
|
67
|
+
manual_setting_key_str = manual_setting_key_sym.to_s
|
68
|
+
custom_value = nil
|
69
|
+
setting_exists = false
|
70
|
+
|
71
|
+
# Check both symbol and string keys for manual overrides.
|
72
|
+
if @settings.key?(manual_setting_key_sym)
|
73
|
+
custom_value = @settings[manual_setting_key_sym]
|
74
|
+
setting_exists = true
|
75
|
+
elsif @settings.key?(manual_setting_key_str)
|
76
|
+
custom_value = @settings[manual_setting_key_str]
|
77
|
+
setting_exists = true
|
78
|
+
end
|
79
|
+
|
80
|
+
if setting_exists
|
81
|
+
# Find the corresponding definition to check allow_blank
|
82
|
+
# Assuming manual translations are defined similarly to 'options'
|
83
|
+
definition = find_setting_definition(manual_setting_key_str) || {}
|
84
|
+
allow_blank = definition['allow_blank'].to_s == 'true'
|
85
|
+
|
86
|
+
# Return custom value if it's present, or if it's blank but allowed
|
87
|
+
return custom_value if custom_value.present? || (allow_blank && custom_value == '')
|
88
|
+
|
89
|
+
# If we reach here, the value is blank but blank is not allowed.
|
90
|
+
# Fall through to automatic lookup.
|
91
|
+
end
|
92
|
+
|
93
|
+
# If setting key didn't exist, fall through to automatic lookup.
|
94
|
+
|
95
|
+
end # End manual_mode? check
|
96
|
+
|
97
|
+
# Automatic mode or fallback from manual mode
|
98
|
+
lookup_automatic_translation(key_str)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Helper to find a setting definition by its variable name across common sections.
|
102
|
+
def find_setting_definition(variable_name)
|
103
|
+
# Look primarily in 'options' as manual translations often mimic them.
|
104
|
+
# Add other potential sections if needed in the future.
|
105
|
+
(@definitions['options'] || []).find { |d| d['variable'] == variable_name }
|
106
|
+
end
|
107
|
+
|
108
|
+
# Logs a warning message for a missing translation.
|
109
|
+
def log_missing_translation(key_str)
|
110
|
+
mode = manual_mode? ? 'manual' : 'automatic'
|
111
|
+
locale_info = manual_mode? ? '' : " locale='#{@translation_locale}'"
|
112
|
+
message = "Missing translation: key='#{key_str}'#{locale_info} mode='#{mode}'"
|
113
|
+
Dugway.logger.warn(message)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the standard string indicating a missing translation.
|
117
|
+
def missing_translation_message(key_str)
|
118
|
+
"[MISSING TRANSLATION: #{key_str}]"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/dugway/liquifier.rb
CHANGED
@@ -16,6 +16,7 @@ module Dugway
|
|
16
16
|
class Liquifier
|
17
17
|
def initialize(request)
|
18
18
|
@request = request
|
19
|
+
load_theme_locales
|
19
20
|
end
|
20
21
|
|
21
22
|
def render(content, variables={})
|
@@ -41,7 +42,7 @@ module Dugway
|
|
41
42
|
|
42
43
|
def self.render_styles(css)
|
43
44
|
Liquid::Template.parse(css).render!(
|
44
|
-
{ 'theme' => Drops::ThemeDrop.new(Dugway.theme.customization) },
|
45
|
+
{ 'theme' => Drops::ThemeDrop.new(Dugway.theme.customization, Dugway.theme.settings) },
|
45
46
|
:registers => { :settings => Dugway.theme.settings }
|
46
47
|
)
|
47
48
|
end
|
@@ -68,7 +69,8 @@ module Dugway
|
|
68
69
|
{
|
69
70
|
'store' => Drops::AccountDrop.new(store.account),
|
70
71
|
'cart' => Drops::CartDrop.new(cart),
|
71
|
-
|
72
|
+
# Pass both customization and definitions to ThemeDrop
|
73
|
+
'theme' => Drops::ThemeDrop.new(theme.customization, theme.settings),
|
72
74
|
'pages' => Drops::PagesDrop.new(store.pages.map { |p| Drops::PageDrop.new(p) }),
|
73
75
|
'categories' => Drops::CategoriesDrop.new(store.categories.map { |c| Drops::CategoryDrop.new(c) }),
|
74
76
|
'artists' => Drops::ArtistsDrop.new(store.artists.map { |a| Drops::ArtistDrop.new(a) }),
|
@@ -77,6 +79,10 @@ module Dugway
|
|
77
79
|
'head_content' => [window_bigcartel_script, head_content].join,
|
78
80
|
'bigcartel_credit' => bigcartel_credit,
|
79
81
|
'powered_by_big_cartel' => powered_by_big_cartel,
|
82
|
+
'big_cartel_credit_logo' => big_cartel_credit_logo,
|
83
|
+
# Pass both customization and definitions to TranslationsDrop
|
84
|
+
'translations' => Drops::TranslationsDrop.new(theme.customization, theme.settings),
|
85
|
+
't' => Drops::TranslationsDrop.new(theme.customization, theme.settings),
|
80
86
|
}
|
81
87
|
end
|
82
88
|
|
@@ -109,17 +115,47 @@ module Dugway
|
|
109
115
|
"<script>#{script}</script>"
|
110
116
|
end
|
111
117
|
|
118
|
+
def read_svg_asset(filename)
|
119
|
+
File.read(File.join(File.dirname(__FILE__), 'assets', filename))
|
120
|
+
end
|
121
|
+
|
112
122
|
def bigcartel_credit
|
113
123
|
'<a href="http://bigcartel.com/" title="Start your own store at Big Cartel now">Online Store by Big Cartel</a>'
|
114
124
|
end
|
115
125
|
|
116
126
|
def powered_by_big_cartel
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
127
|
+
build_powered_by_link(include_text: true)
|
128
|
+
end
|
129
|
+
|
130
|
+
def big_cartel_credit_logo
|
131
|
+
build_powered_by_link(include_text: false)
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_powered_by_link(include_text: true)
|
135
|
+
svg_content = read_svg_asset('big_cartel_logo.svg')
|
136
|
+
|
137
|
+
text_span = include_text ? ' <span class="bigcartel-credit__text" aria-hidden="true">Powered by</span>' : ''
|
138
|
+
<<~HTML
|
139
|
+
<a class="bigcartel-credit" href="https://www.bigcartel.com/?utm_source=bigcartel&utm_medium=storefront&utm_campaign=123}" title="Powered by Big Cartel" data-bc-hook="attribution">
|
140
|
+
#{text_span}
|
141
|
+
#{svg_content}
|
142
|
+
</a>
|
143
|
+
HTML
|
144
|
+
end
|
145
|
+
|
146
|
+
# Load theme-specific override locales
|
147
|
+
def load_theme_locales
|
148
|
+
return unless Dugway.source_dir && Dir.exist?(File.dirname(Dugway.source_dir))
|
149
|
+
|
150
|
+
parent_dir = File.dirname(Dugway.source_dir)
|
151
|
+
theme_locales_dir = File.join(parent_dir, 'locales')
|
152
|
+
|
153
|
+
if Dir.exist?(theme_locales_dir)
|
154
|
+
theme_locales_path = File.expand_path(theme_locales_dir)
|
155
|
+
# Ensure theme locales are loaded *after* defaults to allow overrides
|
156
|
+
I18n.load_path |= Dir[File.join(theme_locales_path, 'storefront.*.yml')]
|
157
|
+
I18n.backend.reload!
|
158
|
+
end
|
123
159
|
end
|
124
160
|
end
|
125
161
|
end
|
data/lib/dugway/store.rb
CHANGED
@@ -8,8 +8,9 @@ module Dugway
|
|
8
8
|
default_timeout 5
|
9
9
|
headers 'User-Agent' => "Dugway #{ Dugway::VERSION }"
|
10
10
|
|
11
|
-
def initialize(subdomain)
|
11
|
+
def initialize(subdomain, store_options = {})
|
12
12
|
self.class.base_uri "https://api.bigcartel.com/#{ subdomain }"
|
13
|
+
@store_options = store_options || {}
|
13
14
|
end
|
14
15
|
|
15
16
|
def account
|
@@ -124,7 +125,11 @@ module Dugway
|
|
124
125
|
end
|
125
126
|
|
126
127
|
def locale
|
127
|
-
currency['locale']
|
128
|
+
@store_options[:locale] || currency['locale']
|
129
|
+
end
|
130
|
+
|
131
|
+
def website
|
132
|
+
@store_options[:website] || account['website']
|
128
133
|
end
|
129
134
|
|
130
135
|
def instant_checkout?
|
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
|
-
|
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,7 @@ 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
|
+
valid_operators = %w(eq neq gt lt gte lte present)
|
257
298
|
|
258
299
|
settings['options'].each do |option|
|
259
300
|
next unless option['requires']
|
@@ -277,22 +318,78 @@ module Dugway
|
|
277
318
|
option['requires'].each do |rule|
|
278
319
|
next if rule == 'inventory'
|
279
320
|
|
280
|
-
# Extract setting name
|
281
|
-
|
282
|
-
setting_name =
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
286
345
|
end
|
287
346
|
|
288
|
-
#
|
347
|
+
# --- Non-feature rule validation ---
|
289
348
|
unless all_variables.include?(setting_name)
|
290
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(', ')}."
|
291
356
|
end
|
292
357
|
end
|
293
358
|
end
|
294
359
|
end
|
295
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
|
296
393
|
|
297
394
|
def source_dir
|
298
395
|
Dugway.source_dir
|
data/lib/dugway/version.rb
CHANGED
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
|
-
|
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"
|