1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include <com/sun/star/xml/sax/SAXException.hpp> 29 #include <l10ntools/vosapp.hxx> 30 31 #include <osl/file.hxx> 32 33 #include "export.hxx" 34 #include "layoutparse.hxx" 35 #include "helpmerge.hxx" 36 #include "xmlparse.hxx" 37 38 // Convert a rtl::OUString to a byte string. 39 #define OUSTRING_CSTR( str ) \ 40 rtl::OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() 41 42 #define STRING( str ) String( str, RTL_TEXTENCODING_UTF8 ) 43 #define BSTRING( str ) ByteString( str, RTL_TEXTENCODING_UTF8 ) 44 45 using ::rtl::OUString; 46 47 using namespace ::osl; 48 using namespace ::com::sun::star; 49 using namespace ::com::sun::star::uno; 50 51 52 class TranslateLayout : public Application 53 { 54 ByteString mGid1; 55 ByteString mLanguage; 56 ByteString mLocalize; 57 ByteString mOutput; 58 ByteString mProject; 59 ByteString mRoot; 60 bool mMergeMode; 61 std::vector< ByteString > mLanguages; 62 std::list< ByteString > mFiles; 63 64 public: 65 TranslateLayout(); 66 virtual ~TranslateLayout(); 67 ByteString GetCommandLineParam( int i ); 68 ByteString GetOptionArgument( int const i ); 69 void ExceptionalMain(); 70 void Main(); 71 void Merge(); 72 void MergeLanguage( ByteString const& language ); 73 void ParseCommandLine(); 74 void CreateSDF(); 75 76 using Application::GetCommandLineParam; 77 }; 78 79 static void usage() 80 { 81 fprintf( stderr, "Usage: tralay [OPTION]... XML-FILE\n" ); 82 fprintf( stderr, "\nOptions:\n" ); 83 fprintf( stderr, " -h,--help show this help\n" ); 84 fprintf( stderr, " -l,--language=LANG process this language\n" ); 85 fprintf( stderr, " -m,--merge=DATABASE.SDF translation database\n" ); 86 fprintf( stderr, "\nExamples:\n" ); 87 fprintf( stderr, " tralay -l en-US -o localize.sdf zoom.xml # Extract\n" ); 88 fprintf( stderr, " tralay -m localize.sdf -l de -l nl -o out zoom.xml # Merge/translate\n" ); 89 exit( 2 ); 90 } 91 92 static ByteString ConvertSystemPath( const ByteString& rPath ) 93 { 94 if( rPath.CompareTo( ".", 1 ) == 0 ) 95 { 96 OUString sPath( rPath.GetBuffer(), rPath.Len(), RTL_TEXTENCODING_UTF8 ); 97 98 ::rtl::OUString curDirPth, sURL; 99 osl_getProcessWorkingDir( &curDirPth.pData ); 100 101 ::osl::FileBase::getAbsoluteFileURL( curDirPth, sPath, sURL ); 102 ::osl::FileBase::getSystemPathFromFileURL( sURL, sPath ); 103 104 return ByteString( rtl::OUStringToOString( sPath, RTL_TEXTENCODING_UTF8 ) ); 105 } 106 else 107 { 108 return rPath; 109 } 110 } 111 112 ByteString TranslateLayout::GetCommandLineParam( int i ) 113 { 114 return ByteString( OUSTRING_CSTR( Application::GetCommandLineParam( sal::static_int_cast< sal_uInt16 >( i ) ) ) ); 115 } 116 117 ByteString TranslateLayout::GetOptionArgument( int const i ) 118 { 119 if ( i >= GetCommandLineParamCount() ) 120 usage(); 121 ByteString arg = GetCommandLineParam( i ); 122 if ( !arg.CompareTo( "-", 1 ) ) 123 { 124 fprintf( stderr, "Option needs an argument: %s, found: %s\n", 125 GetCommandLineParam( i - 1 ).GetBuffer(), 126 arg.GetBuffer() ); 127 usage(); 128 } 129 return arg; 130 } 131 132 void TranslateLayout::ParseCommandLine() 133 { 134 for ( int i = 0; i < GetCommandLineParamCount(); i++ ) 135 { 136 ByteString aParam = GetCommandLineParam( i ); 137 if ( aParam.Equals( "-h" ) || aParam.Equals( "--help" ) ) 138 usage(); 139 else if ( aParam.Equals( "-l" ) || aParam.Equals( "--language" ) ) 140 mLanguages.push_back ( GetOptionArgument( ++i ) ); 141 else if ( aParam.Equals( "-m" ) || aParam.Equals( "--merge" ) ) 142 { 143 mMergeMode = true; 144 mLocalize = GetOptionArgument( ++i ); 145 } 146 else if ( aParam.Equals( "-o" ) || aParam.Equals( "--output" ) ) 147 mOutput = ConvertSystemPath( GetOptionArgument( ++i ) ); 148 else if ( !aParam.CompareTo( "-", 1 ) ) 149 { 150 fprintf( stderr, "error: No such option: %s\n", aParam.GetBuffer() ); 151 usage(); 152 } 153 else 154 mFiles.push_back( ConvertSystemPath( aParam ) ); 155 } 156 if ( !mFiles.size() ) 157 { 158 fprintf( stderr, "error: No XML-FILE found\n" ); 159 usage(); 160 } 161 } 162 163 static XMLAttribute* 164 findAttribute( XMLAttributeList* lst, String const& name ) 165 { 166 for ( sal_uLong i = 0; i < lst->Count(); i++ ) 167 if ( lst->GetObject( i )->Equals( name ) ) 168 return lst->GetObject( i ); 169 return 0; 170 } 171 172 static XMLAttribute* 173 translateAttribute( XMLAttributeList* lst, 174 String const& name, String const& translation ) 175 { 176 if ( XMLAttribute* a = findAttribute( lst, name ) ) 177 return lst->Replace ( new XMLAttribute( name.Copy( 1 ), translation ), a ); 178 return 0; 179 } 180 181 static void 182 translateElement( XMLElement* element, ByteString const& lang, 183 ResData* resData, MergeDataFile& mergeData ) 184 { 185 XMLAttributeList* attributes = element->GetAttributeList(); 186 std::vector<XMLAttribute*> interesting( interestingAttributes( attributes ) ); 187 188 189 if( !interesting.empty() ) 190 { 191 std::vector<XMLAttribute*>::iterator i( interesting.begin() ); 192 ByteString id = BSTRING( (*i++)->GetValue() ); 193 for ( ; i != interesting.end(); ++i ) 194 { 195 ByteString attributeId = id; 196 attributeId += BSTRING ( **i ); 197 resData->sGId = attributeId; 198 resData->sId = element->GetOldref(); 199 200 if ( PFormEntrys* entry = mergeData.GetPFormEntrys( resData ) ) 201 { 202 ByteString translation; 203 entry->GetText( translation, STRING_TYP_TEXT, lang, true ); 204 // ByteString original = removeContent( element ); 205 if ( !translation.Len() ) 206 #if 0 207 translation = original; 208 #else 209 translation = BSTRING( ( *i )->GetValue() ); 210 #endif 211 delete translateAttribute( attributes, **i , STRING( translation ) ); 212 } 213 } 214 } 215 } 216 217 static bool is_dir( ByteString const& name ) 218 { 219 DirectoryItem aItem; 220 OUString sFileURL( name.GetBuffer(), name.Len(), RTL_TEXTENCODING_UTF8 ); 221 FileBase::getFileURLFromSystemPath( sFileURL, sFileURL ); 222 if( DirectoryItem::get( sFileURL, aItem ) == FileBase::E_None ) 223 { 224 FileStatus aStatus(FileStatusMask_Type); 225 if( aItem.getFileStatus( aStatus ) == FileBase::E_None ) 226 { 227 if( aStatus.getFileType() == FileStatus::Directory ) 228 return true; 229 } 230 } 231 return false; 232 } 233 234 static void make_directory( ByteString const& name ) 235 { 236 OUString sFileURL( name.GetBuffer(), name.Len(), RTL_TEXTENCODING_UTF8 ); 237 FileBase::getFileURLFromSystemPath( sFileURL, sFileURL ); 238 Directory::create( sFileURL ); 239 } 240 241 static void insertMarker( XMLParentNode *p, ByteString const& file ) 242 { 243 if ( XMLChildNodeList* lst = p->GetChildList() ) 244 if ( lst->Count() ) 245 { 246 sal_uLong i = 1; 247 // Skip newline, if possible. 248 if ( lst->Count() > 1 249 && lst->GetObject( 2 )->GetNodeType() == XML_NODE_TYPE_DEFAULT ) 250 i++; 251 OUString marker = OUString::createFromAscii( "\n NOTE: This file has been generated automagically by transex3/layout/tralay,\n from source template: " ) 252 + STRING( file ) 253 + OUString::createFromAscii( ".\n Do not edit, changes will be lost.\n" ); 254 lst->Insert( new XMLComment( marker, 0 ), i ); 255 } 256 } 257 258 void TranslateLayout::MergeLanguage( ByteString const& language ) 259 { 260 ByteString xmlFile = mFiles.front(); 261 262 MergeDataFile mergeData( mLocalize, xmlFile, 263 sal_False, RTL_TEXTENCODING_MS_1252 ); 264 265 DirEntry aFile( xmlFile ); 266 SimpleXMLParser aParser; 267 LayoutXMLFile* layoutXml = new LayoutXMLFile( mMergeMode ); 268 if ( !aParser.Execute( aFile.GetFull() , STRING( xmlFile ), layoutXml ) ) 269 { 270 fprintf(stderr, "error: parsing: %s\n", xmlFile.GetBuffer() ); 271 return; 272 } 273 274 layoutXml->Extract(); 275 insertMarker( layoutXml, xmlFile ); 276 277 ResData resData( "", "", "" ); 278 resData.sResTyp = mProject; /* mGid1 ?? */ 279 resData.sFilename = xmlFile; 280 281 XMLHashMap* xmlStrings = layoutXml->GetStrings(); 282 for ( XMLHashMap::iterator i = xmlStrings->begin(); i != xmlStrings->end(); 283 ++i ) 284 { 285 if ( LangHashMap* languageMap = i->second ) 286 if ( XMLElement* element = ( *languageMap )[ "en-US" ] ) 287 translateElement( element, language, &resData, mergeData ); 288 } 289 290 #ifndef WNT 291 ByteString outFile = "/dev/stdout"; 292 #else 293 ByteString outFile = "\\\\.\\CON"; 294 #endif 295 if ( mOutput.Len() ) 296 { 297 outFile = mOutput; 298 if ( is_dir( outFile ) ) 299 { 300 ByteString outDir = mOutput; 301 outDir.Append( "/" ).Append( language ); 302 if ( !is_dir( outDir ) ) 303 make_directory( outDir ); 304 outFile = outDir; 305 outFile.Append( "/" ).Append( xmlFile ); 306 } 307 } 308 layoutXml->Write( outFile ); 309 delete layoutXml; 310 } 311 312 void TranslateLayout::Merge() 313 { 314 if ( mLanguages.size() ) 315 for ( std::vector<ByteString>::iterator i = mLanguages.begin(); 316 i != mLanguages.end(); ++i) 317 MergeLanguage( *i ); 318 else 319 MergeLanguage( mLanguage ); 320 } 321 322 void TranslateLayout::CreateSDF() 323 { 324 ByteString xmlFile = mFiles.front(); 325 #ifndef WNT 326 ByteString sdf = "/dev/stdout"; 327 #else 328 ByteString sdf = "\\\\.\\CON"; 329 #endif 330 if ( mOutput.Len() ) 331 sdf = mOutput; 332 Export::SetLanguages( mLanguages ); 333 HelpParser::CreateSDF( sdf, mProject, mRoot, xmlFile, 334 new LayoutXMLFile( mMergeMode ), mGid1 ); 335 } 336 337 void TranslateLayout::ExceptionalMain() 338 { 339 ParseCommandLine(); 340 if ( mLanguages.size() ) 341 mLanguage = mLanguages.front(); 342 if ( mMergeMode ) 343 Merge(); 344 else 345 CreateSDF(); 346 } 347 348 void TranslateLayout::Main() 349 { 350 try 351 { 352 ExceptionalMain(); 353 } 354 catch ( xml::sax::SAXException& rExc ) 355 { 356 OString aStr( OUStringToOString( rExc.Message, 357 RTL_TEXTENCODING_ASCII_US ) ); 358 uno::Exception exc; 359 if (rExc.WrappedException >>= exc) 360 { 361 aStr += OString( " >>> " ); 362 aStr += OUStringToOString( exc.Message, RTL_TEXTENCODING_ASCII_US ); 363 } 364 fprintf( stderr, "error: parsing: '%s'\n", aStr.getStr() ); 365 OSL_ENSURE( 0, aStr.getStr() ); 366 } 367 catch ( uno::Exception& rExc ) 368 { 369 OString aStr( OUStringToOString( rExc.Message, 370 RTL_TEXTENCODING_ASCII_US ) ); 371 fprintf( stderr, "error: UNO: '%s'\n", aStr.getStr() ); 372 OSL_ENSURE( 0, aStr.getStr() ); 373 } 374 } 375 376 TranslateLayout::TranslateLayout() 377 : Application() 378 , mGid1( "layout" ) 379 , mLanguage( "en-US" ) 380 , mLocalize( "localize.sdf" ) 381 , mOutput() 382 , mProject( "layout" ) 383 , mRoot() 384 , mMergeMode( false ) 385 , mLanguages() 386 , mFiles() 387 { 388 } 389 390 TranslateLayout::~TranslateLayout() 391 { 392 } 393 394 SAL_IMPLEMENT_MAIN() 395 { 396 TranslateLayout t; 397 t.Main(); 398 return 0; 399 } 400