lesli_babel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +5 -0
  3. data/app/assets/config/lesli_babel_manifest.js +7 -0
  4. data/app/assets/images/lesli_babel/babel-logo.svg +157 -0
  5. data/app/assets/javascripts/lesli_babel/application.js +3288 -0
  6. data/app/assets/stylesheets/lesli_babel/application.scss +41 -0
  7. data/app/assets/stylesheets/lesli_babel/dashboards.scss +55 -0
  8. data/app/assets/stylesheets/lesli_babel/modules.scss +33 -0
  9. data/app/assets/stylesheets/lesli_babel/translations.scss +58 -0
  10. data/app/controllers/lesli_babel/application_controller.rb +37 -0
  11. data/app/controllers/lesli_babel/buckets_controller.rb +79 -0
  12. data/app/controllers/lesli_babel/clones_controller.rb +80 -0
  13. data/app/controllers/lesli_babel/dashboards_controller.rb +38 -0
  14. data/app/controllers/lesli_babel/modules_controller.rb +91 -0
  15. data/app/controllers/lesli_babel/relevants_controller.rb +38 -0
  16. data/app/controllers/lesli_babel/strings_controller.rb +142 -0
  17. data/app/controllers/lesli_babel/translations_controller.rb +134 -0
  18. data/app/jobs/lesli_babel/application_job.rb +7 -0
  19. data/app/mailers/lesli_babel/application_mailer.rb +6 -0
  20. data/app/models/lesli_babel/application_record.rb +36 -0
  21. data/app/models/lesli_babel/bucket.rb +6 -0
  22. data/app/models/lesli_babel/clone.rb +24 -0
  23. data/app/models/lesli_babel/dashboard.rb +35 -0
  24. data/app/models/lesli_babel/module.rb +35 -0
  25. data/app/models/lesli_babel/relevant.rb +21 -0
  26. data/app/models/lesli_babel/string/activity.rb +5 -0
  27. data/app/models/lesli_babel/string/discussion.rb +68 -0
  28. data/app/models/lesli_babel/string.rb +225 -0
  29. data/app/models/lesli_babel/translation.rb +5 -0
  30. data/app/services/lesli_babel/module_service.rb +18 -0
  31. data/app/services/lesli_babel/old/translations_android_service.rb +82 -0
  32. data/app/services/lesli_babel/old/translations_clone_service.rb +161 -0
  33. data/app/services/lesli_babel/old/translations_flutter_service.rb +102 -0
  34. data/app/services/lesli_babel/old/translations_ios_service.rb +119 -0
  35. data/app/services/lesli_babel/old/translations_js_service.rb +114 -0
  36. data/app/services/lesli_babel/old/translations_middleman_service.rb +77 -0
  37. data/app/services/lesli_babel/old/translations_rails_service.rb +119 -0
  38. data/app/services/lesli_babel/old/translations_service.rb +97 -0
  39. data/app/services/lesli_babel/old/translations_synchronization_service.rb +215 -0
  40. data/app/services/lesli_babel/string_service.rb +80 -0
  41. data/app/views/lesli_babel/buckets/_form.html.erb +17 -0
  42. data/app/views/lesli_babel/buckets/edit.html.erb +6 -0
  43. data/app/views/lesli_babel/buckets/index.html.erb +25 -0
  44. data/app/views/lesli_babel/buckets/new.html.erb +5 -0
  45. data/app/views/lesli_babel/buckets/show.html.erb +4 -0
  46. data/app/views/lesli_babel/clones/_form.html.erb +19 -0
  47. data/app/views/lesli_babel/clones/edit.html.erb +20 -0
  48. data/app/views/lesli_babel/clones/index.html.erb +20 -0
  49. data/app/views/lesli_babel/clones/new.html.erb +20 -0
  50. data/app/views/lesli_babel/clones/show.html.erb +20 -0
  51. data/app/views/lesli_babel/dashboards/show.html.erb +1 -0
  52. data/app/views/lesli_babel/dashboards/stats..html.erb +1 -0
  53. data/app/views/lesli_babel/modules/_form.html.erb +17 -0
  54. data/app/views/lesli_babel/modules/edit.html.erb +1 -0
  55. data/app/views/lesli_babel/modules/index.html.erb +1 -0
  56. data/app/views/lesli_babel/modules/new.html.erb +1 -0
  57. data/app/views/lesli_babel/modules/show.html.erb +1 -0
  58. data/app/views/lesli_babel/partials/_engine-navigation.html.erb +37 -0
  59. data/app/views/lesli_babel/partials/_engine-sidebar.html.erb +45 -0
  60. data/app/views/lesli_babel/relevants/edit.html.erb +20 -0
  61. data/app/views/lesli_babel/relevants/index.html.erb +21 -0
  62. data/app/views/lesli_babel/relevants/new.html.erb +20 -0
  63. data/app/views/lesli_babel/relevants/show.html.erb +20 -0
  64. data/app/views/lesli_babel/string/activities/_form.html.erb +17 -0
  65. data/app/views/lesli_babel/string/activities/edit.html.erb +6 -0
  66. data/app/views/lesli_babel/string/activities/index.html.erb +25 -0
  67. data/app/views/lesli_babel/string/activities/new.html.erb +5 -0
  68. data/app/views/lesli_babel/string/activities/show.html.erb +4 -0
  69. data/app/views/lesli_babel/string/discussions/_form.html.erb +17 -0
  70. data/app/views/lesli_babel/string/discussions/edit.html.erb +6 -0
  71. data/app/views/lesli_babel/string/discussions/index.html.erb +25 -0
  72. data/app/views/lesli_babel/string/discussions/new.html.erb +5 -0
  73. data/app/views/lesli_babel/string/discussions/show.html.erb +4 -0
  74. data/app/views/lesli_babel/strings/_form.html.erb +17 -0
  75. data/app/views/lesli_babel/strings/edit.html.erb +6 -0
  76. data/app/views/lesli_babel/strings/index.html.erb +25 -0
  77. data/app/views/lesli_babel/strings/new.html.erb +5 -0
  78. data/app/views/lesli_babel/strings/show.html.erb +4 -0
  79. data/app/views/lesli_babel/translations/_form.html.erb +17 -0
  80. data/app/views/lesli_babel/translations/edit.html.erb +6 -0
  81. data/app/views/lesli_babel/translations/index.html.erb +1 -0
  82. data/app/views/lesli_babel/translations/new.html.erb +5 -0
  83. data/app/views/lesli_babel/translations/show.html.erb +1 -0
  84. data/config/routes.rb +63 -0
  85. data/db/migrate/v1.0/0901100110_create_lesli_babel_modules.rb +42 -0
  86. data/db/migrate/v1.0/0901110110_create_lesli_babel_buckets.rb +43 -0
  87. data/db/migrate/v1.0/0901120110_create_lesli_babel_strings.rb +80 -0
  88. data/lib/lesli_babel/engine.rb +54 -0
  89. data/lib/lesli_babel/version.rb +3 -0
  90. data/lib/lesli_babel.rb +6 -0
  91. data/lib/tasks/lesli_babel_tasks.rake +121 -0
  92. data/lib/vue/application.js +46 -0
  93. data/lib/vue/apps/dashboards/show.vue +85 -0
  94. data/lib/vue/apps/modules/show.vue +97 -0
  95. data/lib/vue/apps/relevants/index.vue +47 -0
  96. data/lib/vue/apps/translations/index.vue +64 -0
  97. data/lib/vue/components/actions.vue +29 -0
  98. data/lib/vue/components/form-label-editor.vue +439 -0
  99. data/lib/vue/components/form-string-new.vue +171 -0
  100. data/lib/vue/stores/module.js +49 -0
  101. data/lib/vue/stores/statistics.js +42 -0
  102. data/lib/vue/stores/strings.js +141 -0
  103. data/lib/vue/stores/translations.js +73 -0
  104. data/license +674 -0
  105. data/readme.md +19 -0
  106. 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
+ &nbsp;
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
+ &nbsp;
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
+ })