Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mololab
GitHub Repository: mololab/json-translator
Path: blob/master/src/core/json_object.ts
235 views
1
import { translatedObject } from '..';
2
import { getKey, plaintranslate} from './translator';
3
import { TaskQueue } from 'cwait';
4
import { Promise as bluebirdPromise } from 'bluebird';
5
import { TranslationConfig } from '../modules/modules';
6
import { default_concurrency_limit } from '../utils/micro';
7
import {checkFile, getFile, saveFilePublic} from "./core";
8
import {flatten} from "../utils/json";
9
10
var queue = new TaskQueue(bluebirdPromise, default_concurrency_limit);
11
12
export async function objectTranslator(
13
TranslationConfig: TranslationConfig,
14
object: translatedObject,
15
from: string,
16
to: string[],
17
oldTranslations?: { [key: string]: any }
18
): Promise<translatedObject[]> {
19
queue.concurrency = TranslationConfig.concurrencyLimit;
20
21
if (object && from && to) {
22
let generalObject: translatedObject[] | null[] = [];
23
24
await Promise.all(
25
Object.keys(to).map(async function(index) {
26
const indexAsNum = Number(index);
27
28
// Start with exact copy of source structure
29
const resultObject = JSON.parse(JSON.stringify(object));
30
31
// Translate the source object and fill only the keys that exist in source
32
const translatedResult = await deepDiver(
33
TranslationConfig,
34
resultObject,
35
from,
36
to[indexAsNum],
37
oldTranslations ? oldTranslations[to[indexAsNum]] : undefined
38
);
39
40
generalObject[indexAsNum] = translatedResult;
41
})
42
);
43
44
return generalObject as translatedObject[];
45
} else {
46
throw new Error(
47
`Undefined values detected. Available ones: object: ${!!object}, from: ${!!from}, to: ${!!to}`
48
);
49
}
50
}
51
52
export async function deepDiver(
53
TranslationConfig: TranslationConfig,
54
object: translatedObject,
55
from: string,
56
to: string,
57
oldTranslation?: any
58
): Promise<translatedObject | null> {
59
var has = Object.prototype.hasOwnProperty.bind(object);
60
61
if (object === null) {
62
return null;
63
}
64
65
const CACHE_FILE_NAME = `./cache_${from}_${to}.json`;
66
let originalObject: any = JSON.parse(JSON.stringify(object));
67
var cacheObject: Record<string, any> = {};
68
69
// Load existing cache
70
if (TranslationConfig.cacheEnabled) {
71
if (!await checkFile(CACHE_FILE_NAME)) {
72
await saveFilePublic(CACHE_FILE_NAME, {});
73
}
74
const cacheDataFile = await getFile(CACHE_FILE_NAME);
75
cacheObject = JSON.parse(cacheDataFile);
76
}
77
78
// Process each key in the source object
79
await Promise.all(
80
Object.keys(object).map(async function(k) {
81
if (has(k)) {
82
switch (typeof object[k]) {
83
case 'object':
84
// Recursively process nested objects
85
await deepDiver(
86
TranslationConfig,
87
object[k],
88
from,
89
to,
90
oldTranslation && oldTranslation.data ? oldTranslation.data[k] : undefined
91
);
92
break;
93
case 'string':
94
// Check if we have this translation in old file first
95
if (oldTranslation && oldTranslation.data && oldTranslation.data[k] &&
96
oldTranslation.data[k] !== '' && oldTranslation.data[k] !== '--') {
97
// Use existing translation
98
object[k] = oldTranslation.data[k];
99
global.skipInCache = global.skipInCache + 1;
100
} else {
101
// Need to translate this string
102
global.totalTranslation = global.totalTranslation + 1;
103
104
return queue.add(async () => {
105
return await plaintranslate(
106
TranslationConfig,
107
object[k],
108
from,
109
to,
110
[],
111
cacheObject
112
)
113
.then(data => {
114
object[k] = data;
115
})
116
.catch(err => {
117
console.log('Translation error:', err);
118
});
119
});
120
}
121
break;
122
}
123
}
124
})
125
);
126
127
// Update cache with ONLY current translations (refresh cache to latest content)
128
if (TranslationConfig.cacheEnabled) {
129
// Clear old cache and rebuild with only current file's translations
130
const newCacheObject: Record<string, any> = {};
131
132
let originalStructure: Record<string, string> = flatten(originalObject);
133
let translatedStructure: Record<string, string> = flatten(object);
134
135
// Only add current file's translations to cache
136
Object.keys(originalStructure).forEach(key => {
137
let value = originalStructure[key];
138
let cacheKey = getKey(value, from, to);
139
newCacheObject[cacheKey] = translatedStructure[key];
140
});
141
142
// Save refreshed cache with only current content
143
await saveFilePublic(CACHE_FILE_NAME, newCacheObject);
144
}
145
146
return object;
147
}
148
149