moirai 0.2.0 → 0.3.1
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/.ruby-version +1 -0
- data/.semaphore/main-deploy.yml +18 -0
- data/.semaphore/semaphore.yml +50 -0
- data/CHANGELOG.md +12 -1
- data/Gemfile +1 -0
- data/README.md +51 -33
- data/app/assets/javascripts/moirai_translation_controller.js +9 -0
- data/app/assets/stylesheets/moirai/application.css +2 -0
- data/app/assets/stylesheets/translation_files.css +22 -0
- data/app/controllers/moirai/translation_files_controller.rb +25 -2
- data/app/views/layouts/moirai/application.html.erb +28 -0
- data/app/views/moirai/translation_files/_form.html.erb +3 -3
- data/app/views/moirai/translation_files/show.html.erb +8 -3
- data/lib/generators/moirai/install_generator.rb +26 -0
- data/lib/i18n/extensions/i18n.rb +13 -0
- data/lib/moirai/engine.rb +27 -1
- data/lib/moirai/version.rb +1 -1
- data/lib/moirai.rb +2 -1
- data/moirai.gemspec +3 -1
- metadata +11 -7
- data/.github/workflows/test.yml +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecb41a395f21cd347b67e6e0233bd776a80493be4bf98d423077b7fd7cbe2005
|
4
|
+
data.tar.gz: 1335f5ca28ab8804459172894841f71cb8ac88740ce0b94d21096bc3a1713386
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 927bd40a31ce569fca7b6d6a68194106db7616bf3a733009dd84669a387450a4808bc650aeff8f7f1d6aefe630d859db0c0486972008189e183f957247fada80
|
7
|
+
data.tar.gz: c8a831cd233bf06ff6160acd096346afb3ab8b1209b3bda7a5a4c7ccbecffe263fadaffd4829508d5f13cb3f4201fdf3658ef46ddcb1ae14c16d46ca4c85c324
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.1
|
@@ -0,0 +1,18 @@
|
|
1
|
+
version: v1.0
|
2
|
+
name: main-deploy
|
3
|
+
agent:
|
4
|
+
machine:
|
5
|
+
type: e1-standard-2
|
6
|
+
os_image: ubuntu2004
|
7
|
+
|
8
|
+
blocks:
|
9
|
+
- name: main-deploy
|
10
|
+
task:
|
11
|
+
secrets:
|
12
|
+
- name: rubygems-deploy
|
13
|
+
jobs:
|
14
|
+
- name: main-deploy
|
15
|
+
commands:
|
16
|
+
- checkout --use-cache
|
17
|
+
- gem build moirai
|
18
|
+
- gem push moirai-*.gem
|
@@ -0,0 +1,50 @@
|
|
1
|
+
version: "v1.0"
|
2
|
+
name: moirai
|
3
|
+
agent:
|
4
|
+
machine:
|
5
|
+
type: e1-standard-2
|
6
|
+
os_image: ubuntu2004
|
7
|
+
auto_cancel:
|
8
|
+
running:
|
9
|
+
when: "true"
|
10
|
+
|
11
|
+
blocks:
|
12
|
+
- name: tests
|
13
|
+
execution_time_limit:
|
14
|
+
minutes: 10
|
15
|
+
task:
|
16
|
+
secrets:
|
17
|
+
- name: moirai
|
18
|
+
prologue:
|
19
|
+
commands:
|
20
|
+
- checkout --use-cache
|
21
|
+
- cache restore
|
22
|
+
- bundle config set path 'vendor/bundle'
|
23
|
+
- bundle install -j 4
|
24
|
+
- cache store
|
25
|
+
jobs:
|
26
|
+
- name: linter
|
27
|
+
commands:
|
28
|
+
- bundle exec standardrb
|
29
|
+
- name: tests sqlite
|
30
|
+
env_vars:
|
31
|
+
- name: TARGET_DB
|
32
|
+
value: sqlite
|
33
|
+
- name: RAILS_ENV
|
34
|
+
value: test
|
35
|
+
commands:
|
36
|
+
- bin/rails db:create db:schema:load
|
37
|
+
- bin/check
|
38
|
+
- name: tests postgres
|
39
|
+
env_vars:
|
40
|
+
- name: TARGET_DB
|
41
|
+
value: postgres
|
42
|
+
- name: RAILS_ENV
|
43
|
+
value: test
|
44
|
+
commands:
|
45
|
+
- sem-service start postgres 14 --username=semaphore
|
46
|
+
- bin/rails db:create db:schema:load
|
47
|
+
- bin/check
|
48
|
+
promotions:
|
49
|
+
- name: main
|
50
|
+
pipeline_file: main-deploy.yml
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
-
##
|
1
|
+
## 0.3.1
|
2
2
|
|
3
|
+
* Fixes a problem when running a rake command and no database exists yet using postgres. ([@coorasse][])
|
4
|
+
|
5
|
+
## 0.3.0
|
6
|
+
|
7
|
+
* Added a method `I18n.translate_without_moirai` ([@oliveranthony17][])
|
8
|
+
* Simplified stimulus setup ([@coorasse][])
|
9
|
+
* Fixed some setup issues in test environments ([@oliveranthony17][])
|
10
|
+
* Show original translation when deleting the whole inline editing content. ([@CuddlyBunion341][])
|
11
|
+
|
12
|
+
* Support for html translations ([@CuddlyBunion341][])
|
13
|
+
=======
|
3
14
|
## 0.2.0
|
4
15
|
|
5
16
|
* Support for strings coming from gems ([@coorasse][])
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,12 +5,13 @@
|
|
5
5
|
### Manage translation strings in real time
|
6
6
|
|
7
7
|
- Let your non-developer team members finally manage translations (yes, even Karen from marketing...).
|
8
|
-
- See those translations live in your app, so you can make sure “Submit” isn’t overlapping the button where “**Do not
|
9
|
-
|
8
|
+
- See those translations live in your app, so you can make sure “Submit” isn’t overlapping the button where “**Do not
|
9
|
+
press this button EVER” should be.
|
10
|
+
- Automatically create Pull Requests based on these changes, saving your developers from yet another “small tweak” email
|
11
|
+
request.
|
10
12
|
|
11
13
|
> Let the world be translated, one typo at a time.
|
12
14
|
|
13
|
-
|
14
15
|
## Installation
|
15
16
|
|
16
17
|
Add this line to your application's Gemfile:
|
@@ -20,11 +21,13 @@ gem "moirai"
|
|
20
21
|
```
|
21
22
|
|
22
23
|
And then execute:
|
24
|
+
|
23
25
|
```bash
|
24
26
|
bundle
|
25
27
|
```
|
26
28
|
|
27
|
-
Next, you need to run the generator which will create the necessary files including the database migration,
|
29
|
+
Next, you need to run the generator which will create the necessary files including the database migration,
|
30
|
+
as well as inserting the engine in the `routes.rb` file, and importing the necessary javascript files:
|
28
31
|
|
29
32
|
```bash
|
30
33
|
bin/rails g moirai:install
|
@@ -40,40 +43,51 @@ bin/rails db:migrate
|
|
40
43
|
|
41
44
|
### How to change translations
|
42
45
|
|
43
|
-
If you mounted Moirai under "/moirai", head there and you will find a list of all the files containing texts that can be
|
44
|
-
|
46
|
+
If you mounted Moirai under "/moirai", head there and you will find a list of all the files containing texts that can be
|
47
|
+
translated.
|
48
|
+
Open a file, change the value of translations, and press ENTER to update the translation and see it immediately changed
|
49
|
+
on the application.
|
45
50
|
|
46
51
|
### Inline editing
|
47
52
|
|
48
53
|
By default, inline editing is disabled. To enable it, set the `moirai=true` query parameter in the URL.
|
49
54
|
|
50
|
-
If you want to only allow specific users to perform inline editing, you can override the `moirai_edit_enabled?` method
|
55
|
+
If you want to only allow specific users to perform inline editing, you can override the `moirai_edit_enabled?` method
|
56
|
+
in your application helper.
|
51
57
|
|
52
58
|
```ruby
|
59
|
+
|
53
60
|
module ApplicationHelper
|
54
61
|
def moirai_edit_enabled?
|
55
|
-
params[:moirai] == "true"
|
62
|
+
params[:moirai] == "true" && current_user&.admin?
|
56
63
|
end
|
57
64
|
end
|
58
65
|
```
|
59
66
|
|
60
67
|
You also need to have the moirai_translations_controller.js Stimulus Controller initialized.
|
61
68
|
|
62
|
-
|
69
|
+
#### Importmap
|
63
70
|
|
64
|
-
|
71
|
+
The command `bin/rails g moirai:install` should have already pinned the necessary controller for you in importmap.rb, so
|
72
|
+
no further steps are needed.
|
65
73
|
|
66
|
-
|
67
|
-
pin "controllers/moirai_translation_controller", to: "moirai_translation_controller.js"
|
68
|
-
```
|
74
|
+
#### jsbulding
|
69
75
|
|
70
|
-
|
76
|
+
The command `bin/rails g moirai:install` should have already copied the necessary controller for you in
|
77
|
+
`app/javascripts/controllers`, so no further steps are needed.
|
78
|
+
|
79
|
+
#### More?
|
80
|
+
|
81
|
+
If you’re unsure about all the possible configuration options, you can simply copy and paste the stimulus controller
|
82
|
+
into your app as a fallback.
|
71
83
|
|
72
84
|
### Automatic PR creation with Octokit (**optional**)
|
73
85
|
|
74
|
-
If you would like Moirai to automatically create a pull request on GitHub to keep translations synchronized with the
|
86
|
+
If you would like Moirai to automatically create a pull request on GitHub to keep translations synchronized with the
|
87
|
+
codebase,
|
75
88
|
you need to set up [Octokit](https://github.com/octokit/octokit.rb).
|
76
|
-
You will also need to create a **Personal Access Token** on GitHub, and configure the access in the appropriate
|
89
|
+
You will also need to create a **Personal Access Token** on GitHub, and configure the access in the appropriate *
|
90
|
+
*environment variables** (this is explained below).
|
77
91
|
|
78
92
|
#### 1. Add Octokit to Your Gemfile
|
79
93
|
|
@@ -87,22 +101,23 @@ Then run `bundle install`.
|
|
87
101
|
|
88
102
|
#### 2. Create a Personal Access Token (PAT) on GitHub
|
89
103
|
|
90
|
-
You will need a Personal Access Token (PAT) with the `Content - Write` permission to allow Octokit to create branches
|
104
|
+
You will need a Personal Access Token (PAT) with the `Content - Write` permission to allow Octokit to create branches
|
105
|
+
and pull requests.
|
91
106
|
|
92
|
-
-
|
93
|
-
-
|
94
|
-
-
|
95
|
-
-
|
96
|
-
|
97
|
-
|
98
|
-
-
|
107
|
+
- Go to GitHub Token Settings.
|
108
|
+
- Click Generate New Token.
|
109
|
+
- Give your token a name (e.g., “Moirai”).
|
110
|
+
- Under Scopes, select:
|
111
|
+
- repo (for full control of private repositories, including writing content).
|
112
|
+
- content (for read/write access to code, commit statuses, and pull requests).
|
113
|
+
- Generate the token and copy it immediately as it will be shown only once.
|
99
114
|
|
100
115
|
#### 3. Set Up Environment Variables
|
101
116
|
|
102
117
|
You need to configure the following environment variables in your application:
|
103
118
|
|
104
|
-
-
|
105
|
-
-
|
119
|
+
- `MOIRAI_GITHUB_REPO_NAME`: The name of the repository where the pull request will be created.
|
120
|
+
- `MOIRAI_GITHUB_ACCESS_TOKEN`: The Personal Access Token (PAT) you created earlier.
|
106
121
|
|
107
122
|
For example, in your `.env` file:
|
108
123
|
|
@@ -111,7 +126,8 @@ MOIRAI_GITHUB_REPO_NAME=your-organization/your-repo
|
|
111
126
|
MOIRAI_GITHUB_ACCESS_TOKEN=your-generated-token
|
112
127
|
```
|
113
128
|
|
114
|
-
We also support Rails credentials. The environment variables need to be stored in a slightly different way to adhere to
|
129
|
+
We also support Rails credentials. The environment variables need to be stored in a slightly different way to adhere to
|
130
|
+
convention. For example:
|
115
131
|
|
116
132
|
```env
|
117
133
|
moirai:
|
@@ -121,13 +137,14 @@ moirai:
|
|
121
137
|
|
122
138
|
#### 4. Triggering the pull request creation
|
123
139
|
|
124
|
-
Moirai will now be able to use this Personal Access Token to create a pull request on GitHub when a translation is
|
140
|
+
Moirai will now be able to use this Personal Access Token to create a pull request on GitHub when a translation is
|
141
|
+
updated.
|
125
142
|
|
126
143
|
To trigger this, you can press the `Create or update PR` button once you have made your changes.
|
127
144
|
|
128
145
|
### Authentication
|
129
146
|
|
130
|
-
Moirai allows you to use basic HTTP authentication to protect the engine.
|
147
|
+
Moirai allows you to use basic HTTP authentication to protect the engine.
|
131
148
|
To enable this, you need to set the following environment variables:
|
132
149
|
|
133
150
|
```env
|
@@ -137,11 +154,11 @@ MOIRAI_BASICAUTH_PASSWORD=moirai
|
|
137
154
|
|
138
155
|
> ⚠️ Remember to protect Moirai. You don't want to give everyone the possibility to change strings in the application.
|
139
156
|
|
140
|
-
If you have authenticated users, you can leverage the Rails Routes protection mechanism to protect the engine.
|
157
|
+
If you have authenticated users, you can leverage the Rails Routes protection mechanism to protect the engine.
|
141
158
|
See the following example:
|
142
159
|
|
143
160
|
```ruby
|
144
|
-
authenticated :user, lambda {|u| u.role == "admin"} do
|
161
|
+
authenticated :user, lambda { |u| u.role == "admin" } do
|
145
162
|
mount Moirai::Engine => '/moirai', as: 'moirai'
|
146
163
|
end
|
147
164
|
```
|
@@ -166,12 +183,12 @@ end
|
|
166
183
|
|
167
184
|
4. Set your environment variables using the newly created `.env` file.
|
168
185
|
|
169
|
-
You will need a repository to test against and a token. Generate a new Fine-GRained Personal access token and give the
|
186
|
+
You will need a repository to test against and a token. Generate a new Fine-GRained Personal access token and give the
|
187
|
+
necessary permissions to your repository.
|
170
188
|
See the image below as an example:
|
171
189
|
|
172
190
|

