lesli_babel 1.0.0 → 1.1.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.
@@ -1,47 +0,0 @@
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, inject } from "vue"
22
- import { useRouter, useRoute } from 'vue-router'
23
-
24
-
25
- // · initialize/inject plugins
26
- const router = useRouter()
27
- const route = useRoute()
28
- const msg = inject("msg")
29
- const url = inject("url")
30
-
31
-
32
- // · import components
33
- import componentLabelEditor from "LesliBabel/vue/components/form-label-editor.vue"
34
- import componentActions from "LesliBabel/vue/components/actions.vue"
35
-
36
-
37
- </script>
38
- <template>
39
- <section class="application-component">
40
- <lesli-header title="Relevant translations">
41
- <component-actions></component-actions>
42
- </lesli-header>
43
-
44
- <component-label-editor module="relevants">
45
- </component-label-editor>
46
- </section>
47
- </template>
@@ -1,78 +0,0 @@
1
- <script setup>
2
- /*
3
- Lesli
4
-
5
- Copyright (c) 2023, Lesli Technologies, S. A.
6
-
7
- This program is free software: you can redistribute it and/or modify
8
- it under the terms of the GNU General Public License as published by
9
- the Free Software Foundation, either version 3 of the License, or
10
- (at your option) any later version.
11
-
12
- This program is distributed in the hope that it will be useful,
13
- but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
- GNU General Public License for more details.
16
-
17
- You should have received a copy of the GNU General Public License
18
- along with this program. If not, see http://www.gnu.org/licenses/.
19
-
20
- Lesli · Ruby on Rails SaaS Development Framework.
21
-
22
- Made with ♥ by LesliTech
23
- Building a better future, one line of code at a time.
24
-
25
- @contact hello@lesli.tech
26
- @website https://www.lesli.tech
27
- @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
-
29
- // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
- // ·
31
- */
32
-
33
-
34
- // · import vue tools
35
- import { ref, reactive, onMounted, watch, computed, inject } from "vue"
36
- import { useRouter, useRoute } from 'vue-router'
37
-
38
-
39
- // · initialize/inject plugins
40
- const router = useRouter()
41
- const route = useRoute()
42
- const msg = inject("msg")
43
- const url = inject("url")
44
-
45
-
46
- // · import components
47
- import componentLabelEditor from "LesliBabel/vue/components/form-label-editor.vue"
48
- import componentActions from "LesliBabel/vue/components/actions.vue"
49
-
50
-
51
- // · import lesli stores
52
- import { useStrings } from "LesliBabel/vue/stores/strings"
53
- import { useTranslations } from "LesliBabel/vue/stores/translations"
54
-
55
-
56
- // · implement stores
57
- const storeStrings = useStrings()
58
- const storeTranslations = useTranslations()
59
-
60
-
61
- // ·
62
- function search(string) {
63
- router.push({ path: "translations", query: { search: string }})
64
- }
65
- </script>
66
- <template>
67
- <lesli-application-container>
68
- <lesli-header title="Translations">
69
- <component-actions></component-actions>
70
- </lesli-header>
71
-
72
- <lesli-toolbar :initial-value="storeStrings.search" @search="search">
73
- </lesli-toolbar>
74
-
75
- <component-label-editor>
76
- </component-label-editor>
77
- </lesli-application-container>
78
- </template>
@@ -1,29 +0,0 @@
1
- <script setup>
2
-
3
- // · import lesli stores
4
- import { useTranslations } from "LesliBabel/vue/stores/translations"
5
-
6
-
7
- // · implement stores
8
- const storeTranslations = useTranslations()
9
-
10
- </script>
11
- <template>
12
- <lesli-button icon="rocket_launch" @click="storeTranslations.postDeploy()">
13
- deploy
14
- </lesli-button>
15
- <!--
16
- <lesli-button icon="download">
17
- update
18
- </lesli-button>
19
- <lesli-button icon="sync" @click="storeTranslations.postSync()">
20
- sync
21
- </lesli-button>
22
- <lesli-button icon="code">
23
- javascript
24
- </lesli-button>
25
- <lesli-button icon="code">
26
- flutter
27
- </lesli-button>
28
- -->
29
- </template>
@@ -1,437 +0,0 @@
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/vue/stores/strings"
28
- import { useTranslations } from "LesliBabel/vue/stores/translations"
29
- import { useServiceTranslator } from "Lesli/vue/shared/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.status = 2
165
- storeStrings.putString(record)
166
- }
167
-
168
-
169
- // switch a label as help needed
170
- function askForTranslation(record) {
171
- record.status = 3
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
- return
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
- <!-- Table custom header, renders a language selector -->
294
- <template :[languageHead(language)]="{ column }">
295
- <lesli-select
296
- icon="public"
297
- v-model="language"
298
- :options="storeTranslations.locales">
299
- </lesli-select>
300
- </template>
301
-
302
- <!-- Print the label string with a button to easely copy to clipboard -->
303
- <template #label="{ record }">
304
- <button
305
- class="button is-white p-0"
306
- @click.stop="copyToClipboard($event, record.label)"
307
- @contextmenu.capture.prevent="copyToClipboard($event, record.path)">
308
- <span class="icon has-text-grey">
309
- <span class="material-icons">
310
- content_copy
311
- </span>
312
- </span>
313
- <span :class="{
314
- 'has-text-info': record.status == 2,
315
- 'has-text-warning': record.status == 3
316
- }">
317
- {{ record.label }}
318
- </span>
319
- </button>
320
- </template>
321
-
322
- <!-- Print a input to edit the translation for the current locale -->
323
- <template #[language]="{ value, record }">
324
- <div class="is-flex is-align-items-center">
325
- <input
326
- type="text"
327
- class="input"
328
- placeholder="Add translations..."
329
- @input="storeStrings.updateString(record, language)"
330
- v-model="record[language]"
331
- />
332
- <span class="icon mx-1" v-if="record.need_help">
333
- <span class="material-icons has-text-info">
334
- help_outline
335
- </span>
336
- </span>
337
- <span class="icon mx-1" v-if="record.need_translation">
338
- <span class="material-icons has-text-warning-dark">
339
- translate
340
- </span>
341
- </span>
342
- </div>
343
- </template>
344
-
345
- <template #detail="{ record }">
346
- <tr v-for="(locale_name, locale_code) in storeTranslations.options.locales_available">
347
- <td></td>
348
- <td class="has-text-right">{{ locale_name }}</td>
349
- <td class="is-flex is-align-items-center">
350
- <input
351
- type="text"
352
- class="input"
353
- placeholder="Add translations..."
354
- v-model="record[locale_code]"
355
- @input="storeStrings.updateString(record, locale_code)"
356
- />
357
- <lesli-button
358
- icon-only small danger icon="clear"
359
- v-if="record[`translated_${locale_code}`] == true"
360
- @click="clearStringTranslation(record, locale_code)">
361
- </lesli-button>
362
- &nbsp;
363
- <lesli-button
364
- icon-only small icon="save"
365
- v-if="record[`translated_${locale_code}`] == true"
366
- @click="storeStrings.putString(record)">
367
- </lesli-button>
368
- &nbsp;
369
- <lesli-button
370
- icon-only small icon="translate"
371
- v-if="!!record['en']"
372
- @click="suggestTranslation(record, locale_code)">
373
- </lesli-button>
374
- </td>
375
- </tr>
376
- <tr>
377
- <td></td>
378
- <td class="has-text-right">Context</td>
379
- <td>
380
- <input
381
- type="text"
382
- class="input"
383
- placeholder="Add translation context..."
384
- v-model="record.context"
385
- @input="storeStrings.updateString(record)"
386
- />
387
- </td>
388
- </tr>
389
- <tr>
390
- <td></td>
391
- <td class="has-text-right">Full path</td>
392
- <td>
393
- <button
394
- class="button is-primary is-inverted"
395
- @click.stop="copyToClipboard($event, record.path)">
396
- <span class="icon has-text-grey">
397
- <span class="material-icons">
398
- content_copy
399
- </span>
400
- </span>
401
- <span>
402
- {{ record.path }}
403
- </span>
404
- </button>
405
- </td>
406
- </tr>
407
- <tr>
408
- <td colspan="100%">
409
- <div class="buttons is-justify-content-center">
410
- <button
411
- class="button is-primary is-small"
412
- @click="copyToClipboard($event, getLabelLink(record.id))">
413
- <span class="icon is-small">
414
- <span class="material-icons">link</span>
415
- </span>
416
- <span>Copy link</span>
417
- </button>
418
-
419
- <lesli-button
420
- small info solid icon="help_outline"
421
- :disabled="record.status == 2"
422
- @click="askForHelp(record)">
423
- Need help
424
- </lesli-button>
425
-
426
- <lesli-button
427
- small warning solid icon="translate"
428
- :disabled="record.status == 3"
429
- @click="askForTranslation(record)">
430
- Need translation
431
- </lesli-button>
432
- </div>
433
- </td>
434
- </tr>
435
- </template>
436
- </lesli-table>
437
- </template>