moirai 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/test.yml +56 -0
- data/.gitignore +18 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +17 -0
- data/README.md +180 -0
- data/Rakefile +17 -0
- data/app/assets/config/moirai_manifest.js +2 -0
- data/app/assets/images/moirai/.keep +0 -0
- data/app/assets/javascripts/moirai/application.js +2 -0
- data/app/assets/javascripts/moirai/controllers/moirai_translation_controller.js +27 -0
- data/app/assets/javascripts/moirai/stimulus/init.js +5 -0
- data/app/assets/stylesheets/moirai/application.css +15 -0
- data/app/controllers/concerns/.keep +0 -0
- data/app/controllers/moirai/application_controller.rb +21 -0
- data/app/controllers/moirai/translation_files_controller.rb +93 -0
- data/app/helpers/moirai/application_helper.rb +9 -0
- data/app/jobs/moirai/application_job.rb +6 -0
- data/app/models/concerns/.keep +0 -0
- data/app/models/moirai/application_record.rb +7 -0
- data/app/models/moirai/translation.rb +14 -0
- data/app/models/moirai/translation_dumper.rb +43 -0
- data/app/models/moirai/translation_file_handler.rb +56 -0
- data/app/views/layouts/moirai/application.html.erb +26 -0
- data/app/views/moirai/translation_files/_form.html.erb +9 -0
- data/app/views/moirai/translation_files/index.html.erb +19 -0
- data/app/views/moirai/translation_files/show.html.erb +38 -0
- data/bin/check +4 -0
- data/bin/console +15 -0
- data/bin/fastcheck +17 -0
- data/bin/rails +16 -0
- data/bin/setup +8 -0
- data/config/routes.rb +9 -0
- data/lib/generators/moirai/install_generator.rb +19 -0
- data/lib/generators/moirai/migration_generator.rb +24 -0
- data/lib/generators/moirai/templates/migration.rb.erb +11 -0
- data/lib/i18n/backend/moirai.rb +65 -0
- data/lib/moirai/engine.rb +49 -0
- data/lib/moirai/pull_request_creator.rb +83 -0
- data/lib/moirai/version.rb +5 -0
- data/lib/moirai.rb +10 -0
- data/moirai.gemspec +36 -0
- metadata +181 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Moirai</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "moirai/application", media: "all" %>
|
9
|
+
<%= javascript_include_tag "moirai/application", "data-turoblinks-track": "reload", type: "module" %>
|
10
|
+
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
11
|
+
<link rel="icon" href="data:image/png;base64,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">
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
|
15
|
+
<% if notice.present? %>
|
16
|
+
<p class="notice"><%= notice %></p>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if alert.present? %>
|
20
|
+
<p class="notice">⚠️ <%= alert %></p>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<%= yield %>
|
24
|
+
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<span
|
2
|
+
contenteditable
|
3
|
+
data-action="blur->moirai-translation#submit click->moirai-translation#click"
|
4
|
+
style="border: 1px dashed #1d9f74; min-width: 30px; display: inline-block;"
|
5
|
+
data-key="<%= key %>"
|
6
|
+
data-file-path="<%= file_path %>"
|
7
|
+
data-controller="moirai-translation">
|
8
|
+
<%= value %>
|
9
|
+
</span>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<main>
|
2
|
+
<h1>Translation files</h1>
|
3
|
+
|
4
|
+
<% if Moirai::PullRequestCreator.available? %>
|
5
|
+
<%= button_to "Create or update PR", moirai_open_pr_path %>
|
6
|
+
<% else %>
|
7
|
+
<p>PR creation is not available. Add the gem octokit to your gemfile to enable this feature</p>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% @files.each do |file| %>
|
11
|
+
<article>
|
12
|
+
<bold><%= link_to (file[:name]), moirai_translation_file_path(file[:id]) %></bold>
|
13
|
+
|
14
|
+
<hr>
|
15
|
+
|
16
|
+
<%= shorten_path file[:path] %>
|
17
|
+
</article>
|
18
|
+
<% end %>
|
19
|
+
</main>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<main>
|
2
|
+
<h1>Update translations</h1>
|
3
|
+
|
4
|
+
<code><%= shorten_path @file_path %></code><br>
|
5
|
+
<p><%= link_to 'Back', moirai_translation_files_path %></p>
|
6
|
+
|
7
|
+
<table>
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<th>Key</th>
|
11
|
+
<th>Value</th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody>
|
15
|
+
<% @translation_keys.each do |key, value| %>
|
16
|
+
<% translation = Moirai::Translation.find_by(key: key, file_path: @decoded_path) %>
|
17
|
+
|
18
|
+
<tr>
|
19
|
+
<td>
|
20
|
+
<% if translation.present? %>
|
21
|
+
<mark title="<%= value %>"><%= key %></mark>
|
22
|
+
<% else %>
|
23
|
+
<%= key %>
|
24
|
+
<% end %>
|
25
|
+
</td>
|
26
|
+
<td>
|
27
|
+
<%= form_for Moirai::Translation.new, url: moirai_create_or_update_translation_path do |f| %>
|
28
|
+
<%= f.hidden_field :key, value: key %>
|
29
|
+
<%= f.hidden_field :file_path, value: @decoded_path %>
|
30
|
+
<%= f.text_field :value, value: translation&.value || value %>
|
31
|
+
<%= f.submit 'Update', style: 'display: none;' %>
|
32
|
+
<% end %>
|
33
|
+
</td>
|
34
|
+
</tr>
|
35
|
+
<% end %>
|
36
|
+
</tbody>
|
37
|
+
</table>
|
38
|
+
</main>
|
data/bin/check
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'moirai'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/fastcheck
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
if ! bundle exec standardrb
|
6
|
+
then
|
7
|
+
echo '\n Standard detected issues! Attempting to fix...'
|
8
|
+
|
9
|
+
if ! bundle exec standardrb --fix
|
10
|
+
then
|
11
|
+
echo '\n Tried to auto correct the issues, but some must be reviewed manually. Commit aborted.'
|
12
|
+
exit 1
|
13
|
+
fi
|
14
|
+
|
15
|
+
echo '\n Auto-corrected issues, but you should review changes before committing.'
|
16
|
+
exit 1
|
17
|
+
fi
|
data/bin/rails
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
5
|
+
# installed from the root of your application.
|
6
|
+
|
7
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
8
|
+
ENGINE_PATH = File.expand_path('../lib/moirai/engine', __dir__)
|
9
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
10
|
+
|
11
|
+
# Set up gems listed in the Gemfile.
|
12
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
13
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
14
|
+
|
15
|
+
require 'rails/all'
|
16
|
+
require 'rails/engine/commands'
|
data/bin/setup
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Moirai::Engine.routes.draw do
|
4
|
+
root to: "translation_files#index"
|
5
|
+
|
6
|
+
resources :translation_files, only: %i[index show], as: "moirai_translation_files"
|
7
|
+
post "/translation_files/open_pr", to: "translation_files#open_pr", as: "moirai_open_pr"
|
8
|
+
post "/translation_files", to: "translation_files#create_or_update", as: "moirai_create_or_update_translation"
|
9
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/migration"
|
3
|
+
|
4
|
+
module Moirai
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
8
|
+
|
9
|
+
def copy_migration
|
10
|
+
invoke "moirai:migration"
|
11
|
+
end
|
12
|
+
|
13
|
+
def mount_engine
|
14
|
+
route 'mount Moirai::Engine => "/moirai", as: "moirai"
|
15
|
+
'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/active_record"
|
5
|
+
|
6
|
+
module Moirai
|
7
|
+
module Generators
|
8
|
+
class MigrationGenerator < Rails::Generators::Base
|
9
|
+
include ActiveRecord::Generators::Migration
|
10
|
+
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
12
|
+
|
13
|
+
def create_migration_file
|
14
|
+
migration_template "migration.rb.erb", "db/migrate/create_moirai_translations.rb", migration_version: migration_version
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def migration_version
|
20
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateMoiraiTranslations < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
create_table :moirai_translations do |t|
|
4
|
+
t.string :locale, null: false
|
5
|
+
t.string :key, null: false
|
6
|
+
t.string :file_path, null: false
|
7
|
+
t.text :value
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module I18n
|
2
|
+
module Backend
|
3
|
+
class Moirai < I18n::Backend::Simple # TODO: no need to extend the simple one. It does too much
|
4
|
+
# TODO: mega inefficient. we don't want to perform a SQL query for each key!
|
5
|
+
def translate(locale, key, options = EMPTY_HASH)
|
6
|
+
overridden_translation = ::Moirai::Translation.find_by(locale: locale, key: key)
|
7
|
+
if overridden_translation.present?
|
8
|
+
overridden_translation.value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def store_moirai_translations(filename, locale, data, options)
|
13
|
+
moirai_translations[locale] ||= Concurrent::Hash.new
|
14
|
+
flatten_data = flatten_hash(filename, data)
|
15
|
+
flatten_data = Utils.deep_symbolize_keys(flatten_data) unless options.fetch(:skip_symbolize_keys, false)
|
16
|
+
Utils.deep_merge!(moirai_translations[locale], flatten_data)
|
17
|
+
end
|
18
|
+
|
19
|
+
def moirai_translations(do_init: false)
|
20
|
+
@moirai_translations ||= Concurrent::Hash.new do |h, k|
|
21
|
+
MUTEX.synchronize do
|
22
|
+
h[k] = Concurrent::Hash.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def flatten_hash(filename, hash, parent_key = "", result = {})
|
28
|
+
hash.each do |key, value|
|
29
|
+
new_key = parent_key.empty? ? key.to_s : "#{parent_key}.#{key}"
|
30
|
+
case value
|
31
|
+
when Hash
|
32
|
+
flatten_hash(filename, value, new_key, result)
|
33
|
+
when Array
|
34
|
+
value.each_with_index do |item, index|
|
35
|
+
array_key = "#{new_key}.#{index}"
|
36
|
+
if item.is_a?(Hash)
|
37
|
+
flatten_hash(filename, item, array_key, result)
|
38
|
+
else
|
39
|
+
result[array_key] = filename
|
40
|
+
end
|
41
|
+
end
|
42
|
+
else
|
43
|
+
result[new_key] = filename
|
44
|
+
end
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_file(filename)
|
50
|
+
type = File.extname(filename).tr(".", "").downcase
|
51
|
+
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
|
52
|
+
data, keys_symbolized = send(:"load_#{type}", filename)
|
53
|
+
unless data.is_a?(Hash)
|
54
|
+
raise InvalidLocaleData.new(filename, "expects it to return a hash, but does not")
|
55
|
+
end
|
56
|
+
data.each do |locale, d|
|
57
|
+
store_translations(locale, d || {}, skip_symbolize_keys: keys_symbolized)
|
58
|
+
store_moirai_translations(filename, locale, d || {}, skip_symbolize_keys: keys_symbolized)
|
59
|
+
end
|
60
|
+
|
61
|
+
data
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moirai
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace Moirai
|
6
|
+
|
7
|
+
config.generators do |generator|
|
8
|
+
generator.orm :active_record
|
9
|
+
end
|
10
|
+
|
11
|
+
config.after_initialize do
|
12
|
+
moirai_backend = I18n::Backend::Moirai.new
|
13
|
+
moirai_backend.eager_load!
|
14
|
+
I18n.backend = I18n::Backend::Chain.new(moirai_backend, I18n.backend)
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: how to do this without rewriting the entire method?
|
18
|
+
# https://github.com/rails/rails/blob/main/actionview/lib/action_view/helpers/translation_helper.rb#L122
|
19
|
+
initializer "moirai.override_translation_helper" do
|
20
|
+
ActiveSupport.on_load(:action_view) do
|
21
|
+
module ActionView::Helpers::TranslationHelper # rubocop:disable Lint/ConstantDefinitionInBlock
|
22
|
+
alias_method :original_translate, :translate
|
23
|
+
|
24
|
+
def translate(key, **options)
|
25
|
+
value = original_translate(key, **options)
|
26
|
+
|
27
|
+
if moirai_edit_enabled?
|
28
|
+
moirai_translations = I18n.backend.backends.find { |b| b.respond_to?(:moirai_translations) }.moirai_translations
|
29
|
+
file_path = moirai_translations[I18n.locale][scope_key_by_partial(key)]
|
30
|
+
|
31
|
+
render(partial: "moirai/translation_files/form",
|
32
|
+
locals: {key: scope_key_by_partial(key),
|
33
|
+
file_path: file_path,
|
34
|
+
value: value})
|
35
|
+
else
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :t, :translate
|
41
|
+
|
42
|
+
def moirai_edit_enabled?
|
43
|
+
params[:moirai] == "true"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Moirai::PullRequestCreator
|
2
|
+
BRANCH_NAME = "moirai-translations"
|
3
|
+
|
4
|
+
def self.available?
|
5
|
+
defined?(Octokit)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@github_repo_name = ENV["MOIRAI_GITHUB_REPO_NAME"] || Rails.application.credentials.dig(:moirai, :github_repo_name)
|
10
|
+
@github_access_token = ENV["MOIRAI_GITHUB_ACCESS_TOKEN"] || Rails.application.credentials.dig(:moirai, :github_access_token)
|
11
|
+
@client = Octokit::Client.new(
|
12
|
+
access_token: @github_access_token
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_pull_request(translations_array)
|
17
|
+
repo = @client.repo(@github_repo_name)
|
18
|
+
default_branch = repo.default_branch
|
19
|
+
|
20
|
+
if branch_exists?
|
21
|
+
puts "Branch #{BRANCH_NAME} already exists - the branch will be updated with the new changes"
|
22
|
+
else
|
23
|
+
puts "Branch #{BRANCH_NAME} does not exist - creating branch"
|
24
|
+
default_branch_ref = @client.ref(@github_repo_name, "heads/#{default_branch}")
|
25
|
+
latest_commit_sha = default_branch_ref.object.sha
|
26
|
+
|
27
|
+
@client.create_ref(@github_repo_name, "heads/#{BRANCH_NAME}", latest_commit_sha)
|
28
|
+
end
|
29
|
+
|
30
|
+
translations_array.each do |translation_hash|
|
31
|
+
update_file(translation_hash[:file_path], translation_hash[:content])
|
32
|
+
end
|
33
|
+
|
34
|
+
unless pull_request_exists?
|
35
|
+
pull_request = @client.create_pull_request(
|
36
|
+
@github_repo_name,
|
37
|
+
default_branch,
|
38
|
+
BRANCH_NAME,
|
39
|
+
"Adding new content by Moirai",
|
40
|
+
"BODY - This is a pull request created by Moirai"
|
41
|
+
)
|
42
|
+
|
43
|
+
puts "Pull request created: #{pull_request.html_url}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def branch_exists?
|
48
|
+
@client.ref(@github_repo_name, "heads/#{BRANCH_NAME}")
|
49
|
+
true
|
50
|
+
rescue Octokit::NotFound
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def update_file(path, content)
|
55
|
+
# TODO: check what happens if branch exists
|
56
|
+
|
57
|
+
file = @client.contents(@github_repo_name, path: path, ref: BRANCH_NAME)
|
58
|
+
file_sha = file.sha
|
59
|
+
|
60
|
+
@client.update_contents(
|
61
|
+
@github_repo_name,
|
62
|
+
path,
|
63
|
+
"Updating translations for #{path} by Moirai",
|
64
|
+
file_sha,
|
65
|
+
content,
|
66
|
+
branch: BRANCH_NAME
|
67
|
+
)
|
68
|
+
rescue Octokit::NotFound
|
69
|
+
@client.create_contents(
|
70
|
+
@github_repo_name,
|
71
|
+
path,
|
72
|
+
"Creating translations for #{path} by Moirai",
|
73
|
+
content,
|
74
|
+
branch: BRANCH_NAME
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def pull_request_exists?
|
79
|
+
@client.pull_requests(@github_repo_name).any? do |pull_request|
|
80
|
+
pull_request.head.ref == BRANCH_NAME
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/moirai.rb
ADDED
data/moirai.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "moirai"
|
5
|
+
spec.version = "0.1.0"
|
6
|
+
spec.authors = ["Alessandro Rodi", "Oliver Anthony", "Daniel Bengl"]
|
7
|
+
spec.email = %w[alessandro.rodi@renuo.ch oliver.anthony@renuo.ch daniel.bengl@renuo.ch]
|
8
|
+
|
9
|
+
spec.summary = "Manage translation strings in real time"
|
10
|
+
spec.description = 'This gem allows you to manage translation strings in real time,
|
11
|
+
viewing the live changes in the browser, with the changes then converted to a PR opened on the repository.'
|
12
|
+
spec.homepage = "https://github.com/renuo/moirai"
|
13
|
+
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
+
spec.metadata["source_code_uri"] = "https://github.com/renuo/moirai"
|
16
|
+
spec.metadata["changelog_uri"] = "https://github.com/renuo/moirai/CHANGELOG.md"
|
17
|
+
spec.metadata["steep_types"] = "sig"
|
18
|
+
|
19
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|docs)/}) || f.end_with?(".gem") }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.required_ruby_version = ">= 2.7.0"
|
27
|
+
spec.add_dependency "rails", ">= 6.1"
|
28
|
+
spec.add_development_dependency "octokit", ">= 4.0"
|
29
|
+
spec.add_development_dependency "capybara"
|
30
|
+
spec.add_development_dependency "selenium-webdriver"
|
31
|
+
spec.add_development_dependency "rails", "~> 7.2.0"
|
32
|
+
spec.add_development_dependency "dotenv"
|
33
|
+
|
34
|
+
spec.license = "MIT"
|
35
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moirai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alessandro Rodi
|
8
|
+
- Oliver Anthony
|
9
|
+
- Daniel Bengl
|
10
|
+
autorequire:
|
11
|
+
bindir: exe
|
12
|
+
cert_chain: []
|
13
|
+
date: 2024-10-23 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '6.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '6.1'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: octokit
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '4.0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '4.0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: capybara
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: selenium-webdriver
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rails
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 7.2.0
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 7.2.0
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: dotenv
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
description: |-
|
100
|
+
This gem allows you to manage translation strings in real time,
|
101
|
+
viewing the live changes in the browser, with the changes then converted to a PR opened on the repository.
|
102
|
+
email:
|
103
|
+
- alessandro.rodi@renuo.ch
|
104
|
+
- oliver.anthony@renuo.ch
|
105
|
+
- daniel.bengl@renuo.ch
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- ".github/CODEOWNERS"
|
111
|
+
- ".github/workflows/test.yml"
|
112
|
+
- ".gitignore"
|
113
|
+
- Appraisals
|
114
|
+
- CHANGELOG.md
|
115
|
+
- Gemfile
|
116
|
+
- README.md
|
117
|
+
- Rakefile
|
118
|
+
- app/assets/config/moirai_manifest.js
|
119
|
+
- app/assets/images/moirai/.keep
|
120
|
+
- app/assets/javascripts/moirai/application.js
|
121
|
+
- app/assets/javascripts/moirai/controllers/moirai_translation_controller.js
|
122
|
+
- app/assets/javascripts/moirai/stimulus/init.js
|
123
|
+
- app/assets/stylesheets/moirai/application.css
|
124
|
+
- app/controllers/concerns/.keep
|
125
|
+
- app/controllers/moirai/application_controller.rb
|
126
|
+
- app/controllers/moirai/translation_files_controller.rb
|
127
|
+
- app/helpers/moirai/application_helper.rb
|
128
|
+
- app/jobs/moirai/application_job.rb
|
129
|
+
- app/models/concerns/.keep
|
130
|
+
- app/models/moirai/application_record.rb
|
131
|
+
- app/models/moirai/translation.rb
|
132
|
+
- app/models/moirai/translation_dumper.rb
|
133
|
+
- app/models/moirai/translation_file_handler.rb
|
134
|
+
- app/views/layouts/moirai/application.html.erb
|
135
|
+
- app/views/moirai/translation_files/_form.html.erb
|
136
|
+
- app/views/moirai/translation_files/index.html.erb
|
137
|
+
- app/views/moirai/translation_files/show.html.erb
|
138
|
+
- bin/check
|
139
|
+
- bin/console
|
140
|
+
- bin/fastcheck
|
141
|
+
- bin/rails
|
142
|
+
- bin/setup
|
143
|
+
- config/routes.rb
|
144
|
+
- lib/generators/moirai/install_generator.rb
|
145
|
+
- lib/generators/moirai/migration_generator.rb
|
146
|
+
- lib/generators/moirai/templates/migration.rb.erb
|
147
|
+
- lib/i18n/backend/moirai.rb
|
148
|
+
- lib/moirai.rb
|
149
|
+
- lib/moirai/engine.rb
|
150
|
+
- lib/moirai/pull_request_creator.rb
|
151
|
+
- lib/moirai/version.rb
|
152
|
+
- moirai.gemspec
|
153
|
+
homepage: https://github.com/renuo/moirai
|
154
|
+
licenses:
|
155
|
+
- MIT
|
156
|
+
metadata:
|
157
|
+
homepage_uri: https://github.com/renuo/moirai
|
158
|
+
source_code_uri: https://github.com/renuo/moirai
|
159
|
+
changelog_uri: https://github.com/renuo/moirai/CHANGELOG.md
|
160
|
+
steep_types: sig
|
161
|
+
rubygems_mfa_required: 'true'
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: 2.7.0
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubygems_version: 3.4.20
|
178
|
+
signing_key:
|
179
|
+
specification_version: 4
|
180
|
+
summary: Manage translation strings in real time
|
181
|
+
test_files: []
|