import { forkJoin , Observable , of } from 'rxjs' ; import { isNullOrUndefined } from '../common/util' ; import { catchError , map } from 'rxjs/operators' ; import { FILETYPE_XTB , FORMAT_XMB , IICUMessage , IICUMessageTranslation , ITranslationMessagesFile , ITransUnit , TranslationMessagesFileFactory } from '@ngx-i18nsupport/ngx-i18nsupport-lib' ; import { TranslationUnit } from './translation-unit' ; import { AutoTranslateServiceAPI } from './auto-translate-service-api' ; import { AutoTranslateSummaryReport } from './auto-translate-summary-report' ; import { AutoTranslateResult } from './auto-translate-result' ; import { IFileDescription } from '../file-accessors/common/i-file-description' ; import { DownloadUploadFileDescription } from '../file-accessors/download-upload/download-upload-file-description' ; import { IFile } from '../file-accessors/common/i-file' ; import { SerializationService } from './serialization.service' ; import { GenericFile } from '../file-accessors/common/generic-file' ; /**
 * A single xlf or xmb file ready for work.
 * This is a wrapper around ITranslationMessagesFile.
 * It can read from uploaded files and adds errorhandling.
 * Created by roobm on 22.03.2017.
 */ interface ISerializedTranslationFileV2 { version : string ; file : string ; editedContent : string ; master ? : string ; explicitSourceLanguage : string ; } interface ISerializedTranslationFile { name : string ; size : number ; fileContent : string ; editedContent : string ; masterContent : string ; masterName : string ; explicitSourceLanguage : string ; } export class TranslationFile { private _file : IFile ; private _error : string = null ; private _master ? : IFile ; private _translationFile : ITranslationMessagesFile ; private _explicitSourceLanguage : string ; /**
     * all TransUnits read from file.
     */ private _allTransUnits : TranslationUnit [ ] ; param loadedFile param loadedMasterXmbFile static fromFile ( loadedFile , loadedMasterXmbFile ? ) { const newInstance = new TranslationFile ( ) ; newInstance . _file = loadedFile ; if ( loadedFile . content ) { try { let optionalMaster = null ; if ( loadedMasterXmbFile && loadedMasterXmbFile . content ) { optionalMaster = { path : loadedMasterXmbFile . description . name , xmlContent : loadedMasterXmbFile . content , encoding : null } ; newInstance . _master = loadedMasterXmbFile ; } newInstance . _translationFile = TranslationMessagesFileFactory . fromUnknownFormatFileContent ( loadedFile . content , loadedFile . description . name , 'utf-8' , optionalMaster ) ; if ( newInstance . _translationFile . i18nFormat ( ) === FORMAT_XMB ) { newInstance . _error = 'xmb files cannot be translated, use xtb instead' ; } newInstance . readTransUnits ( ) ; } catch ( err ) { newInstance . _error = err . toString ( ) ; } } return newInstance ; } param serializationService param serializationString return static deserialize ( serializationService , serializationString ) { const deserializedObject = < ISerializedTranslationFile > JSON . parse ( serializationString ) ; return TranslationFile . fromDeserializedObject ( serializationService , deserializedObject ) ; } static fromDeserializedObject ( serializationService , deserializedJsonObject ) { let deserializedObject ; if ( deserializedJsonObject . version ) { deserializedObject = deserializedJsonObject as ISerializedTranslationFileV2 ; } else { const v1Object = deserializedJsonObject as ISerializedTranslationFile ; deserializedObject = { version : '1' , file : new GenericFile ( DownloadUploadFileDescription . deserialize ( serializationService , null ) , v1Object . name , v1Object . size , v1Object . fileContent ) . serialize ( serializationService ) , editedContent : v1Object . editedContent , explicitSourceLanguage : v1Object . explicitSourceLanguage } ; if ( v1Object . masterContent ) { deserializedObject . master = new GenericFile ( DownloadUploadFileDescription . deserialize ( serializationService , null ) , v1Object . masterName , 0 , v1Object . masterContent ) . serialize ( serializationService ) ; } } const newInstance = new TranslationFile ( ) ; newInstance . _file = serializationService . deserializeIFile ( deserializedObject . file ) ; newInstance . _explicitSourceLanguage = deserializedObject . explicitSourceLanguage ; try { const encoding = null ; let optionalMaster = null ; if ( deserializedObject . master ) { newInstance . _master = serializationService . deserializeIFile ( deserializedObject . master ) ; optionalMaster = { xmlContent : newInstance . _master . content , path : newInstance . _master . description . name , encoding : encoding } ; } newInstance . _translationFile = TranslationMessagesFileFactory . fromUnknownFormatFileContent ( deserializedObject . editedContent , newInstance . _file . description . name , encoding , optionalMaster ) ; newInstance . readTransUnits ( ) ; } catch ( err ) { newInstance . _error = err . toString ( ) ; } return newInstance ; } constructor ( ) { this . _allTransUnits = [ ] ; } private readTransUnits ( ) { this . _allTransUnits = [ ] ; if ( this . _translationFile ) { this . _translationFile . forEachTransUnit ( ( tu ) => { this . _allTransUnits . push ( new TranslationUnit ( this , tu ) ) ; } ) ; } } get name ( ) : string { return ( this . _file && this . _file . description ) ? this . _file . description . name : '' ; } return get masterName ( ) : string | null { return ( this . _master ) ? this . _master . description . name : null ; } get size ( ) : number { return this . _file . size ; } get numberOfTransUnits ( ) : number { return this . _allTransUnits . length ; } get numberOfUntranslatedTransUnits ( ) : number { return ( this . _translationFile ) ? this . _translationFile . numberOfUntranslatedTransUnits ( ) : 0 ; } public fileDescription ( ) { return this . _file . description ; } public editedFile ( ) { const content = this . editedContent ( ) ; return this . _file . copyWithNewContent ( content ) ; } return public fileType ( ) { if ( this . _translationFile ) { return this . _translationFile . fileType ( ) ; } else { if ( this . name && this . name . endsWith ( 'xtb' ) ) { return FILETYPE_XTB ; } else { return null ; } } } return public sourceLanguageFromFile ( ) { return this . _translationFile ? this . _translationFile . sourceLanguage ( ) : 'unknown' ; } return public sourceLanguage ( ) { if ( this . _translationFile ) { const srcLang = this . _translationFile . sourceLanguage ( ) ; if ( isNullOrUndefined ( srcLang ) ) { return this . _explicitSourceLanguage ? this . _explicitSourceLanguage : '' ; } else { return srcLang ; } } else { return '' ; } } param srcLang public setSourceLanguage ( srcLang ) { this . _explicitSourceLanguage = srcLang ; } public targetLanguage ( ) { return this . _translationFile ? this . _translationFile . targetLanguage ( ) : '' ; } public percentageUntranslated ( ) { if ( this . numberOfTransUnits === 0 ) { return 100 ; } return 100 * this . numberOfUntranslatedTransUnits / this . numberOfTransUnits ; } public percentageTranslated ( ) { return 100 - this . percentageUntranslated ( ) ; } public hasErrors ( ) { return ! isNullOrUndefined ( this . _error ) ; } public canTranslate ( ) { return ! this . hasErrors ( ) && this . numberOfTransUnits > 0 ; } get error ( ) : string { return this . _error ; } return public warnings ( ) { return this . _translationFile ? this . _translationFile . warnings ( ) : [ ] ; } return public isDirty ( ) { return this . _translationFile && this . _file . content !== this . editedContent ( ) ; } /**
     * return content with all changes.
     */ public editedContent ( ) { if ( this . _translationFile ) { return this . _translationFile . editedContent ( ) ; } else { this . _error = 'cannot save, no valid file' ; } } /**
     * Mark file as "exported".
     * This means, that the file was downloaded.
     * So the new file content is the edited one.
     */ public markExported ( ) { this . _file . content = this . editedContent ( ) ; } return public allTransUnits ( ) { return this . _allTransUnits ; } /**
     * Return a string representation of translation file content.
     * This will be stored in BackendService.
     */ public serialize ( serializationService ) { const serializedObject = { version : '2' , file : this . _file . serialize ( serializationService ) , editedContent : this . editedContent ( ) , master : ( this . _master ) ? this . _master . serialize ( serializationService ) : null , explicitSourceLanguage : this . _explicitSourceLanguage } ; return JSON . stringify ( serializedObject ) ; } param autoTranslateService return public autoTranslateUsingService ( autoTranslateService ) { return forkJoin ( [ this . doAutoTranslateNonICUMessages ( autoTranslateService ) , ... this . doAutoTranslateICUMessages ( autoTranslateService ) ] ) . pipe ( map ( ( summaries ) => { const summary = summaries [ 0 ] ; for ( let i = 1 ; i < summaries . length ; i ++ ) { summary . merge ( summaries [ i ] ) ; } return summary ; } ) ) ; } param autoTranslateService return private doAutoTranslateNonICUMessages ( autoTranslateService ) { const allUntranslated = this . allTransUnits ( ) . filter ( ( tu ) => ! tu . isTranslated ( ) ) ; const allTranslatable = allUntranslated . filter ( ( tu ) => ! tu . sourceContentNormalized ( ) . isICUMessage ( ) ) ; const allMessages = allTranslatable . map ( ( tu ) => { return tu . sourceContentNormalized ( ) . dislayText ( true ) ; } ) ; return autoTranslateService . translateMultipleStrings ( allMessages , this . sourceLanguage ( ) , this . targetLanguage ( ) ) . pipe ( map ( ( translations ) => { const summary = new AutoTranslateSummaryReport ( ) ; for ( let i = 0 ; i < translations . length ; i ++ ) { const tu = allTranslatable [ i ] ; const translationText = translations [ i ] ; const result = tu . autoTranslateNonICUUnit ( translationText ) ; summary . addSingleResult ( result ) ; } return summary ; } ) ) ; } private doAutoTranslateICUMessages ( autoTranslateService ) { const allUntranslated = this . allTransUnits ( ) . filter ( ( tu ) => ! tu . isTranslated ( ) ) ; const allTranslatableICU = allUntranslated . filter ( ( tu ) => ! isNullOrUndefined ( tu . sourceContentNormalized ( ) . getICUMessage ( ) ) ) ; return allTranslatableICU . map ( ( tu ) => { return this . doAutoTranslateICUMessage ( autoTranslateService , tu ) ; } ) ; } param autoTranslateService param tu return private doAutoTranslateICUMessage ( autoTranslateService , tu ) { const icuMessage = tu . sourceContentNormalized ( ) . getICUMessage ( ) ; const categories = icuMessage . getCategories ( ) ; if ( categories . find ( ( category ) => ! isNullOrUndefined ( category . getMessageNormalized ( ) . getICUMessage ( ) ) ) ) { const summary = new AutoTranslateSummaryReport ( ) ; summary . addSingleResult ( AutoTranslateResult . Ignored ( tu , 'nested icu message' ) ) ; return of ( summary ) ; } const allMessages = categories . map ( ( category ) => category . getMessageNormalized ( ) . asDisplayString ( ) ) ; return autoTranslateService . translateMultipleStrings ( allMessages , this . sourceLanguage ( ) , this . targetLanguage ( ) ) . pipe ( map ( ( translations ) => { const summary = new AutoTranslateSummaryReport ( ) ; const icuTranslation = { } ; for ( let i = 0 ; i < translations . length ; i ++ ) { icuTranslation [ categories [ i ] . getCategory ( ) ] = translations [ i ] ; } const result = tu . autoTranslateICUUnit ( icuTranslation ) ; summary . addSingleResult ( result ) ; return summary ; } ) , catchError ( ( err ) => { const failSummary = new AutoTranslateSummaryReport ( ) ; failSummary . addSingleResult ( AutoTranslateResult . Failed ( tu , err . message ) ) ; return of ( failSummary ) ; } ) ) ; } }