Click here to Skip to main content
15,879,095 members
Articles / Desktop Programming / MFC

RC Localization Tool (LocalizeRC)

Rate me:
Please Sign up or sign in to vote.
4.89/5 (53 votes)
14 Jan 2004BSD9 min read 485.6K   9.5K   159  
A tool for localizing/translating Resource Scripts
// LocalizeRCDlg.cpp : implementation file
//

#include "stdafx.h"
#include "LocalizeRC.h"
#include "LocalizeRCDlg.h"

#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//#define _MFC_VER 0x0420

// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CLocalizeRCDlg dialog


CLocalizeRCDlg::CLocalizeRCDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CLocalizeRCDlg::IDD, pParent)
	, m_bCopy(FALSE)
	, m_nObsoleteItems(0)
	, m_strDetails(_T("no Workspace loaded"))
	, m_strWorkspace(_T(""))
	, m_strEdit(_T(""))
	, m_strTextmode(_T(""))
{
	CWinApp* pApp = AfxGetApp();

	m_hIcon = pApp->LoadIcon(IDR_MAINFRAME);
	m_strWorkspace = pApp->GetProfileString( SEC_LASTPROJECT, ENT_WORKSPACE );
}

void CLocalizeRCDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Check(pDX, IDC_COPY, m_bCopy);
	DDX_CBIndex(pDX, IDC_OBS_ITEMS, m_nObsoleteItems);
	DDX_Text(pDX, IDC_DETAILS, m_strDetails);
	DDX_Text(pDX, IDC_WORKSPACE, m_strWorkspace);
	DDX_Control(pDX, IDC_LANGUAGE, m_CtrlLanguage);
	DDX_Text(pDX, IDC_EDIT, m_strEdit);
	DDX_Text(pDX, IDC_TEXTMODE, m_strTextmode);
}

BEGIN_MESSAGE_MAP(CLocalizeRCDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_CREATEINI, OnBnClickedCreateini)
	ON_BN_CLICKED(IDC_OPENINI, OnBnClickedOpenini)
	ON_BN_CLICKED(IDC_CREATEOUTPUT, OnBnClickedCreateoutput)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_REVERSEINI, OnBnClickedReverseini)
	ON_BN_CLICKED(IDC_CHNG_WORKSPACE, OnBnClickedChngWorkspace)
	ON_BN_CLICKED(IDC_MODIFYWORKSPACE, OnBnClickedModifyworkspace)
	ON_CBN_SELCHANGE(IDC_LANGUAGE, OnCbnSelchangeLanguage)
END_MESSAGE_MAP()


// CLocalizeRCDlg message handlers
BOOL CLocalizeRCDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	//SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// display text mode (UNICODE/ANSI)
#ifdef UNICODE
	m_strTextmode.LoadString( IDS_UNICODE );
#else
	m_strTextmode.LoadString( IDS_ANSI );
#endif
	UpdateData(false);

	// get installed languages
	CFileFind Finder;
	TCHAR szFilename[MAX_PATH];
	GetModuleFileName( NULL, szFilename, MAX_PATH );
	
	AddLanguage( &m_CtrlLanguage, _T("09"), LangID );
	
	// the last 2 chars are the LanguageID in hexadecimal form
	CString strSearch;
	strSearch.Format( _T("%sLocalizeRC????.dll"), GetFolder(szFilename) );

	BOOL bResult = Finder.FindFile( strSearch );
	CString strLangCode;

	while( bResult )
	{
		bResult = Finder.FindNextFile();
		// extract 2-digit language code
		strLangCode = Finder.GetFileTitle().Right(2);
		AddLanguage( &m_CtrlLanguage, strLangCode, LangID );
	}
	Finder.Close();

	// Load last workspace
	LoadWorkspace();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CLocalizeRCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CLocalizeRCDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CLocalizeRCDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

#define NUMKEYWORDS 8
LPCTSTR strKeyword[NUMKEYWORDS] = 
{
	_T("ACCELERATORS"),
	_T("DIALOG"),
	_T("DIALOGEX"),
	_T("MENU"),
	_T("MENUEX"),
	_T("STRINGTABLE"),
	_T("DLGINIT"),
	_T("code_page")
};
	