|
173
191
|
|
174
|
-
|
175
192
|
5. Run the tests:
|
176
193
|
```bash
|
177
194
|
bin/check
|
@@ -191,6 +208,7 @@ See the image below as an example:
|
|
191
208
|
* Support for fallbacks: it should detect when a fallback string is in use and prevent attempts to override its value.
|
192
209
|
|
193
210
|
## License
|
211
|
+
|
194
212
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
195
213
|
|
196
214
|
## Copyright
|
@@ -27,6 +27,15 @@ export default class MoiraiTranslationController extends Controller {
|
|
27
27
|
value: event.target.innerText
|
28
28
|
}
|
29
29
|
})
|
30
|
+
})
|
31
|
+
.then(response => response.json())
|
32
|
+
.then(data => {
|
33
|
+
if (data?.fallback_translation) {
|
34
|
+
event.target.innerText = data.fallback_translation
|
35
|
+
}
|
36
|
+
})
|
37
|
+
.catch(error => {
|
38
|
+
console.error('Error:', error);
|
30
39
|
});
|
31
40
|
}
|
32
41
|
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
td {
|
2
|
+
height: 100px;
|
3
|
+
width: 200px;
|
4
|
+
vertical-align: top;
|
5
|
+
}
|
6
|
+
|
7
|
+
form {
|
8
|
+
height: 100%;
|
9
|
+
display: flex;
|
10
|
+
align-items: stretch;
|
11
|
+
}
|
12
|
+
|
13
|
+
textarea.translation-textarea {
|
14
|
+
width: 100%;
|
15
|
+
height: auto;
|
16
|
+
resize: vertical;
|
17
|
+
min-height: 3em;
|
18
|
+
overflow: hidden;
|
19
|
+
margin-bottom: 0;
|
20
|
+
}
|
21
|
+
|
22
|
+
/*# TODO: this isn't coming through */
|
@@ -39,8 +39,17 @@ module Moirai
|
|
39
39
|
def handle_update(translation)
|
40
40
|
if translation_params[:value].blank? || translation_same_as_current?
|
41
41
|
translation.destroy
|
42
|
-
|
43
|
-
|
42
|
+
respond_to do |format|
|
43
|
+
format.json do
|
44
|
+
render json: {
|
45
|
+
fallback_translation: get_fallback_translation
|
46
|
+
}
|
47
|
+
end
|
48
|
+
format.html do
|
49
|
+
flash.notice = "Translation #{translation.key} was successfully deleted."
|
50
|
+
redirect_to_translation_file(translation.file_path)
|
51
|
+
end
|
52
|
+
end
|
44
53
|
return
|
45
54
|
end
|
46
55
|
|
@@ -60,6 +69,10 @@ module Moirai
|
|
60
69
|
return
|
61
70
|
end
|
62
71
|
|
72
|
+
if translation_params[:value].blank? && request.format.json?
|
73
|
+
return render json: {fallback_translation: get_fallback_translation}
|
74
|
+
end
|
75
|
+
|
63
76
|
translation = Translation.new(translation_params)
|
64
77
|
|
65
78
|
if translation.save
|
@@ -74,6 +87,7 @@ module Moirai
|
|
74
87
|
def success_response(translation)
|
75
88
|
respond_to do |format|
|
76
89
|
format.json do
|
90
|
+
flash.discard
|
77
91
|
render json: {}
|
78
92
|
end
|
79
93
|
format.all do
|
@@ -114,5 +128,14 @@ module Moirai
|
|
114
128
|
|
115
129
|
translation_params[:value] == @file_handler.parse_file(file_paths.first)[translation_params[:key]]
|
116
130
|
end
|
131
|
+
|
132
|
+
def get_fallback_translation
|
133
|
+
file_paths = KeyFinder.new.file_paths_for(translation_params[:key], locale: translation_params[:locale])
|
134
|
+
|
135
|
+
return "" if file_paths.empty?
|
136
|
+
return "" unless file_paths.all? { |file_path| File.exist?(file_path) }
|
137
|
+
|
138
|
+
@file_handler.parse_file(file_paths.first)[translation_params[:key]]
|
139
|
+
end
|
117
140
|
end
|
118
141
|
end
|
@@ -8,6 +8,34 @@
|
|
8
8
|
<%= stylesheet_link_tag "moirai/application", media: "all" %>
|
9
9
|
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
10
10
|
<link rel="icon" href="data:image/png;base64,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">
|
11
|
+
|
12
|
+
<style>
|
13
|
+
td {
|
14
|
+
height: 100px; /* Ensure the cell has a consistent height */
|
15
|
+
width: 200px; /* Set a reasonable width */
|
16
|
+
vertical-align: top;
|
17
|
+
}
|
18
|
+
|
19
|
+
form {
|
20
|
+
height: 100%;
|
21
|
+
display: flex;
|
22
|
+
align-items: stretch;
|
23
|
+
}
|
24
|
+
|
25
|
+
textarea {
|
26
|
+
flex: 1; /* Allow the textarea to expand */
|
27
|
+
width: 100%; /* Ensure it fills the width */
|
28
|
+
resize: none; /* Disable manual resizing */
|
29
|
+
box-sizing: border-box; /* Account for padding and borders */
|
30
|
+
border: 1px solid #ccc; /* Standard input border style */
|
31
|
+
border-radius: 4px; /* Match the design */
|
32
|
+
padding: 5px; /* Inner padding for text */
|
33
|
+
font-family: inherit; /* Use consistent font */
|
34
|
+
font-size: inherit; /* Match the font size */
|
35
|
+
overflow: hidden; /* Hide overflow before it resizes */
|
36
|
+
}
|
37
|
+
</style>
|
38
|
+
|
11
39
|
</head>
|
12
40
|
<body>
|
13
41
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<span
|
2
|
-
contenteditable
|
2
|
+
contenteditable
|
3
3
|
data-action="blur->moirai-translation#submit click->moirai-translation#click"
|
4
|
-
style="
|
4
|
+
style="outline: 1px solid #1d9f74; min-width: 30px; display: inline-block; <%= 'color: red;' if is_missing_translation %>"
|
5
5
|
data-moirai-translation-key-value="<%= key %>"
|
6
6
|
data-moirai-translation-locale-value="<%= locale %>"
|
7
7
|
data-controller="moirai-translation">
|
8
|
-
<%= value %>
|
8
|
+
<%= value.to_s.gsub("<", "<").gsub(">", ">").html_safe %>
|
9
9
|
</span>
|
@@ -9,6 +9,7 @@
|
|
9
9
|
<tr>
|
10
10
|
<th>Key</th>
|
11
11
|
<th>Value</th>
|
12
|
+
<th>Original Translation</th>
|
12
13
|
</tr>
|
13
14
|
</thead>
|
14
15
|
<tbody>
|
@@ -24,13 +25,17 @@
|
|
24
25
|
<% end %>
|
25
26
|
</td>
|
26
27
|
<td>
|
27
|
-
<%= form_for translation&.presence || Moirai::Translation.new(key: key, locale: @locale, value: value),
|
28
|
+
<%= form_for translation&.presence || Moirai::Translation.new(key: key, locale: @locale, value: value),
|
29
|
+
url: moirai_create_or_update_translation_path,
|
30
|
+
method: :post do |f| %>
|
28
31
|
<%= f.hidden_field :key %>
|
29
32
|
<%= f.hidden_field :locale %>
|
30
|
-
<%= f.text_field :value %>
|
31
|
-
<%= f.submit 'Update', style: 'display: none;' %>
|
33
|
+
<%= f.text_field :value, class: 'translation-textarea' %>
|
32
34
|
<% end %>
|
33
35
|
</td>
|
36
|
+
<td>
|
37
|
+
<%= I18n.translate_without_moirai(key, @locale) %>
|
38
|
+
</td>
|
34
39
|
</tr>
|
35
40
|
<% end %>
|
36
41
|
</tbody>
|
@@ -10,9 +10,35 @@ module Moirai
|
|
10
10
|
invoke "moirai:migration"
|
11
11
|
end
|
12
12
|
|
13
|
+
def setup_javascript
|
14
|
+
if using_importmap?
|
15
|
+
say "Pin moirai"
|
16
|
+
string_to_be_added = "pin \"controllers/moirai_translation_controller\", to: \"moirai_translation_controller.js\""
|
17
|
+
say %(Appending: #{string_to_be_added})
|
18
|
+
append_to_file "config/importmap.rb", %(#{string_to_be_added}\n)
|
19
|
+
elsif using_js_bundling?
|
20
|
+
append_path = "app/javascript/controllers/moirai_translation_controller.js"
|
21
|
+
say "Copying Moirai Stimulus controller in #{append_path}"
|
22
|
+
copy_file "../../../../app/assets/javascripts/moirai_translation_controller.js", append_path
|
23
|
+
rails_command "stimulus:manifest:update"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def using_js_bundling?
|
30
|
+
Rails.root.join("app/javascript/controllers").exist?
|
31
|
+
end
|
32
|
+
|
13
33
|
def mount_engine
|
14
34
|
route 'mount Moirai::Engine => "/moirai", as: "moirai"'
|
15
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def using_importmap?
|
40
|
+
Rails.root.join("config/importmap.rb").exist?
|
41
|
+
end
|
16
42
|
end
|
17
43
|
end
|
18
44
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
class << self
|
5
|
+
attr_accessor :original_backend
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.translate_without_moirai(key, locale, **)
|
9
|
+
raise "Original backend is not set" unless original_backend
|
10
|
+
|
11
|
+
original_backend.translate(locale, key, **)
|
12
|
+
end
|
13
|
+
end
|
data/lib/moirai/engine.rb
CHANGED
@@ -9,7 +9,20 @@ module Moirai
|
|
9
9
|
end
|
10
10
|
|
11
11
|
config.after_initialize do
|
12
|
-
I18n.
|
12
|
+
I18n.original_backend = I18n.backend
|
13
|
+
table_created =
|
14
|
+
begin
|
15
|
+
(defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter) &&
|
16
|
+
ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)) ||
|
17
|
+
ActiveRecord::Base.connection.table_exists?("moirai_translations")
|
18
|
+
rescue ActiveRecord::NoDatabaseError
|
19
|
+
false
|
20
|
+
end
|
21
|
+
if table_created
|
22
|
+
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Moirai.new, I18n.backend)
|
23
|
+
else
|
24
|
+
Rails.logger.warn("moirai disabled: tables have not been generated yet.")
|
25
|
+
end
|
13
26
|
end
|
14
27
|
|
15
28
|
# TODO: how to do this without rewriting the entire method?
|
@@ -22,12 +35,18 @@ module Moirai
|
|
22
35
|
def translate(key, **options)
|
23
36
|
value = original_translate(key, **options)
|
24
37
|
|
38
|
+
is_missing_translation = value.include?('class="translation_missing"')
|
39
|
+
if value.is_a?(String) && is_missing_translation
|
40
|
+
value = extract_inner_content(value)
|
41
|
+
end
|
42
|
+
|
25
43
|
if moirai_edit_enabled?
|
26
44
|
@key_finder ||= Moirai::KeyFinder.new
|
27
45
|
|
28
46
|
render(partial: "moirai/translation_files/form",
|
29
47
|
locals: {key: scope_key_by_partial(key),
|
30
48
|
locale: I18n.locale,
|
49
|
+
is_missing_translation: is_missing_translation,
|
31
50
|
value: value})
|
32
51
|
else
|
33
52
|
value
|
@@ -39,6 +58,13 @@ module Moirai
|
|
39
58
|
def moirai_edit_enabled?
|
40
59
|
params[:moirai] == "true"
|
41
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def extract_inner_content(html)
|
65
|
+
match = html.match(/<[^>]+>([^<]*)<\/[^>]+>/)
|
66
|
+
match ? match[1] : nil
|
67
|
+
end
|
42
68
|
end
|
43
69
|
end
|
44
70
|
end
|
data/lib/moirai/version.rb
CHANGED
data/lib/moirai.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "moirai/version"
|
4
|
+
require "i18n/extensions/i18n"
|
5
|
+
require "i18n/backend/moirai"
|
4
6
|
require "moirai/engine"
|
5
7
|
require "moirai/pull_request_creator"
|
6
|
-
require "i18n/backend/moirai"
|
7
8
|
|
8
9
|
module Moirai
|
9
10
|
# Your code goes here...
|
data/moirai.gemspec
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "lib/moirai/version"
|
4
|
+
|
3
5
|
Gem::Specification.new do |spec|
|
4
6
|
spec.name = "moirai"
|
5
|
-
spec.version =
|
7
|
+
spec.version = Moirai::VERSION
|
6
8
|
spec.authors = ["Alessandro Rodi", "Oliver Anthony", "Daniel Bengl"]
|
7
9
|
spec.email = %w[alessandro.rodi@renuo.ch oliver.anthony@renuo.ch daniel.bengl@renuo.ch]
|
8
10
|
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moirai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Rodi
|
8
8
|
- Oliver Anthony
|
9
9
|
- Daniel Bengl
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-11-
|
13
|
+
date: 2024-11-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -38,8 +38,10 @@ extensions: []
|
|
38
38
|
extra_rdoc_files: []
|
39
39
|
files:
|
40
40
|
- ".github/CODEOWNERS"
|
41
|
-
- ".github/workflows/test.yml"
|
42
41
|
- ".gitignore"
|
42
|
+
- ".ruby-version"
|
43
|
+
- ".semaphore/main-deploy.yml"
|
44
|
+
- ".semaphore/semaphore.yml"
|
43
45
|
- Appraisals
|
44
46
|
- CHANGELOG.md
|
45
47
|
- Gemfile
|
@@ -49,6 +51,7 @@ files:
|
|
49
51
|
- app/assets/images/moirai/.keep
|
50
52
|
- app/assets/javascripts/moirai_translation_controller.js
|
51
53
|
- app/assets/stylesheets/moirai/application.css
|
54
|
+
- app/assets/stylesheets/translation_files.css
|
52
55
|
- app/controllers/concerns/.keep
|
53
56
|
- app/controllers/moirai/application_controller.rb
|
54
57
|
- app/controllers/moirai/translation_files_controller.rb
|
@@ -78,6 +81,7 @@ files:
|
|
78
81
|
- lib/generators/moirai/templates/create_moirai_translations.rb.erb
|
79
82
|
- lib/generators/moirai/templates/make_moirai_translations_file_path_not_required.rb.erb
|
80
83
|
- lib/i18n/backend/moirai.rb
|
84
|
+
- lib/i18n/extensions/i18n.rb
|
81
85
|
- lib/moirai.rb
|
82
86
|
- lib/moirai/engine.rb
|
83
87
|
- lib/moirai/pull_request_creator.rb
|
@@ -92,7 +96,7 @@ metadata:
|
|
92
96
|
changelog_uri: https://github.com/renuo/moirai/CHANGELOG.md
|
93
97
|
steep_types: sig
|
94
98
|
rubygems_mfa_required: 'true'
|
95
|
-
post_install_message:
|
99
|
+
post_install_message:
|
96
100
|
rdoc_options: []
|
97
101
|
require_paths:
|
98
102
|
- lib
|
@@ -107,8 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
111
|
- !ruby/object:Gem::Version
|
108
112
|
version: '0'
|
109
113
|
requirements: []
|
110
|
-
rubygems_version: 3.4.
|
111
|
-
signing_key:
|
114
|
+
rubygems_version: 3.4.7
|
115
|
+
signing_key:
|
112
116
|
specification_version: 4
|
113
117
|
summary: Manage translation strings in real time
|
114
118
|
test_files: []
|
data/.github/workflows/test.yml
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
name: Test & lint
|
2
|
-
on: [push]
|
3
|
-
|
4
|
-
env:
|
5
|
-
RAILS_ENV: test
|
6
|
-
PGHOST: localhost
|
7
|
-
PGUSER: postgres
|
8
|
-
MOIRAI_GITHUB_REPO_NAME: ${{ secrets.MOIRAI_GITHUB_REPO_NAME }}
|
9
|
-
MOIRAI_GITHUB_ACCESS_TOKEN: ${{ secrets.MOIRAI_GITHUB_ACCESS_TOKEN }}
|
10
|
-
jobs:
|
11
|
-
tests:
|
12
|
-
name: Test
|
13
|
-
|
14
|
-
strategy:
|
15
|
-
fail-fast: false
|
16
|
-
matrix:
|
17
|
-
os: [ubuntu-latest]
|
18
|
-
ruby: [3.3]
|
19
|
-
|
20
|
-
runs-on: ${{ matrix.os }}
|
21
|
-
|
22
|
-
steps:
|
23
|
-
- name: Checkout code
|
24
|
-
uses: actions/checkout@v4
|
25
|
-
|
26
|
-
- name: Set up Ruby
|
27
|
-
uses: ruby/setup-ruby@v1
|
28
|
-
with:
|
29
|
-
ruby-version: ${{ matrix.ruby }}
|
30
|
-
bundler-cache: true
|
31
|
-
|
32
|
-
- name: Install dependencies
|
33
|
-
run: bundle install --jobs 4 --retry 3
|
34
|
-
|
35
|
-
- name: Run tests
|
36
|
-
run: bundle exec rails test
|
37
|
-
|
38
|
-
- name: Run system tests
|
39
|
-
run: bundle exec rails test:system
|
40
|
-
|
41
|
-
|
42
|
-
lint:
|
43
|
-
name: Lint
|
44
|
-
runs-on: ubuntu-latest
|
45
|
-
|
46
|
-
steps:
|
47
|
-
- name: Checkout code
|
48
|
-
uses: actions/checkout@v2
|
49
|
-
|
50
|
-
- name: Set up Ruby
|
51
|
-
uses: ruby/setup-ruby@v1
|
52
|
-
with:
|
53
|
-
ruby-version: 3.3
|
54
|
-
bundler-cache: true
|
55
|
-
|
56
|
-
- name: Install dependencies
|
57
|
-
run: bundle install --jobs 4 --retry 3
|
58
|
-
|
59
|
-
- name: Run linters
|
60
|
-
run: bin/fastcheck
|