@adminforth/i18n 2.1.2 → 2.3.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.
- package/build.log +2 -2
- package/custom/BulkActionButton.vue +8 -2
- package/custom/TranslationJobViewComponent.vue +75 -7
- package/dist/custom/BulkActionButton.vue +8 -2
- package/dist/custom/TranslationJobViewComponent.vue +75 -7
- package/dist/index.d.ts +4 -1
- package/dist/index.js +10 -4
- package/index.ts +11 -5
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -20,5 +20,5 @@ custom/package.json
|
|
|
20
20
|
custom/pnpm-lock.yaml
|
|
21
21
|
custom/tsconfig.json
|
|
22
22
|
|
|
23
|
-
sent
|
|
24
|
-
total size is
|
|
23
|
+
sent 47,968 bytes received 305 bytes 96,546.00 bytes/sec
|
|
24
|
+
total size is 46,800 speedup is 0.97
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<button class="flex items-center justify-center w-full">
|
|
24
24
|
<IconLanguageOutline class="text-gray-500 dark:text-gray-400 w-5 h-5" />
|
|
25
25
|
<div class="flex items-end justify-start gap-2 cursor-pointer">
|
|
26
|
-
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{ t('Translate filtered') }}</p>
|
|
26
|
+
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{props.checkboxes.length === 0 ? t('Translate filtered') : t('Translate selected ({count})', { count: props.checkboxes.length })}}</p>
|
|
27
27
|
<div class="flex items-center justify-center text-white bg-gradient-to-r h-[18px] from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 font-medium rounded-md text-sm px-1 text-center">
|
|
28
28
|
{{t('AI')}}
|
|
29
29
|
</div>
|
|
@@ -133,7 +133,13 @@
|
|
|
133
133
|
|
|
134
134
|
async function runTranslation() {
|
|
135
135
|
isLoading.value = true;
|
|
136
|
-
|
|
136
|
+
|
|
137
|
+
let listOfIds = [];
|
|
138
|
+
if (props.checkboxes.length === 0) {
|
|
139
|
+
listOfIds = await getListOfIds();
|
|
140
|
+
} else {
|
|
141
|
+
listOfIds = props.checkboxes;
|
|
142
|
+
}
|
|
137
143
|
try {
|
|
138
144
|
const res = await callAdminForthApi({
|
|
139
145
|
path: `/plugin/${props.meta.pluginInstanceId}/translate-selected-to-languages`,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<div class="grid grid-cols-3 gap-2">
|
|
14
14
|
<div
|
|
15
|
+
v-if="!isFetchingTasks"
|
|
15
16
|
class="bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border-gray-200 dark:border-gray-700 transition-all px-2 py-2 rounded-md border max-w-64 w-full flex items-center justify-between gap-2"
|
|
16
17
|
v-for="(task, index) in translationTasks"
|
|
17
18
|
:key="index"
|
|
@@ -30,25 +31,56 @@
|
|
|
30
31
|
/>
|
|
31
32
|
</div>
|
|
32
33
|
</div>
|
|
34
|
+
<div v-else class="col-span-3 flex items-center justify-center">
|
|
35
|
+
<Spinner class="w-10 h-10" />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div v-if="page > 0 || hasNextPage" class="flex items-center justify-center gap-3 mt-4">
|
|
39
|
+
<Button
|
|
40
|
+
variant="secondary"
|
|
41
|
+
:disabled="page === 0 || isFetchingTasks"
|
|
42
|
+
@click="loadPage(page - 1)"
|
|
43
|
+
>
|
|
44
|
+
<IconAngleLeftOutline class="w-4 h-4" />
|
|
45
|
+
</Button>
|
|
46
|
+
<span class="text-sm text-gray-600 dark:text-gray-300">
|
|
47
|
+
{{ t('Page {page}', { page: page + 1 }) }}
|
|
48
|
+
</span>
|
|
49
|
+
<Button
|
|
50
|
+
variant="secondary"
|
|
51
|
+
:disabled="!hasNextPage || isFetchingTasks"
|
|
52
|
+
@click="loadPage(page + 1)"
|
|
53
|
+
>
|
|
54
|
+
<IconAngleRightOutline class="w-4 h-4" />
|
|
55
|
+
</Button>
|
|
33
56
|
</div>
|
|
34
57
|
</template>
|
|
35
58
|
|
|
36
59
|
|
|
37
60
|
<script setup lang="ts">
|
|
38
61
|
import { useI18n } from 'vue-i18n'
|
|
39
|
-
import { onMounted, onUnmounted, ref } from 'vue';
|
|
62
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
40
63
|
import websocket from '@/websocket';
|
|
41
64
|
import { getCountryCodeFromLangCode } from './langCommon';
|
|
42
65
|
import { getCustomComponent } from '@/utils';
|
|
66
|
+
import { Button, Spinner } from '@/afcl';
|
|
67
|
+
import { IconAngleLeftOutline, IconAngleRightOutline } from '@iconify-prerendered/vue-flowbite';
|
|
43
68
|
|
|
44
69
|
const { t } = useI18n();
|
|
45
70
|
|
|
46
71
|
const translationTasks = ref<{state: Record<string, any>, status: string}[]>([]);
|
|
47
|
-
const
|
|
72
|
+
const isFetchingTasks = ref(false);
|
|
73
|
+
const page = ref(0);
|
|
74
|
+
const hasNextPage = ref(false);
|
|
75
|
+
const PAGE_SIZE = 24;
|
|
76
|
+
const MAX_RETRIES = 10; // Maximum number of retries for fetching tasks
|
|
77
|
+
const RETRY_DELAY_MS = 2000; // 2 seconds
|
|
78
|
+
|
|
79
|
+
const currentOffset = computed(() => page.value * PAGE_SIZE);
|
|
48
80
|
|
|
49
81
|
const props = defineProps<{
|
|
50
82
|
meta: any;
|
|
51
|
-
getJobTasks: (limit?: number, offset?: number) => Promise<
|
|
83
|
+
getJobTasks: (limit?: number, offset?: number, fieldsToReturn?: string[]) => Promise<
|
|
52
84
|
{state: Record<string, any>, status: string}[]>;
|
|
53
85
|
job: {
|
|
54
86
|
id: string;
|
|
@@ -64,15 +96,51 @@ const props = defineProps<{
|
|
|
64
96
|
};
|
|
65
97
|
}>();
|
|
66
98
|
|
|
99
|
+
async function loadPage(targetPage: number) {
|
|
100
|
+
if (targetPage < 0 || isFetchingTasks.value) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
isFetchingTasks.value = true;
|
|
104
|
+
const retries = targetPage === 0 ? MAX_RETRIES : 1;
|
|
105
|
+
try {
|
|
106
|
+
for (let retry = 0; retry < retries; retry++) {
|
|
107
|
+
try {
|
|
108
|
+
const res = await props.getJobTasks(PAGE_SIZE, targetPage * PAGE_SIZE, ['lang', 'error', 'taskName', 'failedToTranslate']);
|
|
109
|
+
if (res && res.length > 0) {
|
|
110
|
+
translationTasks.value = res;
|
|
111
|
+
page.value = targetPage;
|
|
112
|
+
hasNextPage.value = res.length === PAGE_SIZE;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('Failed to fetch translation tasks, retrying...', error);
|
|
117
|
+
}
|
|
118
|
+
if (retry < retries - 1) {
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// nothing came back: either there are no tasks at all, or we went past the last page
|
|
123
|
+
if (targetPage === 0) {
|
|
124
|
+
translationTasks.value = [];
|
|
125
|
+
page.value = 0;
|
|
126
|
+
}
|
|
127
|
+
hasNextPage.value = false;
|
|
128
|
+
} finally {
|
|
129
|
+
isFetchingTasks.value = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
67
133
|
onMounted(async () => {
|
|
68
|
-
|
|
134
|
+
await loadPage(0);
|
|
135
|
+
|
|
69
136
|
websocket.subscribe(`/background-jobs-task-update/${props.job.id}`, (data: { taskIndex: number, status?: string, state?: Record<string, any> }) => {
|
|
70
|
-
|
|
137
|
+
const indexOnPage = data.taskIndex - currentOffset.value;
|
|
138
|
+
if (indexOnPage >= 0 && indexOnPage < translationTasks.value.length) {
|
|
71
139
|
if (data.state) {
|
|
72
|
-
translationTasks.value[
|
|
140
|
+
translationTasks.value[indexOnPage].state = data.state;
|
|
73
141
|
}
|
|
74
142
|
if (data.status) {
|
|
75
|
-
translationTasks.value[
|
|
143
|
+
translationTasks.value[indexOnPage].status = data.status;
|
|
76
144
|
}
|
|
77
145
|
}
|
|
78
146
|
});
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<button class="flex items-center justify-center w-full">
|
|
24
24
|
<IconLanguageOutline class="text-gray-500 dark:text-gray-400 w-5 h-5" />
|
|
25
25
|
<div class="flex items-end justify-start gap-2 cursor-pointer">
|
|
26
|
-
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{ t('Translate filtered') }}</p>
|
|
26
|
+
<p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{props.checkboxes.length === 0 ? t('Translate filtered') : t('Translate selected ({count})', { count: props.checkboxes.length })}}</p>
|
|
27
27
|
<div class="flex items-center justify-center text-white bg-gradient-to-r h-[18px] from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 font-medium rounded-md text-sm px-1 text-center">
|
|
28
28
|
{{t('AI')}}
|
|
29
29
|
</div>
|
|
@@ -133,7 +133,13 @@
|
|
|
133
133
|
|
|
134
134
|
async function runTranslation() {
|
|
135
135
|
isLoading.value = true;
|
|
136
|
-
|
|
136
|
+
|
|
137
|
+
let listOfIds = [];
|
|
138
|
+
if (props.checkboxes.length === 0) {
|
|
139
|
+
listOfIds = await getListOfIds();
|
|
140
|
+
} else {
|
|
141
|
+
listOfIds = props.checkboxes;
|
|
142
|
+
}
|
|
137
143
|
try {
|
|
138
144
|
const res = await callAdminForthApi({
|
|
139
145
|
path: `/plugin/${props.meta.pluginInstanceId}/translate-selected-to-languages`,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<div class="grid grid-cols-3 gap-2">
|
|
14
14
|
<div
|
|
15
|
+
v-if="!isFetchingTasks"
|
|
15
16
|
class="bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border-gray-200 dark:border-gray-700 transition-all px-2 py-2 rounded-md border max-w-64 w-full flex items-center justify-between gap-2"
|
|
16
17
|
v-for="(task, index) in translationTasks"
|
|
17
18
|
:key="index"
|
|
@@ -30,25 +31,56 @@
|
|
|
30
31
|
/>
|
|
31
32
|
</div>
|
|
32
33
|
</div>
|
|
34
|
+
<div v-else class="col-span-3 flex items-center justify-center">
|
|
35
|
+
<Spinner class="w-10 h-10" />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div v-if="page > 0 || hasNextPage" class="flex items-center justify-center gap-3 mt-4">
|
|
39
|
+
<Button
|
|
40
|
+
variant="secondary"
|
|
41
|
+
:disabled="page === 0 || isFetchingTasks"
|
|
42
|
+
@click="loadPage(page - 1)"
|
|
43
|
+
>
|
|
44
|
+
<IconAngleLeftOutline class="w-4 h-4" />
|
|
45
|
+
</Button>
|
|
46
|
+
<span class="text-sm text-gray-600 dark:text-gray-300">
|
|
47
|
+
{{ t('Page {page}', { page: page + 1 }) }}
|
|
48
|
+
</span>
|
|
49
|
+
<Button
|
|
50
|
+
variant="secondary"
|
|
51
|
+
:disabled="!hasNextPage || isFetchingTasks"
|
|
52
|
+
@click="loadPage(page + 1)"
|
|
53
|
+
>
|
|
54
|
+
<IconAngleRightOutline class="w-4 h-4" />
|
|
55
|
+
</Button>
|
|
33
56
|
</div>
|
|
34
57
|
</template>
|
|
35
58
|
|
|
36
59
|
|
|
37
60
|
<script setup lang="ts">
|
|
38
61
|
import { useI18n } from 'vue-i18n'
|
|
39
|
-
import { onMounted, onUnmounted, ref } from 'vue';
|
|
62
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
40
63
|
import websocket from '@/websocket';
|
|
41
64
|
import { getCountryCodeFromLangCode } from './langCommon';
|
|
42
65
|
import { getCustomComponent } from '@/utils';
|
|
66
|
+
import { Button, Spinner } from '@/afcl';
|
|
67
|
+
import { IconAngleLeftOutline, IconAngleRightOutline } from '@iconify-prerendered/vue-flowbite';
|
|
43
68
|
|
|
44
69
|
const { t } = useI18n();
|
|
45
70
|
|
|
46
71
|
const translationTasks = ref<{state: Record<string, any>, status: string}[]>([]);
|
|
47
|
-
const
|
|
72
|
+
const isFetchingTasks = ref(false);
|
|
73
|
+
const page = ref(0);
|
|
74
|
+
const hasNextPage = ref(false);
|
|
75
|
+
const PAGE_SIZE = 24;
|
|
76
|
+
const MAX_RETRIES = 10; // Maximum number of retries for fetching tasks
|
|
77
|
+
const RETRY_DELAY_MS = 2000; // 2 seconds
|
|
78
|
+
|
|
79
|
+
const currentOffset = computed(() => page.value * PAGE_SIZE);
|
|
48
80
|
|
|
49
81
|
const props = defineProps<{
|
|
50
82
|
meta: any;
|
|
51
|
-
getJobTasks: (limit?: number, offset?: number) => Promise<
|
|
83
|
+
getJobTasks: (limit?: number, offset?: number, fieldsToReturn?: string[]) => Promise<
|
|
52
84
|
{state: Record<string, any>, status: string}[]>;
|
|
53
85
|
job: {
|
|
54
86
|
id: string;
|
|
@@ -64,15 +96,51 @@ const props = defineProps<{
|
|
|
64
96
|
};
|
|
65
97
|
}>();
|
|
66
98
|
|
|
99
|
+
async function loadPage(targetPage: number) {
|
|
100
|
+
if (targetPage < 0 || isFetchingTasks.value) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
isFetchingTasks.value = true;
|
|
104
|
+
const retries = targetPage === 0 ? MAX_RETRIES : 1;
|
|
105
|
+
try {
|
|
106
|
+
for (let retry = 0; retry < retries; retry++) {
|
|
107
|
+
try {
|
|
108
|
+
const res = await props.getJobTasks(PAGE_SIZE, targetPage * PAGE_SIZE, ['lang', 'error', 'taskName', 'failedToTranslate']);
|
|
109
|
+
if (res && res.length > 0) {
|
|
110
|
+
translationTasks.value = res;
|
|
111
|
+
page.value = targetPage;
|
|
112
|
+
hasNextPage.value = res.length === PAGE_SIZE;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('Failed to fetch translation tasks, retrying...', error);
|
|
117
|
+
}
|
|
118
|
+
if (retry < retries - 1) {
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// nothing came back: either there are no tasks at all, or we went past the last page
|
|
123
|
+
if (targetPage === 0) {
|
|
124
|
+
translationTasks.value = [];
|
|
125
|
+
page.value = 0;
|
|
126
|
+
}
|
|
127
|
+
hasNextPage.value = false;
|
|
128
|
+
} finally {
|
|
129
|
+
isFetchingTasks.value = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
67
133
|
onMounted(async () => {
|
|
68
|
-
|
|
134
|
+
await loadPage(0);
|
|
135
|
+
|
|
69
136
|
websocket.subscribe(`/background-jobs-task-update/${props.job.id}`, (data: { taskIndex: number, status?: string, state?: Record<string, any> }) => {
|
|
70
|
-
|
|
137
|
+
const indexOnPage = data.taskIndex - currentOffset.value;
|
|
138
|
+
if (indexOnPage >= 0 && indexOnPage < translationTasks.value.length) {
|
|
71
139
|
if (data.state) {
|
|
72
|
-
translationTasks.value[
|
|
140
|
+
translationTasks.value[indexOnPage].state = data.state;
|
|
73
141
|
}
|
|
74
142
|
if (data.status) {
|
|
75
|
-
translationTasks.value[
|
|
143
|
+
translationTasks.value[indexOnPage].status = data.status;
|
|
76
144
|
}
|
|
77
145
|
}
|
|
78
146
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -40,7 +40,10 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
40
40
|
strId: string;
|
|
41
41
|
enStr: string;
|
|
42
42
|
translatedStr: string;
|
|
43
|
-
}>, lang: string, failedToTranslate: IFailedTranslation[], needToTranslateByLang: Record<string, any>, jobId: string, promptCost: number): Promise<
|
|
43
|
+
}>, lang: string, failedToTranslate: IFailedTranslation[], needToTranslateByLang: Record<string, any>, jobId: string, promptCost: number): Promise<{
|
|
44
|
+
ok: boolean;
|
|
45
|
+
error?: string;
|
|
46
|
+
}>;
|
|
44
47
|
getTranslateToLangTasks(langIsoCode: SupportedLanguage, strings: {
|
|
45
48
|
id: string;
|
|
46
49
|
en_string: string;
|
package/dist/index.js
CHANGED
|
@@ -496,7 +496,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
496
496
|
});
|
|
497
497
|
process.env.HEAVY_DEBUG && console.log(`🪲🔪LLM resp >> ${prompt.length}, <<${resp.content.length} :\n\n`, JSON.stringify(resp));
|
|
498
498
|
if (resp.error) {
|
|
499
|
-
|
|
499
|
+
return { ok: false, error: resp.error };
|
|
500
500
|
}
|
|
501
501
|
let res;
|
|
502
502
|
try {
|
|
@@ -511,7 +511,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
511
511
|
failedReason: "Error in parsing LLM response"
|
|
512
512
|
});
|
|
513
513
|
});
|
|
514
|
-
return
|
|
514
|
+
return { ok: false, error: 'Error in parsing LLM response' };
|
|
515
515
|
}
|
|
516
516
|
const backgroundJobsPlugin = this.adminforth.getPluginByClassName('BackgroundJobsPlugin');
|
|
517
517
|
backgroundJobsPlugin.updateJobFieldsAtomically(jobId, () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -533,7 +533,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
533
533
|
failedReason: "Error in parsing LLM response JSON"
|
|
534
534
|
});
|
|
535
535
|
});
|
|
536
|
-
return
|
|
536
|
+
return { ok: false, error: 'Error in parsing LLM response JSON' };
|
|
537
537
|
}
|
|
538
538
|
for (const [schemaKey, translatedStr] of Object.entries(res)) {
|
|
539
539
|
const enStr = schemaKeyToEnString.get(schemaKey) || schemaKey;
|
|
@@ -577,6 +577,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
577
577
|
yield this.cache.clear(`${this.resourceConfig.resourceId}:${category}:${lang}`);
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
|
+
return { ok: true };
|
|
580
581
|
});
|
|
581
582
|
}
|
|
582
583
|
getTranslateToLangTasks(langIsoCode_1, strings_1) {
|
|
@@ -793,8 +794,9 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
793
794
|
//handler function
|
|
794
795
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ jobId, setTaskStateField, getTaskStateField }) {
|
|
795
796
|
const initialState = yield getTaskStateField();
|
|
797
|
+
let bunchResult;
|
|
796
798
|
if (initialState.prompt && initialState.strings && initialState.translations && initialState.updateStrings && initialState.lang && initialState.failedToTranslate && initialState.needToTranslateByLang) {
|
|
797
|
-
yield this.generateAndSaveBunch(initialState.prompt, initialState.strings, initialState.translations, initialState.updateStrings, initialState.lang, initialState.failedToTranslate, initialState.needToTranslateByLang, jobId, initialState.promptCost);
|
|
799
|
+
bunchResult = yield this.generateAndSaveBunch(initialState.prompt, initialState.strings, initialState.translations, initialState.updateStrings, initialState.lang, initialState.failedToTranslate, initialState.needToTranslateByLang, jobId, initialState.promptCost);
|
|
798
800
|
}
|
|
799
801
|
afLogger.debug(`Translation task for language ${initialState.lang} completed.`);
|
|
800
802
|
const stateToSave = {
|
|
@@ -804,6 +806,10 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
804
806
|
};
|
|
805
807
|
yield setTaskStateField(stateToSave);
|
|
806
808
|
this.adminforth.websocket.publish('/translation_progress', {});
|
|
809
|
+
if (bunchResult && !bunchResult.ok) {
|
|
810
|
+
afLogger.error(`Failed to translate bunch for language ${initialState.lang} in plugin ${this.constructor.name}: ${bunchResult.error}`);
|
|
811
|
+
throw new AiTranslateError(bunchResult.error);
|
|
812
|
+
}
|
|
807
813
|
if (initialState.failedToTranslate.length > 0) {
|
|
808
814
|
afLogger.error(`Failed to translate some strings for language ${initialState.lang} in plugin ${this.constructor.name}:, ${initialState.failedToTranslate}`);
|
|
809
815
|
throw new Error(`Failed to translate some strings for language ${initialState.lang}, check job details for more info`);
|
package/index.ts
CHANGED
|
@@ -540,7 +540,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
540
540
|
needToTranslateByLang: Record<string, any> = {},
|
|
541
541
|
jobId: string,
|
|
542
542
|
promptCost: number,
|
|
543
|
-
): Promise<
|
|
543
|
+
): Promise<{ ok: boolean, error?: string }>{
|
|
544
544
|
|
|
545
545
|
// return [];
|
|
546
546
|
const jsonSchemaProperties = {};
|
|
@@ -578,7 +578,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
578
578
|
process.env.HEAVY_DEBUG && console.log(`🪲🔪LLM resp >> ${prompt.length}, <<${resp.content.length} :\n\n`, JSON.stringify(resp));
|
|
579
579
|
|
|
580
580
|
if (resp.error) {
|
|
581
|
-
|
|
581
|
+
return { ok: false, error: resp.error };
|
|
582
582
|
}
|
|
583
583
|
|
|
584
584
|
let res;
|
|
@@ -593,7 +593,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
593
593
|
failedReason: "Error in parsing LLM response"
|
|
594
594
|
});
|
|
595
595
|
});
|
|
596
|
-
return
|
|
596
|
+
return { ok: false, error: 'Error in parsing LLM response' };
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
const backgroundJobsPlugin = this.adminforth.getPluginByClassName<any>('BackgroundJobsPlugin');
|
|
@@ -618,7 +618,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
618
618
|
failedReason: "Error in parsing LLM response JSON"
|
|
619
619
|
});
|
|
620
620
|
});
|
|
621
|
-
return
|
|
621
|
+
return { ok: false, error: 'Error in parsing LLM response JSON' };
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
|
|
@@ -681,6 +681,7 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
681
681
|
}
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
return { ok: true };
|
|
684
685
|
}
|
|
685
686
|
|
|
686
687
|
async getTranslateToLangTasks (
|
|
@@ -976,9 +977,10 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
976
977
|
needToTranslateByLang?: Record<string, any>,
|
|
977
978
|
promptCost?: number,
|
|
978
979
|
} = await getTaskStateField();
|
|
980
|
+
let bunchResult: { ok: boolean, error?: string } | undefined;
|
|
979
981
|
|
|
980
982
|
if ( initialState.prompt && initialState.strings && initialState.translations && initialState.updateStrings && initialState.lang && initialState.failedToTranslate && initialState.needToTranslateByLang) {
|
|
981
|
-
await this.generateAndSaveBunch(
|
|
983
|
+
bunchResult = await this.generateAndSaveBunch(
|
|
982
984
|
initialState.prompt,
|
|
983
985
|
initialState.strings,
|
|
984
986
|
initialState.translations,
|
|
@@ -1000,6 +1002,10 @@ export default class I18nPlugin extends AdminForthPlugin {
|
|
|
1000
1002
|
await setTaskStateField(stateToSave);
|
|
1001
1003
|
|
|
1002
1004
|
this.adminforth.websocket.publish('/translation_progress', {});
|
|
1005
|
+
if (bunchResult && !bunchResult.ok) {
|
|
1006
|
+
afLogger.error(`Failed to translate bunch for language ${initialState.lang} in plugin ${this.constructor.name}: ${bunchResult.error}`);
|
|
1007
|
+
throw new AiTranslateError(bunchResult.error);
|
|
1008
|
+
}
|
|
1003
1009
|
if (initialState.failedToTranslate.length > 0) {
|
|
1004
1010
|
afLogger.error(`Failed to translate some strings for language ${initialState.lang} in plugin ${this.constructor.name}:, ${initialState.failedToTranslate}`);
|
|
1005
1011
|
throw new Error(`Failed to translate some strings for language ${initialState.lang}, check job details for more info`);
|