我有一个复杂的javascript对象,例如:
{
"@context": "http://schema.org",
"@type": "Celebrity",
"name": "Julius Caesar",
"description": "translate:julius.amazing.bio",
"address": {
"@type": "PostalAddress",
"addressLocality": "Roma",
"postalCode": "9999",
"streetAddress": "Coliseum Street 1",
"addressCountry": "IT",
"description": "translate:coliseum.amazing.bio"
}
}
物体的深度几乎是无限的。现在必须翻译一些字段。在上面的示例中,它将是name
和address.description
。
我的翻译功能有以下签名:
get(key: string): Observable<string>;
这里的内容是它返回一个Observable。目标是翻译以translate:
开头的字段,并返回具有完全相同结构的对象。
我能想出的解决方案是forkJoin
所有已翻译的字段,并在完成后更新对象。
const obj = {
'@context': 'http://schema.org',
'@type': 'Celebrity',
'name': 'Julius Caesar',
'description': 'translate:julius.amazing.bio',
'address': {
'@type': 'PostalAddress',
'addressLocality': 'Roma',
'postalCode': '9999',
'streetAddress': 'Coliseum Street 1',
'addressCountry': 'IT',
'description': 'translate:coliseum.amazing.bio'
}
};
const cesar$ = this.translate.get('julius.amazing.bio');
const coliseum$ = this.translate.get('coliseum.amazing.bio');
forkJoin([cesar$, coliseum$]).subscribe(res => {
obj.description = res[0];
obj.address.description = res[1];
});
它不雅,不灵活,代码需要为我拥有的每个对象重写。关于如何以优雅和可重复使用的方式(任何对象深度)完成此任务的任何建议,允许我翻译以translate:
开头的任何字段?
答案 0 :(得分:3)
您可以递归遍历整个对象并转换需要翻译的属性。然后,您需要收集数组中的所有可观察对象,以便能够使用forkJoin
在所有翻译完成时具有可观察性。
我的快速刺杀,可能会有所改善:
import { delay, map} from 'rxjs/operators';
import { forkJoin } from 'rxjs/observable/forkJoin';
import { of } from 'rxjs/observable/of';
import { Observable } from 'rxjs/Observable';
const obj = {
'@context': 'http://schema.org',
'@type': 'Celebrity',
'name': 'Julius Caesar',
'description': 'translate:julius.amazing.bio',
'address': {
'@type': 'PostalAddress',
'addressLocality': 'Roma',
'postalCode': '9999',
'streetAddress': 'Coliseum Street 1',
'addressCountry': 'IT',
'description': 'translate:coliseum.amazing.bio'
}
};
let translate = {
get(value: string) {
return of("transalted " + value).pipe(delay(1000));
}
}
const translatePrefix = "translate:"
function translateAll<T>(obj:T) {
let obs : Observable<void>[] = []
for(let [key, value] of Object.entries(obj)) {
if(typeof value === "object") {
let o = translateAll(value).pipe(map(v=> { obj[key] = v }))
obs.push(o);
}
else if(typeof value === "string") {
if(!value.startsWith(translatePrefix)) continue;
var translationKey = value.substr(translatePrefix.length);
let o = translate.get(translationKey).pipe(map(v=> { obj[key] = v }));
obs.push(o);
}
}
return forkJoin(obs).pipe(map(v=> obj));
}
translateAll(obj).subscribe(v=>
{
console.log(v)
})
答案 1 :(得分:1)
正如@estus评论的那样,你可以递归遍历源对象来收集所有要翻译的字符串,然后翻译所有字符串,如下所示:
const translatePrefix = 'translate:';
// to return array of pairs:
// [string, function to apply result on object]
function getFields(obj) {
const fields = [];
Object.keys(obj).forEach(key => {
const val = obj[key];
if (typeof val === 'string' && val.startsWith(translatePrefix)) {
fields.push([
val.substr(translatePrefix.length),
(translation, resultObj) => {resultObj[key] = translation;},
]);
} else if (typeof val === 'object') {
fields.push(... getFields(val).map(([toTranslate, apply]) => [
toTranslate,
(translation, resultObj) => {apply(translation, resultObj[key]);},
]));
}
});
return fields;
}
function translateObj(obj) {
return forkJoin(
getFields(obj).map(
([str, apply]) => translate.get(str)
.pipe(map(translation => apply.bind(translation)))
)
).pipe(
map(translationUpdates => {
// clone obj to not modify source object (quick and dirty way)
const res = JSON.parse(JSON.stringify(obj));
// apply translations to it
translationUpdates.forEach(apply => apply(res));
return res;
})
);
}