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