// random string, that indicates an error
#define ERR_STR _T("asfdshkagzuwrthgadsfhgkh12385143258")

void CLocalizeRCDlg::OpenInputRC(void)
{
	// The file from which to load the contents of rich edit control
	CStdioFile* pFile = NULL;
	
	BOOL bIsUnicode = CStdioUnicodeFile::IsUnicode( strInputRC );

#ifdef UNICODE	
	if( bIsUnicode )
		pFile = new CStdioUnicodeFile( strInputRC, CFile::modeRead | CFILEFLAG_UNICODEHELPER );
	else
		pFile = new CStdioUnicodeFile( strInputRC, CFile::modeRead );
#else
	if( bIsUnicode )
	{
		CString strErr;
		strErr.Format( IDS_UNICODEFILE, strInputRC );
		AfxMessageBox( strErr );

		// clear contents in edit-control
		m_strEdit = _T("");
		UpdateData( false );
		return;	// cancel
	}
	else
        pFile = new CStdioUnicodeFile( strInputRC, CFile::modeRead );
#endif
	
	// clear contents in edit-control
	m_strEdit = _T("");

	// fill edit-control with contents of the RC-file
	CString strLine;
	while( pFile->ReadString( strLine ) )
		m_strEdit += strLine + _T("\r\n");
	UpdateData( false );

	pFile->Close();
	delete pFile;
}

void CLocalizeRCDlg::OnBnClickedCreateini()
{
	// Write/Actualize INI-File
    if( !WriteReadIni(true) )
		AfxMessageBox( IDS_OLDFILEFORMAT );
}

// extract first caption after *pnPosition
CString CLocalizeRCDlg::ExtractCaption(CString& strText, int* pnPosition, CString strKeyword, CString &strIDC )
{
	CString strCaption, strLine;
	int nStart, nEnd, nStartQuote, nEndQuote;

	nStart = *pnPosition;

	// DLGINIT: Microsoft-Format to store data for comboboxes
	if( strKeyword == _T("DLGINIT") )
	{		
		if( nStart == 0 )
		{
			// remove BEGIN and END
			nStart = FindSeperateWord( strText, _T("BEGIN"), 0 );
			if( nStart != -1 )
				nStart += 7;	
		}

		// FORMAT: <IDC>, 0x403, <LENGTH IN BYTES>, 0
		// FORMAT: <WORD> als Hex Wert chars in LOBYTE und HIGHBYTE
		// comma seperated, null-terminated, ends with comma

		// comma seperated, null-terminated, ends with comma
		CString strHelp;
		LPTSTR pHelp;
		int nWord, nToken = 0, nLength = 0;

		CString strToken = StringTokenize( strText, _T(",\r\n"), &nStart);
		while (nStart != -1)
		{
			strToken.TrimLeft();
			strToken.TrimRight();
			if( strToken.IsEmpty() )
			{
				strToken = StringTokenize( strText, _T(",\r\n"),&nStart);
				continue;
			}

			switch( nToken )
			{
				case 0:		// IDC	oder 0 bei Ende von Liste
					if( strToken == "0" )
					{
						*pnPosition = nStart;
						return strCaption;
					}
					else // IDC
					{
						if( strIDC.IsEmpty() )
						{
							strIDC = strToken; 	// new IDC
						}
						else
						{
							if( strIDC != strToken )
								return strCaption;       
						}
					}

					break;
				case 1:		// 0x403 for Combo-Boxes
					if( strToken != "0x403" )
						return ERR_STR;
					break;
				case 2:		// Length of String
					nLength = _ttoi( strToken );
					nLength = (int)floor((nLength / 2.0)+0.5);
					break;
				case 3:		// always 0
					break;
				default:	// everything above 3 is word values
					if( nToken <= nLength + 3 )
					{
						// convert string to hexadecimal integer
						nWord = _tcstol( strToken, &pHelp, 16 );
						strHelp.Format( _T("%c%c"), LOBYTE(nWord), HIBYTE(nWord) );
						strHelp.Remove( '\0' );
						strCaption += strHelp;
						if( nToken == nLength + 3 )	// last word
						{
							strCaption += ";";
							nToken = -1;
						}
					}
			}
			nToken++;
			*pnPosition = nStart;
			strToken = StringTokenize(strText, _T(",\r\n"), &nStart);
		}
		return ERR_STR;
	}	

	// find end of line
	while( (nEnd = strText.Find( _T("\r\n"), nStart )) != -1 )
	{
		// strLine contents one command line
		strLine += strText.Mid( nStart, nEnd - nStart );
					
		if( strLine.GetLength() <= 0 )
		{
			// next line
			nStart = nEnd+2;
			*pnPosition = nStart;
			continue;
		}

		// merge multiline commands to one line
		if( (strLine[strLine.GetLength()-1] == '|') || 
			(strLine[strLine.GetLength()-1] == ',') )
		{
			// next line
			nStart = nEnd+2;
			continue;
		}

		TCHAR chHelp;
		if( nStart > 0 )
			chHelp = strText[nStart-1];

		// search for first "
		nStartQuote = FindQuote( strLine );
		if( nStartQuote == -1  || !MustBeTranslated(strLine, strKeyword) )	// not found
		{
			strLine = "";
			nStart = *pnPosition = nEnd+2;
			continue;
		}

		nEndQuote = FindQuote( strLine, nStartQuote );
		if( nEndQuote == -1 )
		{
			strLine = "";
			nStart = *pnPosition = nEnd+2;
			continue;
		}

		nStartQuote--;
		chHelp = strText[nStart+nStartQuote];
		strCaption = strLine.Mid( nStartQuote, nEndQuote-nStartQuote );
		*pnPosition += nEndQuote;
		chHelp = strText[*pnPosition];
		return strCaption;
	}
	return ERR_STR;
}

