shimmer 0.0.1 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36dcc714d5d4ddff50af58d02889b9c193754c8a8c8a48b38e989f898805adc5
4
- data.tar.gz: 3a3d6e7b848df467c7aa6ceb2f51071cd325f06da912dbc2926f0f7b7d986193
3
+ metadata.gz: f4c44b134bb61f78f82008a1907c738d358b48147ad6518211b7ffcef9fa4aab
4
+ data.tar.gz: 4b7eb93d01ed55818f0845498e3b33a531352dde7d72b491addded5a77f81ce5
5
5
  SHA512:
6
- metadata.gz: 7716f31b96c8d62da7c94151b59a6006ef12ddd918314aceb3026094cfb414634285e583979e0bc6b0932e16d36e95b69199d0e83c03933148ceaaf3002eac0d
7
- data.tar.gz: 64803d66f26ed93da2265b453493bfe768344273cabb9e0c077678d7c881d73eebdee1701801767adef198da8cda100cc72f5486687c56df2fdb8de865f84dac
6
+ metadata.gz: 49e8e4866c5d8bafc59d5ccbcac324afd214d2e83f31845a3ff207400059f25061629261ab494fbaf2d2ce1f8be575f978938f11b347d7e751606724e48b666d
7
+ data.tar.gz: 1e0892571dc244cd238be164c61d4c1b5480e38a994dc266c1d8e8652102a89efcf4ffa8686dd7d8d439dad8aad5080b2faadaa6b3b426f933567a36e16e7b7e
data/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ ; EditorConfig is awesome: http://EditorConfig.org
2
+
3
+ ; top-most EditorConfig file
4
+ root = true
5
+
6
+ ; Unix-style newlines with a newline ending every file
7
+ [*]
8
+ indent_style = spaces
9
+ end_of_line = lf
10
+ insert_final_newline = true
11
+ trim_trailing_whitespace = true
12
+ indent_size = 2
data/.eslintrc.js ADDED
@@ -0,0 +1,25 @@
1
+ module.exports = {
2
+ env: {
3
+ browser: true,
4
+ es6: true,
5
+ },
6
+ parser: "@typescript-eslint/parser",
7
+ extends: [
8
+ "plugin:@typescript-eslint/recommended",
9
+ "plugin:prettier/recommended",
10
+ ],
11
+ parserOptions: {
12
+ ecmaVersion: 2018,
13
+ sourceType: "module",
14
+ },
15
+ plugins: ["@typescript-eslint"],
16
+ rules: {
17
+ "no-console": 2,
18
+ "@typescript-eslint/explicit-function-return-type": [
19
+ "error",
20
+ {
21
+ allowExpressions: true,
22
+ },
23
+ ],
24
+ },
25
+ };
data/.prettierrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "trailingComma": "es5"
3
+ }
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/.solargraph.yml ADDED
@@ -0,0 +1,4 @@
1
+ plugins:
2
+ - solargraph-standardrb
3
+ reporters:
4
+ - standardrb
@@ -0,0 +1,3 @@
1
+ {
2
+ "solargraph.useBundler": true
3
+ }
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "rake", "~> 13.0"
9
+ gem "standard"
10
+ gem "solargraph-standardrb"
data/Gemfile.lock ADDED
@@ -0,0 +1,87 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shimmer (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ backport (1.2.0)
11
+ benchmark (0.2.0)
12
+ diff-lcs (1.4.4)
13
+ e2mmap (0.1.0)
14
+ jaro_winkler (1.5.4)
15
+ kramdown (2.3.1)
16
+ rexml
17
+ kramdown-parser-gfm (1.1.0)
18
+ kramdown (~> 2.0)
19
+ nokogiri (1.12.5-x86_64-darwin)
20
+ racc (~> 1.4)
21
+ nokogiri (1.12.5-x86_64-linux)
22
+ racc (~> 1.4)
23
+ parallel (1.21.0)
24
+ parser (3.0.3.2)
25
+ ast (~> 2.4.1)
26
+ racc (1.6.0)
27
+ rainbow (3.0.0)
28
+ rake (13.0.6)
29
+ regexp_parser (2.2.0)
30
+ reverse_markdown (2.1.1)
31
+ nokogiri
32
+ rexml (3.2.5)
33
+ rubocop (1.23.0)
34
+ parallel (~> 1.10)
35
+ parser (>= 3.0.0.0)
36
+ rainbow (>= 2.2.2, < 4.0)
37
+ regexp_parser (>= 1.8, < 3.0)
38
+ rexml
39
+ rubocop-ast (>= 1.12.0, < 2.0)
40
+ ruby-progressbar (~> 1.7)
41
+ unicode-display_width (>= 1.4.0, < 3.0)
42
+ rubocop-ast (1.15.0)
43
+ parser (>= 3.0.1.1)
44
+ rubocop-performance (1.12.0)
45
+ rubocop (>= 1.7.0, < 2.0)
46
+ rubocop-ast (>= 0.4.0)
47
+ ruby-progressbar (1.11.0)
48
+ solargraph (0.44.2)
49
+ backport (~> 1.2)
50
+ benchmark
51
+ bundler (>= 1.17.2)
52
+ diff-lcs (~> 1.4)
53
+ e2mmap
54
+ jaro_winkler (~> 1.5)
55
+ kramdown (~> 2.3)
56
+ kramdown-parser-gfm (~> 1.1)
57
+ parser (~> 3.0)
58
+ reverse_markdown (>= 1.0.5, < 3)
59
+ rubocop (>= 0.52)
60
+ thor (~> 1.0)
61
+ tilt (~> 2.0)
62
+ yard (~> 0.9, >= 0.9.24)
63
+ solargraph-standardrb (0.0.4)
64
+ solargraph (>= 0.39.1)
65
+ standard (>= 0.4.1)
66
+ standard (1.5.0)
67
+ rubocop (= 1.23.0)
68
+ rubocop-performance (= 1.12.0)
69
+ thor (1.1.0)
70
+ tilt (2.0.10)
71
+ unicode-display_width (2.1.0)
72
+ webrick (1.7.0)
73
+ yard (0.9.27)
74
+ webrick (~> 1.7.0)
75
+
76
+ PLATFORMS
77
+ x86_64-darwin-21
78
+ x86_64-linux
79
+
80
+ DEPENDENCIES
81
+ rake (~> 13.0)
82
+ shimmer!
83
+ solargraph-standardrb
84
+ standard
85
+
86
+ BUNDLED WITH
87
+ 2.2.32
data/README.md CHANGED
@@ -1,38 +1,196 @@
1
- # Shimmer
1
+ # Shimmer - Because Ruby could be more shiny!
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/shimmer`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Shimmer is a collection of Rails extensions that bring advanced UI features into your app and make your life easier as a developer.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ ## Features
6
+
7
+ ### Static File Serving
8
+
9
+ `ActiveStorage` is great, but serving of files, especially behind a CDN, can be complicated to get right. This can be fixed easily:
10
+
11
+ ```ruby
12
+ # use an image tag
13
+ image_tag user.avatar, width: 300
14
+ ```
15
+
16
+ This extension overrides `image_tag` and also supplies a matching `image_file_url` that automatically resizes your image and creates a static, cacheable url.
17
+
18
+ ### Modals
19
+
20
+ Modals are the designer's best friend, but developers usually hate them for their complexity. Fear no more: Shimmer has you covered.
21
+
22
+ ```slim
23
+ a href=modal_path(new_post_path) Create a new Post
24
+ ```
25
+
26
+ This will open a modal on click and then asynchronously request the modal content from the controller. Modals can also be controlled via JavaScript via the global `ui` variable:
27
+
28
+ ```js
29
+ ui.modal.open({ url: "/posts/new" });
30
+ ui.modal.close();
31
+ ```
32
+
33
+ ### Popovers
34
+
35
+ When modals are annoying to implement, popovers are even worse. Thankfully, Shimmer comes to the rescue:
36
+
37
+ ```slim
38
+ a href=popover_path(new_post_path, placement: :left)
39
+ ```
40
+
41
+ This will request `new_post_path` and display it left of the anchor thanks to PopperJS.
42
+
43
+ ### Remote Navigation
44
+
45
+ Remote navigation takes Hotwire to the next level with built-in navigation actions, integrated with modals and popovers.
46
+
47
+ ```ruby
48
+ def create
49
+ @post = current_user.posts.create! post_params
50
+ ui.navigate_to @post
51
+ end
52
+ ```
53
+
54
+ This will automatically close the current modal or popover and navigate via Turbo Drive to the post's url - no redirects necessary.
55
+
56
+ The `ui` helper comes with several built-in functions:
57
+
58
+ ```ruby
59
+ # run any kind of javascript upon request completion
60
+ ui.run_javascript("alert('hello world')")
61
+
62
+ # open or replace a modal's content (dependent on its ID)
63
+ ui.open_modal(new_post_path, size: :small)
64
+
65
+ # close an open modal
66
+ ui.close_modal
67
+
68
+ # same methods also available for popovers
69
+ ui.open_popover(new_post_path, selector: "#user-profile", placement: :left)
70
+ ui.close_popover
71
+
72
+ # navigate via Turbo Drive
73
+ ui.navigate_to(@post)
74
+
75
+ # manipulate the page's content
76
+ ui.append(@post, with: "comments/comment", comment: @comment)
77
+ ui.prepend("#user-profile", with: "users/extra")
78
+ ui.replace(@post)
79
+ ui.remove(@post)
80
+ ```
81
+
82
+ ### Sitemaps
83
+
84
+ Want to implement sitemaps, but the ephemeral filesystem of Heroku hates you? Here's a simple way to upload sitemaps:
85
+
86
+ - install the sitemap gem and configure the `sitemap.rb` as usual
87
+ - use the shimmer adapter to automatically upload your sitemap to your configured ActiveStorage adapter
88
+ - use the shimmer controller to display the sitemap in your app
89
+ - (optional) tell sidekiq scheduler to regularly update your sitemap
90
+
91
+ ```ruby
92
+ # sitemap.rb
93
+ SitemapGenerator::Sitemap.adapter = Shimmer::SitemapAdapter.new
94
+
95
+ # routes.rb
96
+ get "sitemaps/*path", to: "shimmer/sitemaps#show"
97
+
98
+ # sidekiq.yml
99
+ :schedule:
100
+ sitemap:
101
+ cron: '0 0 12 * * * Europe/Berlin' # every day at 16:00, Berlin time
102
+ class: Shimmer::SitemapJob
103
+ ```
104
+
105
+ ### Cloudflare Support
106
+
107
+ As you might have noticed, Cloudflare SSL will cause some issues with your Rails app if you're not using SSL strict mode (https://github.com/rails/rails/issues/22965). If you can't switch to strict mode, go for the standard flexible mode instead and add this middleware to your stack:
108
+
109
+ ```ruby
110
+ # application.rb
111
+ config.middleware.use Shimmer::CloudflareProxy
112
+ ```
113
+
114
+ ### Heroku Database Helpers
115
+
116
+ Can't reproduce an issue with your local test data and just want the production or staging data on your development machine? Here you go:
117
+
118
+ ```bash
119
+ rails db:pull
120
+ ```
121
+
122
+ This will drop your local database and pull in the database of your connected Heroku app (make sure you executed `heroku git:remote -a your_app` before to have the git remote). But what about assets you might ask? Easy - assets are pulled from S3 as well via the AWS CLI automatically (make sure your environment variables in Heroku are correctly named as `AWS_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) and the database is updated to use your local filesystem instead.
123
+
124
+ If you don't want the asset support, you can also only pull the database or only the assets:
125
+
126
+ ```bash
127
+ rails db:pull_data
128
+ rails db:pull_assets
129
+ ```
130
+
131
+ ### Localizable Routes with Browser Locale Support
132
+
133
+ To localize a page via urls, this will help you tremendously.
134
+
135
+ ```ruby
136
+ # routes.rb
137
+ Rails.application.routes.draw do
138
+ scope "/(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
139
+ get "login", to: "sessions#new"
140
+ end
141
+ end
142
+ ```
143
+
144
+ From now on you can prefix your routes with a locale like `/de/login` or `/en/login` and `I18n.locale` will automatically be set. If there is no locale in the url (it's optional), this will automatically use the browser's locale.
145
+
146
+ You want to redirect from unlocalized paths? Add a before action to your controller:
147
+
148
+ ```ruby
149
+ before_action :check_locale
150
+ ```
151
+
152
+ Trying to figure out which key a certain translation on the page has? Append `?debug` to the url and `I18n.debug?` will be set - which leads to keys being printed on the page.
6
153
 
7
154
  ## Installation
8
155
 
9
156
  Add this line to your application's Gemfile:
10
157
 
11
158
  ```ruby
12
- gem 'shimmer'
159
+ gem "shimmer"
13
160
  ```
14
161
 
15
162
  And then execute:
16
163
 
17
- $ bundle install
164
+ ```bash
165
+ $ bundle install
166
+ ```
18
167
 
19
- Or install it yourself as:
168
+ Add some configuration to your project:
20
169
 
21
- $ gem install shimmer
170
+ ```ruby
171
+ # routes.rb
22
172
 
23
- ## Usage
173
+ resources :files, only: :show, controller: "shimmer/files"
24
174
 
25
- TODO: Write usage instructions here
175
+ # application_controller.rb
176
+ class ApplicationController < ActionController::Base
177
+ include Shimmer::Localizable
178
+ include Shimmer::RemoteNavigation
179
+ end
180
+ ```
26
181
 
27
- ## Development
182
+ ```ts
183
+ // application.ts
28
184
 
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
185
+ import { start } from "@nerdgeschoss/shimmer";
186
+ import { application } from "controllers/application";
30
187
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
188
+ start({ application });
189
+ ```
32
190
 
33
191
  ## Contributing
34
192
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/shimmer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/shimmer/blob/master/CODE_OF_CONDUCT.md).
193
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nerdgeschoss/shimmer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nerdgeschoss/shimmer/blob/master/CODE_OF_CONDUCT.md).
36
194
 
