2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
2014-09-09 19:26:11 -04:00
using System.Globalization ;
using System.Linq ;
2014-03-14 14:13:41 -04:00
using System.Text ;
using System.IO ;
using AutomationTool ;
using UnrealBuildTool ;
2014-09-09 19:26:11 -04:00
using OneSky ;
using EpicGames.OneSkyLocalization.Config ;
2014-03-14 14:13:41 -04:00
class Localise : BuildCommand
{
2015-09-24 12:25:10 -04:00
struct ProjectInfo
{
/** The name of this project */
public string ProjectName ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
/** Path to this projects localization config file (relative to the root working directory for the commandlet) */
public string LocalizationConfigFile ;
/** The destination path for the files containing the gathered data for this project (extracted from its config file - relative to the root working directory for the commandlet) */
public string DestinationPath ;
/** The name to use for this projects manifest file (extracted from its config file) */
public string ManifestName ;
/** The name to use for this projects archive file (extracted from its config file) */
public string ArchiveName ;
/** The name to use for this projects portable object file (extracted from its config file) */
public string PortableObjectName ;
/** The native culture for this project (extracted from its config file) */
public string NativeCulture ;
/** The cultures to generate for this project (extracted from its config file) */
public List < string > CulturesToGenerate ;
} ;
2014-09-09 19:26:11 -04:00
2015-04-03 13:53:02 -04:00
private int OneSkyDownloadedPOChangeList = 0 ;
2014-08-18 13:29:39 -04:00
public override void ExecuteBuild ( )
{
var EditorExe = CombinePaths ( CmdEnv . LocalRoot , @"Engine/Binaries/Win64/UE4Editor-Cmd.exe" ) ;
2014-03-14 14:13:41 -04:00
2015-09-24 12:25:10 -04:00
// todo: All of this should be passed in via the command line to allow this script to be generic
var UEProjectDirectory = "Engine" ;
var OneSkyConfigName = "OneSkyConfig_EpicGames" ;
var OneSkyProjectGroupName = "Unreal Engine" ;
var OneSkyProjectNames = new string [ ] {
"Engine" ,
"Editor" ,
"EditorTutorials" ,
"PropertyNames" ,
"ToolTips" ,
"Category" ,
"Keywords" ,
} ;
var RootWorkingDirectory = CombinePaths ( CmdEnv . LocalRoot , UEProjectDirectory ) ;
// Generate the info we need to gather for each project
var ProjectInfos = new List < ProjectInfo > ( ) ;
foreach ( var ProjectName in OneSkyProjectNames )
{
ProjectInfos . Add ( GenerateProjectInfo ( RootWorkingDirectory , ProjectName ) ) ;
}
OneSkyConfigData OneSkyConfig = OneSkyConfigHelper . Find ( OneSkyConfigName ) ;
var OneSkyService = new OneSkyService ( OneSkyConfig . ApiKey , OneSkyConfig . ApiSecret ) ;
var OneSkyProjectGroup = GetOneSkyProjectGroup ( OneSkyService , OneSkyProjectGroupName ) ;
2014-09-09 19:26:11 -04:00
2015-04-03 13:53:02 -04:00
// Create changelist for backed up POs from OneSky.
if ( P4Enabled )
{
OneSkyDownloadedPOChangeList = P4 . CreateChange ( P4Env . Client , "OneSky downloaded PO backup." ) ;
}
2014-09-09 19:26:11 -04:00
// Export all text from OneSky
2015-09-24 12:25:10 -04:00
foreach ( var ProjectInfo in ProjectInfos )
{
ExportOneSkyProjectToDirectory ( RootWorkingDirectory , OneSkyService , OneSkyProjectGroup , ProjectInfo ) ;
}
2014-09-09 19:26:11 -04:00
2015-04-03 13:53:02 -04:00
// Submit changelist for backed up POs from OneSky.
if ( P4Enabled )
{
int SubmittedChangeList ;
P4 . Submit ( OneSkyDownloadedPOChangeList , out SubmittedChangeList ) ;
}
2014-08-18 13:29:39 -04:00
// Setup editor arguments for SCC.
string EditorArguments = String . Empty ;
if ( P4Enabled )
{
2014-04-02 18:09:23 -04:00
EditorArguments = String . Format ( "-SCCProvider={0} -P4Port={1} -P4User={2} -P4Client={3} -P4Passwd={4}" , "Perforce" , P4Env . P4Port , P4Env . User , P4Env . Client , P4 . GetAuthenticationToken ( ) ) ;
2014-08-18 13:29:39 -04:00
}
else
{
EditorArguments = String . Format ( "-SCCProvider={0}" , "None" ) ;
}
2014-03-14 14:13:41 -04:00
2014-08-18 13:29:39 -04:00
// Setup commandlet arguments for SCC.
string CommandletSCCArguments = String . Empty ;
2015-09-24 12:25:10 -04:00
if ( P4Enabled ) { CommandletSCCArguments + = ( String . IsNullOrEmpty ( CommandletSCCArguments ) ? "" : " " ) + "-EnableSCC" ; }
if ( ! AllowSubmit ) { CommandletSCCArguments + = ( String . IsNullOrEmpty ( CommandletSCCArguments ) ? "" : " " ) + "-DisableSCCSubmit" ; }
2014-03-14 14:13:41 -04:00
2015-09-24 12:25:10 -04:00
// Execute commandlet for each project.
foreach ( var ProjectInfo in ProjectInfos )
2014-08-18 13:29:39 -04:00
{
2015-09-24 12:25:10 -04:00
var CommandletArguments = String . Format ( "-config={0}" , ProjectInfo . LocalizationConfigFile ) + ( String . IsNullOrEmpty ( CommandletSCCArguments ) ? "" : " " + CommandletSCCArguments ) ;
2015-08-20 09:37:11 -04:00
Log ( "Localization for {0} {1}" , EditorArguments , CommandletArguments ) ;
2014-03-14 14:13:41 -04:00
2015-08-20 09:37:11 -04:00
Log ( "Running UE4Editor to generate localization data" ) ;
2014-03-14 14:13:41 -04:00
2014-08-18 13:29:39 -04:00
string Arguments = String . Format ( "-run=GatherText {0} {1}" , EditorArguments , CommandletArguments ) ;
var RunResult = Run ( EditorExe , Arguments ) ;
2014-03-14 14:13:41 -04:00
2014-08-18 13:29:39 -04:00
if ( RunResult . ExitCode ! = 0 )
{
throw new AutomationException ( "Error while executing localization commandlet '{0}'" , Arguments ) ;
}
}
2014-03-14 14:13:41 -04:00
2014-09-09 19:26:11 -04:00
// Upload all text to OneSky
2015-09-24 12:25:10 -04:00
foreach ( var ProjectInfo in ProjectInfos )
{
UploadProjectToOneSky ( RootWorkingDirectory , OneSkyService , OneSkyProjectGroup , ProjectInfo ) ;
}
2014-08-18 13:29:39 -04:00
}
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
private ProjectInfo GenerateProjectInfo ( string RootWorkingDirectory , string ProjectName )
{
var ProjectInfo = new ProjectInfo ( ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
ProjectInfo . ProjectName = ProjectName ;
ProjectInfo . LocalizationConfigFile = String . Format ( @"Config/Localization/{0}.ini" , ProjectName ) ;
var LocalizationConfig = new ConfigCacheIni ( new FileReference ( CombinePaths ( RootWorkingDirectory , ProjectInfo . LocalizationConfigFile ) ) ) ;
if ( ! LocalizationConfig . GetString ( "CommonSettings" , "DestinationPath" , out ProjectInfo . DestinationPath ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'DestinationPath', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
if ( ! LocalizationConfig . GetString ( "CommonSettings" , "ManifestName" , out ProjectInfo . ManifestName ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'ManifestName', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
if ( ! LocalizationConfig . GetString ( "CommonSettings" , "ArchiveName" , out ProjectInfo . ArchiveName ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'ArchiveName', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
if ( ! LocalizationConfig . GetString ( "CommonSettings" , "PortableObjectName" , out ProjectInfo . PortableObjectName ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'PortableObjectName', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
if ( ! LocalizationConfig . GetString ( "CommonSettings" , "NativeCulture" , out ProjectInfo . NativeCulture ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'NativeCulture', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
if ( ! LocalizationConfig . GetArray ( "CommonSettings" , "CulturesToGenerate" , out ProjectInfo . CulturesToGenerate ) )
{
throw new AutomationException ( "Failed to find a required config key! Section: 'CommonSettings', Key: 'CulturesToGenerate', File: '{0}'" , ProjectInfo . LocalizationConfigFile ) ;
}
return ProjectInfo ;
}
private static ProjectGroup GetOneSkyProjectGroup ( OneSkyService OneSkyService , string ProjectGroupName )
{
var OneSkyProjectGroup = OneSkyService . ProjectGroups . FirstOrDefault ( g = > g . Name = = ProjectGroupName ) ;
if ( OneSkyProjectGroup = = null )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
OneSkyProjectGroup = new ProjectGroup ( ProjectGroupName , "en" ) ;
OneSkyService . ProjectGroups . Add ( OneSkyProjectGroup ) ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
return OneSkyProjectGroup ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
private static OneSky . Project GetOneSkyProject ( OneSkyService OneSkyService , string GroupName , string ProjectName , string ProjectDescription = "" )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
var OneSkyProjectGroup = GetOneSkyProjectGroup ( OneSkyService , GroupName ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
OneSky . Project OneSkyProject = OneSkyProjectGroup . Projects . FirstOrDefault ( p = > p . Name = = ProjectName ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
if ( OneSkyProject = = null )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
ProjectType projectType = OneSkyService . ProjectTypes . First ( pt = > pt . Code = = "website" ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
OneSkyProject = new OneSky . Project ( ProjectName , ProjectDescription , projectType ) ;
OneSkyProjectGroup . Projects . Add ( OneSkyProject ) ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
return OneSkyProject ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
private void ExportOneSkyProjectToDirectory ( string RootWorkingDirectory , OneSkyService OneSkyService , ProjectGroup OneSkyProjectGroup , ProjectInfo ProjectInfo )
{
var OneSkyProject = GetOneSkyProject ( OneSkyService , OneSkyProjectGroup . Name , ProjectInfo . ProjectName ) ;
var OneSkyFile = OneSkyProject . UploadedFiles . FirstOrDefault ( f = > f . Filename = = ProjectInfo . PortableObjectName ) ;
//Export
if ( OneSkyFile ! = null )
{
var CulturesToExport = new List < string > ( ) ;
foreach ( var OneSkyCulture in OneSkyProject . EnabledCultures )
{
// Only export the OneSky cultures that we care about for this project
if ( ProjectInfo . CulturesToGenerate . Contains ( OneSkyCulture ) )
{
CulturesToExport . Add ( OneSkyCulture ) ;
}
}
ExportOneSkyFileToDirectory ( OneSkyFile , new DirectoryInfo ( CombinePaths ( RootWorkingDirectory , ProjectInfo . DestinationPath ) ) , CulturesToExport ) ;
}
}
private void ExportOneSkyFileToDirectory ( UploadedFile OneSkyFile , DirectoryInfo DestinationDirectory , IEnumerable < string > Cultures )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
foreach ( var Culture in Cultures )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
var CultureDirectory = new DirectoryInfo ( Path . Combine ( DestinationDirectory . FullName , Culture ) ) ;
if ( ! CultureDirectory . Exists )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
CultureDirectory . Create ( ) ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
using ( var MemoryStream = new MemoryStream ( ) )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
var ExportFile = new FileInfo ( Path . Combine ( CultureDirectory . FullName , OneSkyFile . Filename ) ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
var ExportTranslationState = OneSkyFile . ExportTranslation ( Culture , MemoryStream ) . Result ;
if ( ExportTranslationState = = UploadedFile . ExportTranslationState . Success )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
MemoryStream . Position = 0 ;
using ( Stream FileStream = File . OpenWrite ( ExportFile . FullName ) )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
MemoryStream . CopyTo ( FileStream ) ;
Console . WriteLine ( "[SUCCESS] Exporting: '{0}' ({1})" , ExportFile . FullName , Culture ) ;
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
FileInfo ExportFileCopy = new FileInfo ( Path . Combine ( ExportFile . DirectoryName , String . Format ( "{0}_FromOneSky{1}" , Path . GetFileNameWithoutExtension ( ExportFile . Name ) , ExportFile . Extension ) ) ) ;
File . Copy ( ExportFile . FullName , ExportFileCopy . FullName , true ) ;
// Add/check out backed up POs from OneSky.
if ( P4Enabled )
{
UE4Build . AddBuildProductsToChangelist ( OneSkyDownloadedPOChangeList , new List < string > ( ) { ExportFileCopy . FullName } ) ;
}
2014-09-09 19:26:11 -04:00
}
2015-09-24 12:25:10 -04:00
else if ( ExportTranslationState = = UploadedFile . ExportTranslationState . NoContent )
2014-09-09 19:26:11 -04:00
{
2015-09-24 12:25:10 -04:00
Console . WriteLine ( "[WARNING] Exporting: '{0}' ({1}) has no translations!" , ExportFile . FullName , Culture ) ;
2014-09-09 19:26:11 -04:00
}
else
{
2015-09-24 12:25:10 -04:00
Console . WriteLine ( "[FAILED] Exporting: '{0}' ({1})" , ExportFile . FullName , Culture ) ;
2014-09-09 19:26:11 -04:00
}
}
}
}
2015-09-24 12:25:10 -04:00
private void UploadProjectToOneSky ( string RootWorkingDirectory , OneSkyService OneSkyService , ProjectGroup OneSkyProjectGroup , ProjectInfo ProjectInfo )
{
var OneSkyProject = GetOneSkyProject ( OneSkyService , OneSkyProjectGroup . Name , ProjectInfo . ProjectName ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
// Upload the .po file for the native culture first
UploadFileToOneSky ( OneSkyProject , new FileInfo ( Path . Combine ( RootWorkingDirectory , ProjectInfo . DestinationPath , ProjectInfo . NativeCulture , ProjectInfo . PortableObjectName ) ) , ProjectInfo . NativeCulture ) ;
2014-09-09 19:26:11 -04:00
2015-09-24 12:25:10 -04:00
// Upload the remaining .po files for the other cultures
foreach ( var Culture in ProjectInfo . CulturesToGenerate )
{
// Skip native culture as we uploaded it above
if ( Culture ! = ProjectInfo . NativeCulture )
{
UploadFileToOneSky ( OneSkyProject , new FileInfo ( Path . Combine ( RootWorkingDirectory , ProjectInfo . DestinationPath , Culture , ProjectInfo . PortableObjectName ) ) , Culture ) ;
}
}
}
2015-02-23 15:12:45 -05:00
2015-09-24 12:25:10 -04:00
private void UploadFileToOneSky ( OneSky . Project OneSkyProject , FileInfo FileToUpload , string Culture )
{
var FileNameToUpload = FileToUpload . FullName ;
using ( var FileStream = File . OpenRead ( FileNameToUpload ) )
{
// Read the BOM
var UTF8BOM = new byte [ 3 ] ;
FileStream . Read ( UTF8BOM , 0 , 3 ) ;
2015-02-23 15:12:45 -05:00
2015-09-24 12:25:10 -04:00
// We want to ignore the utf8 BOM
if ( UTF8BOM [ 0 ] ! = 0xef | | UTF8BOM [ 1 ] ! = 0xbb | | UTF8BOM [ 2 ] ! = 0xbf )
{
FileStream . Position = 0 ;
}
2015-02-23 15:12:45 -05:00
2015-09-24 12:25:10 -04:00
Console . WriteLine ( "Uploading: '{0}' ({1})" , FileNameToUpload , Culture ) ;
// todo: need to use the branch suffix here so that OneSky will merge them together
var UploadedFile = OneSkyProject . Upload ( Path . GetFileName ( FileNameToUpload ) , FileStream , Culture ) . Result ;
if ( UploadedFile = = null )
{
Console . WriteLine ( "[FAILED] Uploading: '{0}' ({1})" , FileNameToUpload , Culture ) ;
}
else
{
Console . WriteLine ( "[SUCCESS] Uploading: '{0}' ({1})" , FileNameToUpload , Culture ) ;
}
}
}
2014-03-14 14:13:41 -04:00
}