void CLocalizeRCDlg::OnBnClickedOpenini()
{
	UpdateData( true );
	// open lang.ini with standard association
	ShellExecute( m_hWnd, _T("open"), strLangINI, NULL, NULL, SW_SHOWNORMAL );
}

void CLocalizeRCDlg::OnBnClickedCreateoutput()
{
	UpdateData( true );
	if( !WriteReadIni( false ) )
	{
		AfxMessageBox( IDS_OLDFILEFORMAT );
		return;
	}
	UpdateData( false );

	// create output RC
	CFile File( strOutputRC, CFILEFLAG_UNICODEHELPER | CFile::modeWrite|CFile::modeCreate );
#ifdef UNICODE
	// write 0xFF 0xFE
	BYTE fffe[2] = { 0xFF, 0xFE };
	File.Write(fffe, 2);
#endif
	File.Write( m_strEdit, sizeof(TCHAR) * m_strEdit.GetLength() );
	File.Close();

	// copy header and RES-Folder
	if( m_bCopy )
	{
		CString strOutputFolder = GetFolder( strOutputRC );
		CString strInputFolder = GetFolder( strInputRC );
        		
		// copy header
		if( !CopyFile( strInputFolder+_T("resource.h"), strOutputFolder+_T("resource.h"), false ) )
			AfxMessageBox( IDS_ERR_FILECOPY );
		
		// create folder
		CreateDirectory( strOutputFolder+_T("RES"), NULL );

		CFileFind FFind;
		BOOL bWorking = FFind.FindFile( strInputFolder+_T("RES\\*.*"), 0 );
		while( bWorking ) 
		{
			bWorking = FFind.FindNextFile();

			// skip . and .. files; otherwise, we'd
			// recur infinitely!

			if (FFind.IsDots() || FFind.IsDirectory())
				continue;

			else
				// copy file
				if( !CopyFile( FFind.GetFilePath(), strOutputFolder+_T("RES\\")+FFind.GetFileName(), false ) )
					AfxMessageBox( IDS_ERR_FILECOPY );
		}
		FFind.Close();
	}
	// Fill Edit with InputRC again
	OpenInputRC();
}


