import { TranslationMessagesFileFactory , ITranslationMessagesFile , ITransUnit } from 'ngx-i18nsupport-lib' ; import { isNullOrUndefined } from 'util' ; import { TranslationUnit } from './translation-unit' ; import { Observable } from 'rxjs/Observable' ; import { AsynchronousFileReaderResult } from './asynchronous-file-reader.service' ; import { FILETYPE_XTB , FORMAT_XMB , IICUMessage , IICUMessageTranslation , INormalizedMessage } from 'ngx-i18nsupport-lib/dist' ; import { AutoTranslateServiceAPI } from './auto-translate-service-api' ; import { NormalizedMessage } from './normalized-message' ; import { AutoTranslateSummaryReport } from './auto-translate-summary-report' ; import { AutoTranslateResult } from './auto-translate-result' ; /**
 * 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 ISerializedTranslationFile { name : string ; size : number ; fileContent : string ; editedContent : string ; masterContent : string ; masterName : string ; explicitSourceLanguage : string ; } export class TranslationFile { private _name : string ; private _size : number ; private _error : string = null ; private fileContent : string ; private masterContent : string ; private _masterName : string ; private _translationFile : ITranslationMessagesFile ; private _explicitSourceLanguage : string ; /**
     * all TransUnits read from file.
     */ private _allTransUnits : TranslationUnit [ ] ; static fromUploadedFile ( readingUploadedFile , readingMasterXmbFile ? ) { return Observable . combineLatest ( readingUploadedFile , readingMasterXmbFile ) . map ( ( contentArray ) => { const fileContent = contentArray [ 0 ] ; const newInstance = new TranslationFile ( ) ; newInstance . _name = fileContent . name ; newInstance . _size = fileContent . size ; if ( fileContent . content ) { const masterXmbContent = contentArray [ 1 ] ; try { newInstance . fileContent = fileContent . content ; let optionalMaster = null ; if ( masterXmbContent && masterXmbContent . content ) { optionalMaster = { path : masterXmbContent . name , xmlContent : masterXmbContent . content , encoding : null } ; newInstance . masterContent = masterXmbContent . content ; newInstance . _masterName = masterXmbContent . name ; } newInstance . _translationFile = TranslationMessagesFileFactory . fromUnknownFormatFileContent ( fileContent . content , fileContent . name , null , 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 serializationString return { TranslationFile } static deserialize ( serializationString ) { const deserializedObject = < ISerializedTranslationFile > JSON . parse ( serializationString ) ; return TranslationFile . fromDeserializedObject ( deserializedObject ) ; } static fromDeserializedObject ( deserializedObject ) { const newInstance = new TranslationFile ( ) ; newInstance . _name = deserializedObject . name ; newInstance . _size = deserializedObject . size ; newInstance . fileContent = deserializedObject . fileContent ; newInstance . _explicitSourceLanguage = deserializedObject . explicitSourceLanguage ; try { const encoding = null ; let optionalMaster = null ; if ( deserializedObject . masterContent ) { optionalMaster = { xmlContent : deserializedObject . masterContent , path : deserializedObject . masterName , encoding : encoding } ; newInstance . _masterName = deserializedObject . masterName ; } newInstance . _translationFile = TranslationMessagesFileFactory . fromUnknownFormatFileContent ( deserializedObject . editedContent , deserializedObject . 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 . _name ; } return { string } get masterName ( ) : string { return this . _masterName ; } get size ( ) : number { return this . _size ; } get numberOfTransUnits ( ) : number { return this . _allTransUnits . length ; } get numberOfUntranslatedTransUnits ( ) : number { return ( this . _translationFile ) ? this . _translationFile . numberOfUntranslatedTransUnits ( ) : 0 ; } return { null } public fileType ( ) { if ( this . _translationFile ) { return this . _translationFile . fileType ( ) ; } else { if ( this . _name && this . _name . endsWith ( 'xtb' ) ) { return FILETYPE_XTB ; } else { return null ; } } } return { string } public sourceLanguageFromFile ( ) { return this . _translationFile ? this . _translationFile . sourceLanguage ( ) : 'unknown' ; } return { any } 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 0 ; } 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 { string [ ] | Array } public warnings ( ) { return this . _translationFile ? this . _translationFile . warnings ( ) : [ ] ; } return { boolean } public isDirty ( ) { return this . _translationFile && this . fileContent !== 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 fileConted is the edited one.
     */ public markExported ( ) { this . fileContent = this . editedContent ( ) ; } return { TranslationUnit [ ] } public allTransUnits ( ) { return this . _allTransUnits ; } /**
     * Return a string represenation of translation file content.
     * This will be stored in BackendService.
     */ public serialize ( ) { const serializedObject = { name : this . name , size : this . size , fileContent : this . fileContent , editedContent : this . editedContent ( ) , masterContent : this . masterContent , masterName : this . _masterName , explicitSourceLanguage : this . _explicitSourceLanguage } ; return JSON . stringify ( serializedObject ) ; } param autoTranslateService return public autoTranslateUsingService ( autoTranslateService ) { return Observable . forkJoin ( [ this . doAutoTranslateNonICUMessages ( autoTranslateService ) , ... this . doAutoTranslateICUMessages ( autoTranslateService ) ] ) . 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 ( ) ) . 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 { Observable < AutoTranslateSummaryReport > } 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 Observable . of ( summary ) ; } const allMessages = categories . map ( ( category ) => category . getMessageNormalized ( ) . asDisplayString ( ) ) ; return autoTranslateService . translateMultipleStrings ( allMessages , this . sourceLanguage ( ) , this . targetLanguage ( ) ) . map ( ( translations ) => { const summary = new AutoTranslateSummaryReport ( ) ; const icuTranslation = { } ; for ( let i = 0 ; i < translations . length ; i ++ ) { const translationText = translations [ i ] ; icuTranslation [ categories [ i ] . getCategory ( ) ] = translationText ; } const result = tu . autoTranslateICUUnit ( icuTranslation ) ; summary . addSingleResult ( result ) ; return summary ; } ) . catch ( ( err ) => { const failSummary = new AutoTranslateSummaryReport ( ) ; failSummary . addSingleResult ( AutoTranslateResult . Failed ( tu , err . message ) ) ; return Observable . of ( failSummary ) ; } ) ; } }