37
195
  ## License
38
196
 
@@ -40,4 +198,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
40
198
 
41
199
  ## Code of Conduct
42
200
 
43
- Everyone interacting in the Shimmer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/shimmer/blob/master/CODE_OF_CONDUCT.md).
201
+ Everyone interacting in the Shimmer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nerdgeschoss/shimmer/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class FilesController < ActionController::Base
5
+ def show
6
+ expires_in 1.year, public: true
7
+ request.session_options[:skip] = true # prevents a session cookie from being set (would prevent caching on CDNs)
8
+ proxy = FileProxy.restore(params.require(:id))
9
+ send_data proxy.file,
10
+ filename: proxy.filename.to_s,
11
+ type: proxy.content_type,
12
+ disposition: "inline"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class SitemapsController < ActionController::Base
5
+ def show
6
+ path = "sitemaps/#{params.require(:path)}.gz"
7
+ send_data ActiveStorage::Blob.service.download(path)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class SitemapJob < ActiveJob::Base
5
+ def perform
6
+ SitemapGenerator::Interpreter.run
7
+ SitemapGenerator::Sitemap.ping_search_engines
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # https://github.com/rails/rails/issues/22965
4
+ module Shimmer
5
+ class CloudflareProxy
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ return @app.call(env) unless env["HTTP_CF_VISITOR"]
12
+
13
+ env["HTTP_X_FORWARDED_PROTO"] = JSON.parse(env["HTTP_CF_VISITOR"])["scheme"]
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module Shimmer
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ path = File.expand_path(__dir__)
5
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :db do
4
+ desc "Downloads the app database from heroku to local db"
5
+ task pull_data: :environment do
6
+ config = ActiveRecord::Base.connection_db_config.config
7
+ # binding.pry
8
+ ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"] = "1"
9
+ Rake::Task["db:drop"].invoke
10
+ ENV["PGUSER"] = config["username"]
11
+ ENV["PGHOST"] = config["host"]
12
+ ENV["PGPORT"] = config["port"].to_s
13
+ sh "heroku pg:pull DATABASE_URL #{config["database"]} --app thefetishtraveller"
14
+ sh "rails db:environment:set"
15
+ sh "RAILS_ENV=test rails db:create"
16
+ end
17
+
18
+ task pull_assets: :environment do
19
+ config = JSON.parse(`heroku config --json`)
20
+ ENV["AWS_DEFAULT_REGION"] = config.fetch("AWS_REGION")
21
+ bucket = config.fetch("AWS_BUCKET")
22
+ ENV["AWS_ACCESS_KEY_ID"] = config.fetch("AWS_ACCESS_KEY_ID")
23
+ ENV["AWS_SECRET_ACCESS_KEY"] = config.fetch("AWS_SECRET_ACCESS_KEY")
24
+ storage_folder = Rails.root.join("storage")
25
+ download_folder = storage_folder.join("downloads")
26
+ FileUtils.mkdir_p download_folder
27
+ sh "aws s3 sync s3://#{bucket} #{storage_folder}/downloads"
28
+ download_folder.each_child do |file|
29
+ next if file.directory?
30
+
31
+ new_path = storage_folder.join file.basename.to_s.then { |e| [e[0..1], e[2..3], e] }.join("/")
32
+ FileUtils.mkdir_p(new_path.dirname)
33
+ FileUtils.cp(file, new_path)
34
+ end
35
+ # purge variants
36
+ ActiveStorage::VariantRecord.delete_all
37
+ ActiveStorage::Blob.update_all(service_name: :local)
38
+ end
39
+
40
+ desc "Download all app data, including assets"
41
+ task pull: [:pull_data, :pull_assets]
42
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc "Executes all linters and tests"
4
+ task :lint do
5
+ sh "bundle exec standardrb --fix"
6
+ sh "yarn lint"
7
+ sh "i18n-tasks health"
8
+ sh "bin/rspec"
9
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module FileHelper
5
+ def image_tag(source, **options)
6
+ return nil if source.blank?
7
+
8
+ if source.is_a?(ActiveStorage::Variant) || source.is_a?(ActiveStorage::Attached) || source.is_a?(ActiveStorage::Attachment) || source.is_a?(ActionText::Attachment)
9
+ attachment = source
10
+ width = options[:width]
11
+ height = options[:height]
12
+ source = image_file_url(source, width: width, height: height)
13
+ options[:loading] = :lazy
14
+ options[:srcset] = "#{source} 1x, #{image_file_url(attachment, width: width.to_i * 2, height: height ? height.to_i * 2 : nil)} 2x" if options[:width].present?
15
+ end
16
+ super source, options
17
+ end
18
+
19
+ def image_file_url(source, width: nil, height: nil)
20
+ return if source.blank?
21
+ return source if source.is_a?(String)
22
+
23
+ blob = source.try(:blob) || source
24
+ Shimmer::FileProxy.new(blob_id: blob.id, width: width, height: height).url
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class FileProxy
5
+ attr_reader :blob_id, :resize
6
+
7
+ delegate :message_verifier, to: :class
8
+ delegate :content_type, :filename, to: :blob
9
+
10
+ class << self
11
+ def restore(id)
12
+ blob_id, resize = message_verifier.verified(id)
13
+ new blob_id: blob_id, resize: resize
14
+ end
15
+ end
16
+
17
+ def initialize(blob_id:, resize: nil, width: nil, height: nil)
18
+ @blob_id = blob_id
19
+ if !resize && width
20
+ resize = if height
21
+ "#{width}x#{height}>"
22
+ else
23
+ "#{width}x"
24
+ end
25
+ end
26
+ @resize = resize
27
+ end
28
+
29
+ class << self
30
+ def message_verifier
31
+ @message_verifier ||= ApplicationRecord.signed_id_verifier
32
+ end
33
+ end
34
+
35
+ def path
36
+ Rails.application.routes.url_helpers.file_path(id, locale: nil)
37
+ end
38
+
39
+ def url(protocol: Rails.env.production? ? :https : :http)
40
+ Rails.application.routes.url_helpers.file_url(id, locale: nil, protocol: protocol)
41
+ end
42
+
43
+ def blob
44
+ @blob ||= ActiveStorage::Blob.find(blob_id)
45
+ end
46
+
47
+ def resizeable
48
+ resize.present? && !blob.content_type.include?("svg")
49
+ end
50
+
51
+ def variant
52
+ @variantvariant ||= resizeable ? blob.representation(resize: resize).processed : blob
53
+ end
54
+
55
+ def file
56
+ @file ||= blob.service.download(variant.key)
57
+ end
58
+
59
+ private
60
+
61
+ def id
62
+ @id ||= message_verifier.generate([blob_id, resize])
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Localizable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_action :set_locale
9
+
10
+ def default_url_options(options = {})
11
+ options = {locale: I18n.locale}.merge options
12
+ options[:debug] = true if I18n.debug?
13
+ options
14
+ end
15
+
16
+ def request_locale
17
+ request.env["HTTP_ACCEPT_LANGUAGE"].to_s[0..1].downcase.presence&.then { |e| e if I18n.available_locales.include?(e.to_sym) }
18
+ end
19
+
20
+ def set_locale
21
+ I18n.locale = url_locale || request_locale || I18n.default_locale
22
+ I18n.debug = params.key?(:debug)
23
+ end
24
+
25
+ def check_locale
26
+ redirect_to url_for(locale: I18n.locale) if params[:locale].blank? && request.get? && request.format.html?
27
+ end
28
+
29
+ def url_locale
30
+ params[:locale]
31
+ end
32
+
33
+ I18n.class_eval do
34
+ thread_mattr_accessor :debug
35
+
36
+ class << self
37
+ alias_method :old_translate, :translate
38
+ def translate(key, options = {})
39
+ untranslated_scopes = ["number", "transliterate", "date", "datetime", "errors", "helpers", "support", "time", "faker"].map { |k| "#{k}." }
40
+ key = key.to_s.downcase
41
+ untranslated = untranslated_scopes.any? { |e| key.include? e }
42
+ key_name = [options[:scope], key].flatten.compact.join(".")
43
+ option_names = options.except(:count, :default, :raise, :scope).map { |k, v| "#{k}=#{v}" }.join(", ")
44
+ return "#{key_name} #{option_names}" if I18n.debug && !untranslated
45
+
46
+ options.reverse_merge!(default: old_translate(key, **options.merge(locale: :de))) if untranslated
47
+ old_translate(key, **options)
48
+ end
49
+ alias_method :t, :translate
50
+
51
+ def debug?
52
+ debug
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end