// checks if line contents strings that have to be translated
bool CLocalizeRCDlg::MustBeTranslated(CString strLine, CString strKeyword )
{
	// if it is stringtable -> translate
	if( strKeyword == ::strKeyword[5] )
		return true;

	bool bTranslate = true;

	strLine.TrimLeft(_T(" "));
	strLine.TrimRight(_T(" "));
		
	if(strLine[0] == '#') // Preprocessor line
		bTranslate = false;

	if(strLine[0] == '/') // Comment line
		bTranslate = false;

	//--- Exclude following controls from translation ---------------------
	if(strLine.Find(_T("msctls_updown32"), 0) > 0) // Spin control
		bTranslate = false;

	if(strLine.Find(_T("SysTreeView32"), 0) > 0) // Tree view control
		bTranslate = false;

	if(strLine.Find(_T("msctls_trackbar32"), 0) > 0) // Slider control
		bTranslate = false;

	if(strLine.Find(_T("SysIPAddress32"), 0) > 0) // IP adress
		bTranslate = false;

	if(strLine.Find(_T("msctls_hotkey32"), 0) > 0) // Hot key
		bTranslate = false;

	if(strLine.Find(_T("SysListView32"), 0) > 0) // List view control
		bTranslate = false;

	if(strLine.Find(_T("SysAnimate32"), 0) > 0) // Animate control
		bTranslate = false;

	if(strLine.Find(_T("SysMonthCal32"), 0) > 0) // Month calendar
		bTranslate = false;

	if(strLine.Find(_T("ComboBoxEx32"), 0) > 0) // Extended combo box
		bTranslate = false;

	if( !bTranslate )
		return false;

	bTranslate = false;

	//--- Include following controls into translation --------------- 
	if(strLine.Find(_T("CAPTION"), 0) == 0)
		bTranslate = true;					// Dialog box caption

	if(strLine.Find(_T("POPUP"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("MENUITEM"), 0) == 0)
		bTranslate = true;

	/*if(strLine.Find("ID_", 0) > -1)
		bTranslate = true;

	if(strLine.Find("IDS_", 0) > 0)
		bTranslate = true;

	if(strLine.Find(" AFX_IDS_", 0) > 0)
		bTranslate = true;*/

	if(strLine.Find(_T("PUSHBUTTON"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("DEFPUSHBUTTON"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("LTEXT"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("RTEXT"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("CTEXT"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("GROUPBOX"), 0) == 0)
		bTranslate = true;

	if(strLine.Find(_T("CONTROL"), 0) == 0)
	{ 
		if(strLine.Find(_T("BS_AUTORADIOBUTTON"), 0) != -1 )
			bTranslate = true;

		if(strLine.Find(_T("BS_AUTOCHECKBOX"), 0) != -1 )
			bTranslate = true;
	}
	if( strLine.Find( _T("ID"), 0) == 0)	// stringtable
		bTranslate = true;

	if( strLine[0] == '"' )				// accelerator
		bTranslate = true;

    return bTranslate;
}

// return the position where '"' was found
int CLocalizeRCDlg::FindQuote(CString strLine, int nStartPos)
{
	int nPosition = nStartPos;
	
	// search for '"', that is not a quotation mark inside the text ("")
	do 
	{
		nPosition = strLine.Find( '"', nPosition );
		if( nPosition == -1 )
			return nPosition;

		nPosition++;

		if( nPosition >= strLine.GetLength()-1 )
			return nPosition;
	}
	while( strLine[nPosition++] == '"' );
	return nPosition-1;
}

int CLocalizeRCDlg::WriteReadIni(bool bWrite)
{
	BYTE nKeyword;
	
	// Create or Open INI-File
	CIniEx IniEx;
	if( !IniEx.Open( strLangINI ) )
		return true;

	// old sectionnames in INI file ?
	LPCTSTR strOldKeywords[5] =
	{
		_T(" ACCELERATORS "),
		_T(" DIALOG "),
		_T(" DIALOGEX "),
		_T(" MENU "),
		_T(" MENUEX ")
	};

	for( nKeyword = 0; nKeyword < 5; nKeyword++ )
	{
		CString strHelp = strOldKeywords[nKeyword];
		if( IniEx.LookupSection(&strHelp) != -1 )
			return false;
	}

	CString strCaption, strHelp, strValue, strIDC;
	int nStart, nHelp, nPosition, nSelStart, nSelEnd, nOldLength, nNewLength, nPrevHelp;

	for( nKeyword = 0; nKeyword < NUMKEYWORDS-1; nKeyword++ )
	{
		nPosition = 0;
		// search for keyword section in RC-file
		while( (nPosition = FindSeperateWord( m_strEdit, strKeyword[nKeyword], nPosition )) != -1 )
		{
			nStart = nPosition;

			// find BEGIN
			nHelp = FindSeperateWord( m_strEdit, _T("BEGIN"), nPosition );
			
			// find related END (ignore interlocked BEGINs-ENDs)
			nPosition = nHelp-1;
			do
			{
				nPosition = FindSeperateWord( m_strEdit, _T("END"), nPosition+1);
				nHelp = FindSeperateWord( m_strEdit, _T("BEGIN"), nHelp+1 );
			}
			while( nHelp < nPosition && nHelp != -1 );
			
			// save SECTION in strHelp
			strHelp = m_strEdit.Mid( nStart, nPosition-nStart );
			
			nHelp = nPrevHelp = 0;

			// extract caption
			while( (strCaption = ExtractCaption( strHelp, &nHelp, strKeyword[nKeyword], strIDC )) != ERR_STR )
			{
				if( bWrite )
				{
					// check if key in Lang.INI already exists
					strValue = IniEx.GetValue( strKeyword[nKeyword], strCaption );
					if( strValue.IsEmpty() )
					{
						// insert line in Lang.INI
						IniEx.SetValue( strKeyword[nKeyword], strCaption, _T("#")+strCaption );
					}
					else
					{
						// insert line in Lang.INI
						IniEx.SetValue( strKeyword[nKeyword], strCaption, _T("#")+strValue );
					}
				}
				else
				{
					// check if key in Lang.INI exists
					strValue = IniEx.GetValue( strKeyword[nKeyword], strCaption );
					if( !strValue.IsEmpty() )
					{
						// if it is DLGINIT
						if( nKeyword == 6 )
						{
							CString strInsert, strToken, strHelp2;
							WORD wData;
							int n;
							nPrevHelp = 0;

							while( (strToken = StringTokenize( strValue, _T(";"), &nPrevHelp )) != "" )
							{
								// first line: header
								strHelp2.Format(_T("\t%s, 0x403, %d, 0\r\n"), strIDC, strToken.GetLength()+1 );
								strInsert += strHelp2;

								// second line: data
								for( n=0; n < strToken.GetLength(); n+=2 )
								{
									if( n == (strToken.GetLength() - 1) )
										wData = MAKEWORD( strToken.GetAt(n), 0 );
									else
										wData = MAKEWORD( strToken.GetAt(n), strToken.GetAt(n+1) );
									strHelp2.Format( _T("0x%04x, "), wData );
									strInsert += strHelp2;
								}

								// eventually add null character
								if( n == strToken.GetLength() )
								{
									strInsert += _T("\"\\000\"");
								}
								strInsert += _T("\r\n");
							}
							if( nHelp >= strHelp.GetLength() -1 )
							{
								strInsert += _T("\t0\r\n");
								nSelEnd = strHelp.GetLength();
							}
							else
								nSelEnd = nHelp;

							// find BEGIN
							nSelStart = FindSeperateWord(strHelp, _T("BEGIN"), 0 );
							nSelStart += 7;

							nOldLength = nSelEnd-nSelStart;
							strHelp.Delete( nSelStart, nOldLength  );
							nNewLength = strInsert.GetLength();
							strHelp.Insert( nSelStart, strInsert );	
						}
						else
						{
							nSelStart = nHelp-strCaption.GetLength();
							nSelEnd = nHelp;

							nOldLength = nSelEnd-nSelStart;
							strHelp.Delete( nSelStart, nOldLength  );
							nNewLength = strValue.GetLength();
							strHelp.Insert( nSelStart, strValue );	
						}
						nHelp += nNewLength - nOldLength;
						if( nHelp < 0 )
							nHelp = 0;
					}
					nPrevHelp = nHelp;
				}
				strIDC.Empty();
			}
			if( !bWrite )
			{
				m_strEdit.Delete( nStart, nPosition-nStart );
				m_strEdit.Insert( nStart, strHelp );
				nPosition = nStart + strHelp.GetLength();
			}
		}
	}
#define CODEPAGE _T("code_page")
	// search for codepage
	nPosition = 0;
	CString strCodepage;
	while( (nPosition = FindSeperateWord( m_strEdit, CODEPAGE, nPosition )) != -1 )
	{
		nSelEnd = m_strEdit.Find( ')', nPosition );
		nSelStart = m_strEdit.Find( '(', nPosition ) + 1;

		strCaption = m_strEdit.Mid( nSelStart, nSelEnd-nSelStart );
		
		// check if key in Lang.INI already exists
		strValue = IniEx.GetValue( CODEPAGE, strCaption );

		if( bWrite )
		{
	
			if( strValue.IsEmpty() )
			{
				// insert line in Lang.INI
				IniEx.SetValue( CODEPAGE, strCaption, _T("#")+strCaption );
			}
			else
			{
				// insert line in Lang.INI
				IniEx.SetValue( CODEPAGE, strCaption, _T("#")+strValue );
			}
		}
		else
		{
			if( !strValue.IsEmpty() )
			{
				m_strEdit.Delete( nSelStart, nSelEnd-nSelStart );
				m_strEdit.Insert( nSelStart, strValue );
				nPosition = nSelStart + strValue.GetLength();
			}
		}
		nPosition++;
	}

	if( bWrite )
	{
		// remove unnecessary lines in Lang.INI
		CStringArray strSections, strKeys;
		IniEx.GetSections( strSections );

		// go through every section
		int nSection=0;
		while( nSection < strSections.GetSize() )
		{
			strKeys.RemoveAll();
			IniEx.GetKeysInSection( strSections[nSection], strKeys );
			for( nKeyword = 0; nKeyword < NUMKEYWORDS; nKeyword++ )
			{
				// dont touch unknown sections
				if( _tcscmp(strSections[nSection], strKeyword[nKeyword]) != 0 )
					continue;

				// Go through every key in this section
				int nKey=0;
				while( nKey < strKeys.GetSize() )
				{
					// check if key is necessary for this RC
					strValue = IniEx.GetValue( strSections[nSection], strKeys[nKey] );
					if( strValue[0] == '#' )
					{
						strValue.TrimLeft( _T(" #") );
						IniEx.SetValue( strSections[nSection], strKeys[nKey], strValue );
					}
					else
					{
						// key is obsolete and no longer needed
						switch( m_nObsoleteItems )
						{
							case 0:	// delete item	
								IniEx.RemoveKey( strSections[nSection], strKeys[nKey]);
								break;
							case 1:
								IniEx.SetValue( strSections[nSection], strKeys[nKey], _T("#") + strValue );
								break;
						}
					}
					nKey++;
				}
				break;
			}
			if( nKeyword == NUMKEYWORDS && strSections[nSection] != CODEPAGE  )
				IniEx.SetValue( strSections[nSection], _T("!!! This sectionname isn't recognized by LocalizeRC. Probably you can delete the whole section !!!"), _T("") );

			strKeys.RemoveAll();
			IniEx.GetKeysInSection( strSections[nSection], strKeys );
			
			// remove empty sections
			if( strKeys.GetSize() == 0 )
			{
				if( IniEx.RemoveSection( strSections[nSection] ) )
					strSections.RemoveAt( nSection );
				else
					nSection++;
			}
			else
				nSection++;
		}
		IniEx.SortIniValues();
		IniEx.WriteFile();
	}
	return true;
}

void CLocalizeRCDlg::OnDestroy()
{
	CDialog::OnDestroy();

	AfxGetApp()->WriteProfileString( SEC_LASTPROJECT, ENT_WORKSPACE, m_strWorkspace );
	SaveWorkspace();
}	

CString CLocalizeRCDlg::GetFolder(CString strPath)
{
	TCHAR path[_MAX_PATH];
	TCHAR drive[_MAX_DRIVE];   
	TCHAR dir[_MAX_DIR];

	_tcscpy( path, strPath );
	
	// Trenne Pfad von Anwendungsnamen
	_tsplitpath( path, drive, dir, NULL, NULL );

	CString strReturn;
	strReturn.Format( _T("%s%s"), drive, dir);
	return strReturn;
}

void CLocalizeRCDlg::OnBnClickedReverseini()
{
	// Create or Open INI-File
	CIniEx IniEx;
	IniEx.Open( strLangINI );

	CStringArray strSections, strKeys;
	CString strHelp;
	IniEx.GetSections( strSections );
	int nKey, nSec;
	
	// go through every section
	for( nSec=0; nSec < strSections.GetSize(); nSec++ )
	{
		// go through every key
		IniEx.GetKeysInSection( strSections[nSec], strKeys );
		for( nKey=0; nKey < strKeys.GetSize(); nKey++ )
		{
			strHelp = IniEx.GetValue( strSections[nSec], strKeys[nKey] );	
			if( strHelp != strKeys[nKey] )
			{
				IniEx.SetValue( strSections[nSec], strHelp, strKeys[nKey] );
				IniEx.RemoveKey( strSections[nSec], strKeys[nKey] );
			}
		}
	}
	IniEx.WriteFile();
}

#include "CreateWorkspace.h"

void CLocalizeRCDlg::OnBnClickedChngWorkspace()
{
	// Change Workspace
	UpdateData( true );

	CString strExtension, strExtensionInfo;
	strExtension.LoadString( IDS_EXTLWS );
	strExtensionInfo.LoadString( IDS_EXTLWSDESCRIPTION ); 

#if _MFC_VER >= 0x0700
	CFileDialog FileDialog( true, strExtension, NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_EXPLORER|OFN_ENABLESIZING,
							strExtensionInfo, this, 0 );
#else
	CFileDialog FileDialog( true, strExtension, NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_EXPLORER|OFN_ENABLESIZING,
							strExtensionInfo, this );
#endif
	_tcscpy( FileDialog.m_ofn.lpstrFile, m_strWorkspace );

	if( FileDialog.DoModal() == IDOK )
	{
		m_strWorkspace = FileDialog.GetPathName();
		if( !LoadWorkspace() )
		{
			CCreateWorkspace dlg;
			dlg.strWorkspace = m_strWorkspace;
			dlg.DoModal();
			LoadWorkspace();
		}
		UpdateData( false );
	}
}

BOOL CLocalizeRCDlg::LoadWorkspace(void)
{
	CIniEx IniEx;
	if( !IniEx.Open( m_strWorkspace, 1, 0 ) )
		return false;

	CString strPath = GetFolder(m_strWorkspace);
	strInputRC = GetAbsolutePathFromIni( &IniEx, ENT_INPUTRC, strPath );
	strLangINI = GetAbsolutePathFromIni( &IniEx, ENT_LANGINI, strPath );
	strOutputRC = GetAbsolutePathFromIni( &IniEx, ENT_OUTPUTRC, strPath );
	m_strDetails.Format( IDS_DETAILS, strInputRC, strLangINI, strOutputRC );

	m_bCopy = _ttoi( IniEx.GetValue(ENT_COPY) );
	m_nObsoleteItems = _ttoi( IniEx.GetValue(ENT_OBSITEMS) );

	OpenInputRC();

	UpdateData( false );
	return true;
}

BOOL CLocalizeRCDlg::SaveWorkspace(void)
{
	CIniEx IniEx;
	if( !IniEx.Open( m_strWorkspace ) )
		return false;

	// save settings to registry
	CString strHelp;
	strHelp.Format( _T("%d"), m_bCopy );
	IniEx.SetValue( ENT_COPY, strHelp );
	strHelp.Format( _T("%d"), m_nObsoleteItems );
	IniEx.SetValue( ENT_OBSITEMS, strHelp );

	IniEx.WriteFile();

	return 0;
}

CString CLocalizeRCDlg::GetAbsolutePathFromIni(CIniEx* pIniEx, CString strKey, CString strPath)
{
	CString strHelp = pIniEx->GetValue( strKey );
	CString strValue;
	if( strHelp.IsEmpty() )
		return _T("");

	if( PathIsRelative( strHelp ) )
	{
		TCHAR szPath[MAX_PATH];
		PathCombine( szPath, strPath, strHelp );
		strHelp.Format(_T("%s"), szPath);
		return strHelp;
	}
	return strHelp;
}

void CLocalizeRCDlg::OnBnClickedModifyworkspace()
{
	CCreateWorkspace dlg;
	dlg.strWorkspace = m_strWorkspace;
	dlg.DoModal();
	LoadWorkspace();
}

void CLocalizeRCDlg::OnCbnSelchangeLanguage()
{
	// Save Changes
	AfxGetApp()->WriteProfileInt( SEC_LASTPROJECT, ENT_LANGID, m_CtrlLanguage.GetItemData(m_CtrlLanguage.GetCurSel()) );
	AfxMessageBox( IDS_RESTARTAPP );
}

#define STR_LEN 64

int CLocalizeRCDlg::AddLanguage(CComboBox* pComboBox, LPCTSTR strLangCode, LANGID SelectedID)
{
	LCID lcID;
	TCHAR szLangName[STR_LEN];
	LPTSTR pHelp;
	int nIndex;

	lcID =  MAKELCID( MAKELANGID(_tcstoul( strLangCode, &pHelp, 16 ), SUBLANG_NEUTRAL ), SORT_DEFAULT );
	GetLocaleInfo( lcID, LOCALE_SNATIVELANGNAME , szLangName, STR_LEN);
	nIndex = pComboBox->AddString( szLangName );
	pComboBox->SetItemData( nIndex, lcID );
	if( lcID == SelectedID )
		pComboBox->SetCurSel( nIndex );
	return nIndex;
}

CString CLocalizeRCDlg::StringTokenize(CString strSource, LPCTSTR pszTokens, int* pnStart)
{
#if _MFC_VER >= 0x0700
	return strSource.Tokenize( pszTokens, *pnStart );
#else
	// original code CString::Tokenize from MFC7
	/////////////////////////////////////////////////

	if( pszTokens == NULL )
	{
		return( strSource );
	}

	// (LPCSTR)(LPCTSTR) hack for VC6.
	LPCTSTR pszPlace = (LPCTSTR)strSource+*pnStart;
	LPCTSTR pszEnd = (LPCTSTR)strSource+strSource.GetLength();
	if( pszPlace < pszEnd )
	{
		int nIncluding = StringSpanIncluding( pszPlace, pszTokens );

		if( (pszPlace+nIncluding) < pszEnd )
		{
			pszPlace += nIncluding;
			int nExcluding = StringSpanExcluding( pszPlace, pszTokens );

			int iFrom = *pnStart+nIncluding;
			int nUntil = nExcluding;
			*pnStart = iFrom+nUntil+1;

			return( strSource.Mid( iFrom, nUntil ) );
		}
	}

	// return empty string, done tokenizing
	*pnStart = -1;

	return( CString( "" ) );
#endif
}

#define SEPERATORS _T(" \r\n,\t()")

int CLocalizeRCDlg::FindSeperateWord(CString strText, LPCTSTR strWord, int nStartPos)
{
	int nFoundPos;
	while( (nFoundPos = strText.Find( strWord, nStartPos )) != -1 )
	{
        CString strSeperator;

		// look for preceding character
		if( nFoundPos > 0 )
		{
			strSeperator = strText[nFoundPos-1];
			// if preceding character isn't a separator, continue search
			if( strSeperator.FindOneOf( SEPERATORS ) == -1 )
			{
				nStartPos += nFoundPos;
				continue;
			}
		}

		// look for successing character
		int nSuccessingPos =  nFoundPos+_tcslen(strWord);
		if( nSuccessingPos < strText.GetLength() )
		{
			strSeperator = strText[nSuccessingPos];

			// if successing character isn't a separator, continue search
			if( strSeperator.FindOneOf( SEPERATORS ) == -1 )
			{
				nStartPos += nFoundPos;
				continue;
			}
		}
		break;
		
	}
	return nFoundPos;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Web Developer
Germany Germany
Author of the shareware WinCD.

Comments and Discussions