lesli_babel 0.1.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 +7 -0
- data/Rakefile +5 -0
- data/app/assets/config/lesli_babel_manifest.js +7 -0
- data/app/assets/images/lesli_babel/babel-logo.svg +157 -0
- data/app/assets/javascripts/lesli_babel/application.js +3288 -0
- data/app/assets/stylesheets/lesli_babel/application.scss +41 -0
- data/app/assets/stylesheets/lesli_babel/dashboards.scss +55 -0
- data/app/assets/stylesheets/lesli_babel/modules.scss +33 -0
- data/app/assets/stylesheets/lesli_babel/translations.scss +58 -0
- data/app/controllers/lesli_babel/application_controller.rb +37 -0
- data/app/controllers/lesli_babel/buckets_controller.rb +79 -0
- data/app/controllers/lesli_babel/clones_controller.rb +80 -0
- data/app/controllers/lesli_babel/dashboards_controller.rb +38 -0
- data/app/controllers/lesli_babel/modules_controller.rb +91 -0
- data/app/controllers/lesli_babel/relevants_controller.rb +38 -0
- data/app/controllers/lesli_babel/strings_controller.rb +142 -0
- data/app/controllers/lesli_babel/translations_controller.rb +134 -0
- data/app/jobs/lesli_babel/application_job.rb +7 -0
- data/app/mailers/lesli_babel/application_mailer.rb +6 -0
- data/app/models/lesli_babel/application_record.rb +36 -0
- data/app/models/lesli_babel/bucket.rb +6 -0
- data/app/models/lesli_babel/clone.rb +24 -0
- data/app/models/lesli_babel/dashboard.rb +35 -0
- data/app/models/lesli_babel/module.rb +35 -0
- data/app/models/lesli_babel/relevant.rb +21 -0
- data/app/models/lesli_babel/string/activity.rb +5 -0
- data/app/models/lesli_babel/string/discussion.rb +68 -0
- data/app/models/lesli_babel/string.rb +225 -0
- data/app/models/lesli_babel/translation.rb +5 -0
- data/app/services/lesli_babel/module_service.rb +18 -0
- data/app/services/lesli_babel/old/translations_android_service.rb +82 -0
- data/app/services/lesli_babel/old/translations_clone_service.rb +161 -0
- data/app/services/lesli_babel/old/translations_flutter_service.rb +102 -0
- data/app/services/lesli_babel/old/translations_ios_service.rb +119 -0
- data/app/services/lesli_babel/old/translations_js_service.rb +114 -0
- data/app/services/lesli_babel/old/translations_middleman_service.rb +77 -0
- data/app/services/lesli_babel/old/translations_rails_service.rb +119 -0
- data/app/services/lesli_babel/old/translations_service.rb +97 -0
- data/app/services/lesli_babel/old/translations_synchronization_service.rb +215 -0
- data/app/services/lesli_babel/string_service.rb +80 -0
- data/app/views/lesli_babel/buckets/_form.html.erb +17 -0
- data/app/views/lesli_babel/buckets/edit.html.erb +6 -0
- data/app/views/lesli_babel/buckets/index.html.erb +25 -0
- data/app/views/lesli_babel/buckets/new.html.erb +5 -0
- data/app/views/lesli_babel/buckets/show.html.erb +4 -0
- data/app/views/lesli_babel/clones/_form.html.erb +19 -0
- data/app/views/lesli_babel/clones/edit.html.erb +20 -0
- data/app/views/lesli_babel/clones/index.html.erb +20 -0
- data/app/views/lesli_babel/clones/new.html.erb +20 -0
- data/app/views/lesli_babel/clones/show.html.erb +20 -0
- data/app/views/lesli_babel/dashboards/show.html.erb +1 -0
- data/app/views/lesli_babel/dashboards/stats..html.erb +1 -0
- data/app/views/lesli_babel/modules/_form.html.erb +17 -0
- data/app/views/lesli_babel/modules/edit.html.erb +1 -0
- data/app/views/lesli_babel/modules/index.html.erb +1 -0
- data/app/views/lesli_babel/modules/new.html.erb +1 -0
- data/app/views/lesli_babel/modules/show.html.erb +1 -0
- data/app/views/lesli_babel/partials/_engine-navigation.html.erb +37 -0
- data/app/views/lesli_babel/partials/_engine-sidebar.html.erb +45 -0
- data/app/views/lesli_babel/relevants/edit.html.erb +20 -0
- data/app/views/lesli_babel/relevants/index.html.erb +21 -0
- data/app/views/lesli_babel/relevants/new.html.erb +20 -0
- data/app/views/lesli_babel/relevants/show.html.erb +20 -0
- data/app/views/lesli_babel/string/activities/_form.html.erb +17 -0
- data/app/views/lesli_babel/string/activities/edit.html.erb +6 -0
- data/app/views/lesli_babel/string/activities/index.html.erb +25 -0
- data/app/views/lesli_babel/string/activities/new.html.erb +5 -0
- data/app/views/lesli_babel/string/activities/show.html.erb +4 -0
- data/app/views/lesli_babel/string/discussions/_form.html.erb +17 -0
- data/app/views/lesli_babel/string/discussions/edit.html.erb +6 -0
- data/app/views/lesli_babel/string/discussions/index.html.erb +25 -0
- data/app/views/lesli_babel/string/discussions/new.html.erb +5 -0
- data/app/views/lesli_babel/string/discussions/show.html.erb +4 -0
- data/app/views/lesli_babel/strings/_form.html.erb +17 -0
- data/app/views/lesli_babel/strings/edit.html.erb +6 -0
- data/app/views/lesli_babel/strings/index.html.erb +25 -0
- data/app/views/lesli_babel/strings/new.html.erb +5 -0
- data/app/views/lesli_babel/strings/show.html.erb +4 -0
- data/app/views/lesli_babel/translations/_form.html.erb +17 -0
- data/app/views/lesli_babel/translations/edit.html.erb +6 -0
- data/app/views/lesli_babel/translations/index.html.erb +1 -0
- data/app/views/lesli_babel/translations/new.html.erb +5 -0
- data/app/views/lesli_babel/translations/show.html.erb +1 -0
- data/config/routes.rb +63 -0
- data/db/migrate/v1.0/0901100110_create_lesli_babel_modules.rb +42 -0
- data/db/migrate/v1.0/0901110110_create_lesli_babel_buckets.rb +43 -0
- data/db/migrate/v1.0/0901120110_create_lesli_babel_strings.rb +80 -0
- data/lib/lesli_babel/engine.rb +54 -0
- data/lib/lesli_babel/version.rb +3 -0
- data/lib/lesli_babel.rb +6 -0
- data/lib/tasks/lesli_babel_tasks.rake +121 -0
- data/lib/vue/application.js +46 -0
- data/lib/vue/apps/dashboards/show.vue +85 -0
- data/lib/vue/apps/modules/show.vue +97 -0
- data/lib/vue/apps/relevants/index.vue +47 -0
- data/lib/vue/apps/translations/index.vue +64 -0
- data/lib/vue/components/actions.vue +29 -0
- data/lib/vue/components/form-label-editor.vue +439 -0
- data/lib/vue/components/form-string-new.vue +171 -0
- data/lib/vue/stores/module.js +49 -0
- data/lib/vue/stores/statistics.js +42 -0
- data/lib/vue/stores/strings.js +141 -0
- data/lib/vue/stores/translations.js +73 -0
- data/license +674 -0
- data/readme.md +19 -0
- metadata +149 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2022, all rights reserved.
|
|
4
|
+
|
|
5
|
+
All the information provided by this platform is protected by international laws related to
|
|
6
|
+
industrial property, intellectual property, copyright and relative international laws.
|
|
7
|
+
All intellectual or industrial property rights of the code, texts, trade mark, design,
|
|
8
|
+
pictures and any other information belongs to the owner of this platform.
|
|
9
|
+
|
|
10
|
+
Without the written permission of the owner, any replication, modification,
|
|
11
|
+
transmission, publication is strictly forbidden.
|
|
12
|
+
|
|
13
|
+
For more information read the license file including with this software.
|
|
14
|
+
|
|
15
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
16
|
+
// ·
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// · import vue tools
|
|
21
|
+
import { ref, reactive, onMounted, watch, computed, onUnmounted } from "vue"
|
|
22
|
+
import { useRouter, useRoute } from 'vue-router'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// · import lesli stores
|
|
26
|
+
const route = useRoute()
|
|
27
|
+
import { useStrings } from "LesliBabel/stores/strings"
|
|
28
|
+
import { useTranslations } from "LesliBabel/stores/translations"
|
|
29
|
+
import { useServiceTranslator } from "Lesli/stores/services/translator"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
// · implement stores
|
|
33
|
+
const storeStrings = useStrings()
|
|
34
|
+
const storeTranslations = useTranslations()
|
|
35
|
+
const storeServiceTranslator = useServiceTranslator()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// ·
|
|
39
|
+
const props = defineProps({
|
|
40
|
+
module: {
|
|
41
|
+
type: [Number, String],
|
|
42
|
+
require: false
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// · columns of the editor table
|
|
48
|
+
const columns = ref([])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
// · selected language to work with
|
|
52
|
+
const language = ref(null)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// ·
|
|
56
|
+
onMounted(() => {
|
|
57
|
+
|
|
58
|
+
// english by default if custom language not sent through url
|
|
59
|
+
language.value = route.query?.locale ?? 'en'
|
|
60
|
+
|
|
61
|
+
// get options for translations
|
|
62
|
+
storeTranslations.fetchOptions()
|
|
63
|
+
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// · get translations checking configuration provided through the store or query
|
|
68
|
+
function fetchTranslations() {
|
|
69
|
+
|
|
70
|
+
// always reset the config
|
|
71
|
+
storeStrings.search = ""
|
|
72
|
+
storeStrings.module = 0
|
|
73
|
+
storeStrings.ids = null
|
|
74
|
+
|
|
75
|
+
// work with relevant translations
|
|
76
|
+
if (props.module == "relevants") {
|
|
77
|
+
return storeStrings.fetchRelevant()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// check if labels ids are provided
|
|
81
|
+
if (route.query?.ids) {
|
|
82
|
+
storeStrings.ids = route.query?.ids
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// check if search params is provided through query
|
|
86
|
+
if (route.query?.search) {
|
|
87
|
+
storeStrings.search = route.query?.search
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// work with an specific module if provided
|
|
91
|
+
if (props.module) {
|
|
92
|
+
storeStrings.module = props.module
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return storeStrings.fetchStrings()
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
// · load all the columns available for the editor
|
|
101
|
+
function resetColumns() {
|
|
102
|
+
|
|
103
|
+
// reset columns
|
|
104
|
+
columns.value = []
|
|
105
|
+
|
|
106
|
+
// load defaults columns for the editor table
|
|
107
|
+
columns.value.push({
|
|
108
|
+
label: 'Label to translate',
|
|
109
|
+
field: 'label'
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// dynamic add the column related to the selected working language
|
|
113
|
+
columns.value.push({
|
|
114
|
+
label: storeTranslations?.options?.locales_available[language.value],
|
|
115
|
+
field: language.value,
|
|
116
|
+
width: '100%'
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
// · build the title for the table custom headers
|
|
122
|
+
// · this is necessary to show the custom header cell for every language
|
|
123
|
+
// · (we show only one language at the time)
|
|
124
|
+
function languageHead(language) {
|
|
125
|
+
return 'head('+language+')'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
// suggest translation from english to desire language
|
|
130
|
+
// to make this work we need to have the label already translated to english
|
|
131
|
+
function suggestTranslation(label, locale) {
|
|
132
|
+
|
|
133
|
+
// we use the translator service (integrated with google translate)
|
|
134
|
+
// we always send the text in english due the label key is compound with the
|
|
135
|
+
// collection and bucket names
|
|
136
|
+
storeServiceTranslator.getTranslation(label["en"], "en", locale).then(result => {
|
|
137
|
+
|
|
138
|
+
// update the translation
|
|
139
|
+
label[locale] = result.translatedText
|
|
140
|
+
|
|
141
|
+
// custom property added just here to let the editor know that an automatic
|
|
142
|
+
// translation was added to the label
|
|
143
|
+
label[`translated_${locale}`] = true
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// remove a translation for a specific locale
|
|
149
|
+
// mostly used with suggested translations
|
|
150
|
+
function clearStringTranslation(record, locale) {
|
|
151
|
+
record[locale] = ''
|
|
152
|
+
record[`translated_${locale}`] = false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
// build a string to use as direct link to the selected label
|
|
157
|
+
function getLabelLink(id) {
|
|
158
|
+
return `${window.location.host}/babel/translations?ids=${id}`
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
// switch a label as help needed
|
|
163
|
+
function askForHelp(record) {
|
|
164
|
+
record.need_help = !record.need_help
|
|
165
|
+
storeStrings.putString(record)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
// switch a label as help needed
|
|
170
|
+
function askForTranslation(record) {
|
|
171
|
+
record.need_translation = !record.need_translation
|
|
172
|
+
storeStrings.putString(record)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
// · get strings for the module selected, reset if module changed
|
|
177
|
+
watch(() => props.module, () => {
|
|
178
|
+
storeStrings.search = ""
|
|
179
|
+
storeStrings.module = props.module
|
|
180
|
+
fetchTranslations()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
watch(() => route.query.search, string => {
|
|
185
|
+
storeStrings.search = string
|
|
186
|
+
fetchTranslations()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
// · changing the working language, keep config if language changed
|
|
191
|
+
watch(() => language.value, (language) => {
|
|
192
|
+
storeStrings.language = language
|
|
193
|
+
fetchTranslations()
|
|
194
|
+
resetColumns()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
// · watch for the locales available to dynamically show language columns in the editor
|
|
199
|
+
watch(() => storeTranslations.options.locales_available, () => {
|
|
200
|
+
resetColumns()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
// · go to the first input once translations loaded
|
|
205
|
+
watch(() => storeStrings.strings.records, () => {
|
|
206
|
+
setTimeout(() => { nextTranslation() }, 1000)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
// ·
|
|
211
|
+
function copyToClipboard(button, text) {
|
|
212
|
+
const el = document.createElement('textarea');
|
|
213
|
+
el.value = text; // text to copy
|
|
214
|
+
el.setAttribute('readonly', '');
|
|
215
|
+
el.style.position = 'absolute';
|
|
216
|
+
el.style.left = '-9999px';
|
|
217
|
+
document.body.appendChild(el);
|
|
218
|
+
el.select();
|
|
219
|
+
document.execCommand('copy');
|
|
220
|
+
document.body.removeChild(el);
|
|
221
|
+
button.target.classList.add('copied')
|
|
222
|
+
setTimeout(() => button.target.classList.remove('copied'), 1000)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
// · Navigate to the next translation using arrow keys
|
|
227
|
+
function nextTranslation () {
|
|
228
|
+
|
|
229
|
+
var table = document.getElementById("babel-translations")
|
|
230
|
+
var inputs = table.getElementsByTagName("input")
|
|
231
|
+
|
|
232
|
+
for(var i = 0 ; i < inputs.length;i++) {
|
|
233
|
+
|
|
234
|
+
// add a listener for keydown
|
|
235
|
+
inputs[i].addEventListener('keydown', function(e){
|
|
236
|
+
|
|
237
|
+
// execute only for down/up/enter keys
|
|
238
|
+
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13) {
|
|
239
|
+
|
|
240
|
+
// remove default behavior for arrow keys
|
|
241
|
+
e.preventDefault()
|
|
242
|
+
|
|
243
|
+
// get the index of the current index
|
|
244
|
+
var currentIndex = findElement(e.target)
|
|
245
|
+
|
|
246
|
+
// work only with valid index between 0 ~ n
|
|
247
|
+
if (currentIndex < 0 || currentIndex > inputs.length) {
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// downkey
|
|
252
|
+
if (e.keyCode == 38) {
|
|
253
|
+
if (inputs[currentIndex-1]) {
|
|
254
|
+
inputs[currentIndex-1].focus();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// upkey
|
|
259
|
+
if (e.keyCode == 40 || e.keyCode == 13) {
|
|
260
|
+
if (inputs[currentIndex+1]) {
|
|
261
|
+
inputs[currentIndex+1].focus();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// get the current selected input
|
|
271
|
+
function findElement(element) {
|
|
272
|
+
var index = -1;
|
|
273
|
+
for(var i = 0; i < inputs.length; i++) {
|
|
274
|
+
if(inputs[i] == element) {
|
|
275
|
+
return i;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return index;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
</script>
|
|
284
|
+
<template>
|
|
285
|
+
<lesli-table
|
|
286
|
+
id="babel-translations"
|
|
287
|
+
:loading="storeStrings.strings.loading"
|
|
288
|
+
:records="storeStrings.strings.records"
|
|
289
|
+
:columns="columns"
|
|
290
|
+
@paginate="storeStrings.fetchStrings()"
|
|
291
|
+
@details="nextTranslation()">
|
|
292
|
+
|
|
293
|
+
<!--
|
|
294
|
+
Table custom header, renders a language selector
|
|
295
|
+
-->
|
|
296
|
+
<template #[languageHead(language)]="{ column }">
|
|
297
|
+
<lesli-select
|
|
298
|
+
icon="public"
|
|
299
|
+
v-model="language"
|
|
300
|
+
:options="storeTranslations.locales">
|
|
301
|
+
</lesli-select>
|
|
302
|
+
</template>
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
<!--
|
|
306
|
+
Print the label string with a button to easely copy to clipboard
|
|
307
|
+
-->
|
|
308
|
+
<template #label="{ record }">
|
|
309
|
+
<button
|
|
310
|
+
class="button is-white p-0"
|
|
311
|
+
@click.stop="copyToClipboard($event, record.label)"
|
|
312
|
+
@contextmenu.capture.prevent="copyToClipboard($event, record.path)">
|
|
313
|
+
<span class="icon has-text-grey">
|
|
314
|
+
<span class="material-icons">
|
|
315
|
+
content_copy
|
|
316
|
+
</span>
|
|
317
|
+
</span>
|
|
318
|
+
<span>
|
|
319
|
+
{{ record.label }}
|
|
320
|
+
</span>
|
|
321
|
+
</button>
|
|
322
|
+
</template>
|
|
323
|
+
|
|
324
|
+
<!--
|
|
325
|
+
Print a input to edit the translation for the current locale
|
|
326
|
+
-->
|
|
327
|
+
<template #[language]="{ value, record }">
|
|
328
|
+
<div class="is-flex is-align-items-center">
|
|
329
|
+
<input
|
|
330
|
+
type="text"
|
|
331
|
+
class="input"
|
|
332
|
+
placeholder="Add translations..."
|
|
333
|
+
@input="storeStrings.updateString(record, language)"
|
|
334
|
+
v-model="record[language]"
|
|
335
|
+
/>
|
|
336
|
+
<span class="icon mx-1" v-if="record.need_help">
|
|
337
|
+
<span class="material-icons has-text-info">
|
|
338
|
+
help_outline
|
|
339
|
+
</span>
|
|
340
|
+
</span>
|
|
341
|
+
<span class="icon mx-1" v-if="record.need_translation">
|
|
342
|
+
<span class="material-icons has-text-warning-dark">
|
|
343
|
+
translate
|
|
344
|
+
</span>
|
|
345
|
+
</span>
|
|
346
|
+
</div>
|
|
347
|
+
</template>
|
|
348
|
+
|
|
349
|
+
<template #detail="{ record }">
|
|
350
|
+
<tr v-for="(locale_name, locale_code) in storeTranslations.options.locales_available">
|
|
351
|
+
<td></td>
|
|
352
|
+
<td class="has-text-right">{{ locale_name }}</td>
|
|
353
|
+
<td class="is-flex is-align-items-center">
|
|
354
|
+
<input
|
|
355
|
+
type="text"
|
|
356
|
+
class="input"
|
|
357
|
+
placeholder="Add translations..."
|
|
358
|
+
v-model="record[locale_code]"
|
|
359
|
+
@input="storeStrings.updateString(record, locale_code)"
|
|
360
|
+
/>
|
|
361
|
+
<lesli-button
|
|
362
|
+
icon-only small danger icon="clear"
|
|
363
|
+
v-if="record[`translated_${locale_code}`] == true"
|
|
364
|
+
@click="clearStringTranslation(record, locale_code)">
|
|
365
|
+
</lesli-button>
|
|
366
|
+
|
|
367
|
+
<lesli-button
|
|
368
|
+
icon-only small icon="save"
|
|
369
|
+
v-if="record[`translated_${locale_code}`] == true"
|
|
370
|
+
@click="storeStrings.putString(record)">
|
|
371
|
+
</lesli-button>
|
|
372
|
+
|
|
373
|
+
<lesli-button
|
|
374
|
+
icon-only small icon="translate"
|
|
375
|
+
v-if="!!record['en']"
|
|
376
|
+
@click="suggestTranslation(record, locale_code)">
|
|
377
|
+
</lesli-button>
|
|
378
|
+
</td>
|
|
379
|
+
</tr>
|
|
380
|
+
<tr>
|
|
381
|
+
<td></td>
|
|
382
|
+
<td class="has-text-right">Context</td>
|
|
383
|
+
<td>
|
|
384
|
+
<input
|
|
385
|
+
type="text"
|
|
386
|
+
class="input"
|
|
387
|
+
placeholder="Add translation context..."
|
|
388
|
+
v-model="record.context"
|
|
389
|
+
@input="storeStrings.updateString(record)"
|
|
390
|
+
/>
|
|
391
|
+
</td>
|
|
392
|
+
</tr>
|
|
393
|
+
<tr>
|
|
394
|
+
<td></td>
|
|
395
|
+
<td class="has-text-right">Full path</td>
|
|
396
|
+
<td>
|
|
397
|
+
<button
|
|
398
|
+
class="button is-primary is-inverted"
|
|
399
|
+
@click.stop="copyToClipboard($event, record.path)">
|
|
400
|
+
<span class="icon has-text-grey">
|
|
401
|
+
<span class="material-icons">
|
|
402
|
+
content_copy
|
|
403
|
+
</span>
|
|
404
|
+
</span>
|
|
405
|
+
<span>
|
|
406
|
+
{{ record.path }}
|
|
407
|
+
</span>
|
|
408
|
+
</button>
|
|
409
|
+
</td>
|
|
410
|
+
</tr>
|
|
411
|
+
<tr>
|
|
412
|
+
<td colspan="100%">
|
|
413
|
+
<div class="buttons is-justify-content-center">
|
|
414
|
+
<button
|
|
415
|
+
class="button is-primary is-small"
|
|
416
|
+
@click="copyToClipboard($event, getLabelLink(record.id))">
|
|
417
|
+
<span class="icon is-small">
|
|
418
|
+
<span class="material-icons">link</span>
|
|
419
|
+
</span>
|
|
420
|
+
<span>Copy link</span>
|
|
421
|
+
</button>
|
|
422
|
+
|
|
423
|
+
<lesli-button
|
|
424
|
+
small info solid icon="help_outline"
|
|
425
|
+
@click="askForHelp(record)">
|
|
426
|
+
Need help
|
|
427
|
+
</lesli-button>
|
|
428
|
+
|
|
429
|
+
<lesli-button
|
|
430
|
+
small warning solid icon="translate"
|
|
431
|
+
@click="askForTranslation(record)">
|
|
432
|
+
Need translation
|
|
433
|
+
</lesli-button>
|
|
434
|
+
</div>
|
|
435
|
+
</td>
|
|
436
|
+
</tr>
|
|
437
|
+
</template>
|
|
438
|
+
</lesli-table>
|
|
439
|
+
</template>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2022, all rights reserved.
|
|
4
|
+
|
|
5
|
+
All the information provided by this platform is protected by international laws related to
|
|
6
|
+
industrial property, intellectual property, copyright and relative international laws.
|
|
7
|
+
All intellectual or industrial property rights of the code, texts, trade mark, design,
|
|
8
|
+
pictures and any other information belongs to the owner of this platform.
|
|
9
|
+
|
|
10
|
+
Without the written permission of the owner, any replication, modification,
|
|
11
|
+
transmission, publication is strictly forbidden.
|
|
12
|
+
|
|
13
|
+
For more information read the license file including with this software.
|
|
14
|
+
|
|
15
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
16
|
+
// ·
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// · import vue tools
|
|
21
|
+
import { ref, reactive, onMounted, watch, computed, onUnmounted } from "vue"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// ·
|
|
25
|
+
import { useStrings } from "LesliBabel/stores/strings"
|
|
26
|
+
import { useModule } from "LesliBabel/stores/module"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// · implement stores
|
|
30
|
+
const storeModule = useModule()
|
|
31
|
+
const storeString = useStrings()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// ·
|
|
35
|
+
const props = defineProps({
|
|
36
|
+
moduleId: {
|
|
37
|
+
type: Number,
|
|
38
|
+
require: true
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// ·
|
|
44
|
+
onMounted(() => {
|
|
45
|
+
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
// ·
|
|
50
|
+
const bucket = ref('')
|
|
51
|
+
const prefix = ref('')
|
|
52
|
+
const string = ref('')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// ·
|
|
56
|
+
const prefixes = [{
|
|
57
|
+
label: 'account_init',
|
|
58
|
+
value: 'account_init'
|
|
59
|
+
}, {
|
|
60
|
+
label: 'activity_description',
|
|
61
|
+
value: 'activity_description'
|
|
62
|
+
}, {
|
|
63
|
+
label: "column",
|
|
64
|
+
value: "column"
|
|
65
|
+
}, {
|
|
66
|
+
label: "column_enum",
|
|
67
|
+
value: "column_enum"
|
|
68
|
+
}, {
|
|
69
|
+
label: "mailer",
|
|
70
|
+
value: "mailer"
|
|
71
|
+
}, {
|
|
72
|
+
label: "messages_success",
|
|
73
|
+
value: "messages_success"
|
|
74
|
+
}, {
|
|
75
|
+
label: "messages_info",
|
|
76
|
+
value: "messages_info"
|
|
77
|
+
}, {
|
|
78
|
+
label: "messages_warning",
|
|
79
|
+
value: "messages_warning"
|
|
80
|
+
}, {
|
|
81
|
+
label: "messages_danger",
|
|
82
|
+
value: "messages_danger"
|
|
83
|
+
}, {
|
|
84
|
+
label: "sidebar_nav",
|
|
85
|
+
value: "sidebar_nav"
|
|
86
|
+
}, {
|
|
87
|
+
label: "error",
|
|
88
|
+
value: "error"
|
|
89
|
+
}, {
|
|
90
|
+
label: "view",
|
|
91
|
+
value: "view"
|
|
92
|
+
}, {
|
|
93
|
+
label: "view_btn",
|
|
94
|
+
value: "view_btn"
|
|
95
|
+
}, {
|
|
96
|
+
label: "view_chart_title",
|
|
97
|
+
value: "view_chart_title"
|
|
98
|
+
}, {
|
|
99
|
+
label: "view_placeholder",
|
|
100
|
+
value: "view_placeholder"
|
|
101
|
+
}, {
|
|
102
|
+
label: "view_tab_title",
|
|
103
|
+
value: "view_tab_title"
|
|
104
|
+
}, {
|
|
105
|
+
label: "view_table_action",
|
|
106
|
+
value: "view_table_action"
|
|
107
|
+
}, {
|
|
108
|
+
label: "view_table_header",
|
|
109
|
+
value: "view_table_header"
|
|
110
|
+
}, {
|
|
111
|
+
label: "view_text",
|
|
112
|
+
value: "view_text"
|
|
113
|
+
}, {
|
|
114
|
+
label: "view_title",
|
|
115
|
+
value: "view_title"
|
|
116
|
+
}, {
|
|
117
|
+
label: "view_toolbar_filter",
|
|
118
|
+
value: "view_toolbar_filter"
|
|
119
|
+
}, {
|
|
120
|
+
label: "view_toolbar_filter_placeholder",
|
|
121
|
+
value: "view_toolbar_filter_placeholder"
|
|
122
|
+
}, {
|
|
123
|
+
label: "view_toolbar_search_by_placeholder",
|
|
124
|
+
value: "view_toolbar_search_by_placeholder"
|
|
125
|
+
}]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
function postString() {
|
|
129
|
+
storeString.post({
|
|
130
|
+
cloud_babel_buckets_id: bucket.value,
|
|
131
|
+
context: '',
|
|
132
|
+
label: prefix.value + "_" + string.value
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
string.value = ''
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
</script>
|
|
139
|
+
<template>
|
|
140
|
+
<form v-on:submit.prevent="postString" class="card is-shadowless">
|
|
141
|
+
<div class="card-content">
|
|
142
|
+
<div class="field">
|
|
143
|
+
<label class="label">Bucket</label>
|
|
144
|
+
<div class="control">
|
|
145
|
+
<lesli-select
|
|
146
|
+
v-model="bucket"
|
|
147
|
+
:options="storeModule.buckets.map(b => { return { value: b.id, label: b.name }})">
|
|
148
|
+
</lesli-select>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="field">
|
|
152
|
+
<label class="label">Prefix</label>
|
|
153
|
+
<div class="control">
|
|
154
|
+
<lesli-select
|
|
155
|
+
v-model="prefix"
|
|
156
|
+
:options="prefixes">
|
|
157
|
+
</lesli-select>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="field">
|
|
161
|
+
<label class="label">Label</label>
|
|
162
|
+
<div class="control">
|
|
163
|
+
<input required v-model="string" class="input" type="text" placeholder="Add label to translation workflow" />
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="control">
|
|
167
|
+
<input class="button is-primary" type="submit" value="Save" />
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</form>
|
|
171
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2022, all rights reserved.
|
|
3
|
+
|
|
4
|
+
All the information provided by this platform is protected by international laws related to
|
|
5
|
+
industrial property, intellectual property, copyright and relative international laws.
|
|
6
|
+
All intellectual or industrial property rights of the code, texts, trade mark, design,
|
|
7
|
+
pictures and any other information belongs to the owner of this platform.
|
|
8
|
+
|
|
9
|
+
Without the written permission of the owner, any replication, modification,
|
|
10
|
+
transmission, publication is strictly forbidden.
|
|
11
|
+
|
|
12
|
+
For more information read the license file including with this software.
|
|
13
|
+
|
|
14
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
15
|
+
// ·
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// ·
|
|
20
|
+
import { defineStore } from "pinia"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// ·
|
|
24
|
+
export const useModule = defineStore("babel.module", {
|
|
25
|
+
state: () => {
|
|
26
|
+
return {
|
|
27
|
+
id: 0,
|
|
28
|
+
name: "",
|
|
29
|
+
platform: "",
|
|
30
|
+
buckets: []
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
actions: {
|
|
34
|
+
fetchModule(modulo) {
|
|
35
|
+
if (!modulo) return;
|
|
36
|
+
this.http.get(this.url.babel("modules/:id", modulo)).then(result => {
|
|
37
|
+
this.id = result.id
|
|
38
|
+
this.name = result.name
|
|
39
|
+
this.platform = result.platform
|
|
40
|
+
this.fetchBuckets()
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
fetchBuckets() {
|
|
44
|
+
this.http.get(this.url.babel("modules/:id/buckets", this.id)).then(result => {
|
|
45
|
+
this.buckets = result
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2022, all rights reserved.
|
|
3
|
+
|
|
4
|
+
All the information provided by this platform is protected by international laws related to
|
|
5
|
+
industrial property, intellectual property, copyright and relative international laws.
|
|
6
|
+
All intellectual or industrial property rights of the code, texts, trade mark, design,
|
|
7
|
+
pictures and any other information belongs to the owner of this platform.
|
|
8
|
+
|
|
9
|
+
Without the written permission of the owner, any replication, modification,
|
|
10
|
+
transmission, publication is strictly forbidden.
|
|
11
|
+
|
|
12
|
+
For more information read the license file including with this software.
|
|
13
|
+
|
|
14
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
15
|
+
// ·
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// ·
|
|
20
|
+
import { defineStore } from "pinia"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// ·
|
|
24
|
+
export const useStatistics = defineStore("statistics", {
|
|
25
|
+
state: () => {
|
|
26
|
+
return {
|
|
27
|
+
lastSyncronizationAt: "",
|
|
28
|
+
totalStrings: 0,
|
|
29
|
+
languages: []
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
actions: {
|
|
33
|
+
fetch() {
|
|
34
|
+
this.http.get(this.url.babel('strings/stats')).then(result => {
|
|
35
|
+
this.lastSyncronizationAt = result.last_syncronization_at
|
|
36
|
+
this.totalStrings = result.total_strings
|
|
37
|
+
this.languages = result.total_strings_translations
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
})
|