對這文章發表回應
發表限制: 非會員 可以發表
DotProject中文亂碼解決總結
Andrew(zhuyi)
DotProject是一個基於AMP(apache+mysql+php)的開源項目管理工具,DotProject可以支持多語言並採用模塊化設計以便於擴展。
最近需要一個項目管理軟件,於是就下載DotProject進行了試用,感覺不錯,但有中文有亂碼問題,如:日曆和gantt圖。現就對解決DotProject亂碼進行一下總結,由於本人以前沒接觸過PHP,初次修改有錯的地方請大家指正。
機器環境:WindowsXP SP2簡體中文,apache2.0.59, mysql-5.0.16-win32, php-5.2.0,其他版本沒有測過,不能保證在其他版本下正確運行,由其在PHP4下。
1. 加入語言包
DotProject有比較不錯的多語言包,項目叫dot modules,在sourceforge上有。DotProject的語言包目錄./locales,運行可根據配置動態加載語言包進行對照轉換相應的語言。下載中文語言包解壓後並複製到語言目錄locales下,修改語言包文件夾下locales.php文件:
$locale_char_set = 'GB2312';為$locale_char_set = 'utf-8';
同時把英文語言包的同名文件也進行修改。使用'utf-8'的好處就是瀏覽器用unicode(utf-8)解碼,頁面可顯示多種語言文字。防照英文語言包創建lang.php文件,加入如下內容:
<? php
$dir = basename ( dirname ( __FILE__ ));
$LANGUAGES [ ' zh-cn ' ] = array ( $dir , ' Chinese (Simplified) ' , ' 簡體中文 ' , ' chs ' );
?>
如果繁體則:
<? php
$dir = basename ( dirname ( __FILE__ ));
$LANGUAGES [ ' zh-tw ' ] = array ( $dir , ' Chinese (Traditional) ' , ' 繁體中文 ' , ' cht ' );
?>同時把目錄下的所有對照文件轉換為utf-8編碼存儲(可用ultraEdit等文本編輯器轉換或登錄後用DotProject翻譯管理進行更改),初步漢化完成。
2. 修正在中文下日曆的亂碼問題
本地化語言後日曆的星期顯示為亂碼,這是由於DotProject採用讀取操作系統本地語言區域日期格式引起的,在windowsXP中文版默認是“星期幾,xxxx-x-x”,且可能為GB2312編碼(本地區域語言可以更改)。DotProject讀取操作系統的日期用utf8_encode轉換後再顯示,這樣只要操作系統的本地區域日期語言格式和運行DotProject選取的語言不一至時就會出現亂碼,顯然不是聰明的做法。
1)語言包文件夾下locales.php文件中加入日期格式對照表變量如下:
$locale_weeks = array ( ' 星期日 ' , ' 星期一 ' , ' 星期二 ' , ' 星期三 ' , ' 星期四 ' , ' 星期五 ' , ' 星期六 ' );
$locale_short_week =array ( ' 日 ' , ' 一 ' , ' 二 ' , ' 三 ' , ' 四 ' , ' 五 ' , ' 六 ' );
$locale_months = array ( null , ' 一月 ' , ' 二月 ' , ' 三月 ' , ' 四月 ' , ' 五月 ' , ' 六月 ' , ' 七月 ' , ' 八月 ' , ' 九月 ' , ' 十月 ' , ' 十一月 ' , ' 十二月 ' );
$locale_short_months = array ( null , ' 1月 ' , ' 2月 ' , ' 3月 ' , ' 4月 ' , ' 5月 ' , ' 6月 ' , ' 7月 ' , ' 8月 ' , ' 9月 ' , ' 10月 ' , ' 11月 ' , ' 12月 ' );2)DotProject的日期處理基類在lib/PEAR/Date下Calc.php文件中,修改或增加其中的相關幾個函數。
加入和修改如下函數:
// ***************************************************************
//得到月全名稱列表
function getMonthNames()
{
global $locale_months ;
if ( ! empty ( $locale_months )){
$months = $locale_months ;
} else {
for ( $i = 1 ; $i < 13 ; $i ++ ){
$months [ $i ] = strftime ( ' %B ' , mktime ( 0 , 0 , 0 , $i , 1 , 2001));
}
}
return ( $months );
}
// ****************************************************************
//得到月短名稱列表
function getMonthShortNames( $length = 3 )
{
global $locale_short_months ;
if ( ! empty ( $locale_short_months )){
$months = $locale_short_months ;
} else {
for ( $i = 1 ; $i < 13 ; $i ++ ){
$months [ $i ] = strftime ( ' %B ' , mktime ( 0 , 0 , 0 , $i , 1 , 2001 ));
$months [ $i ] = substr ( $months [ $i ] , 0 , $length );
}
}
return ( $months );
} 
// *****************************************************************
//得到星期全名稱列表
function getWeekDays()
{
global $locale_weeks ;
if ( ! empty ( $locale_weeks )){
$weekdays = $locale_weeks ;
} else {
for ( $i = 0 ; $i < 7 ; $i ++ ){
$weekdays [ $i ] = strftime ( ' %A ' , mktime ( 0 , 0 , 0 , 1 , $i , 2001 ));
}
}
return ( $weekdays );
}
// ****************************************************************
//得到星期短名稱列表
function getShortWeekDays( $length = 3 )
{
global $locale_short_week ;
if ( ! empty ( $locale_short_week )){
$weekdays = $locale_short_week ;
} else {
for ( $i = 0 ; $i < 7 ; $i ++ ){
$weekdays [ $i ] = strftime ( ' %A ' , mktime ( 0 , 0 , 0 , 1 , $i , 2001 ));
$weekdays [ $i ] = substr ( $weekdays [ $i ] , 0 , $length);
}
}
return ( $weekdays );
} 
//****************************************************************
//得到月全名稱
function getMonthFromFullName( $month )
{
$month = strtolower ( $month );
$months = Date_Calc :: getMonthNames();
while ( list ( $id , $name ) = each ( $months )){
if ( ereg ( $month , strtolower ( $name ))){
return ( $id );
}
}
return ( 0 );
} 
// ****************************************************************
//得到月短名稱
function getMonthAbbrname( $month , $length = 3 )
{
$month = strtolower ($month );
$months = Date_Calc :: getMonthShortNames();
while ( list ( $id , $name ) = each ( $months )){
if ( ereg ( $month , strtolower ( $name ))){
return ( $id );
}
}
return ( 0 );
} // end func getMonthAbbrname
//****************************************************************
//得到星期短名稱
function getWeekdayFullname( $day = "" , $month = "" , $year = "" )
{
if ( empty ( $year ))
$year = Date_Calc :: dateNow( " %Y " );
if( empty ( $month ))
$month = Date_Calc :: dateNow( " %m " );
if ( empty ( $day ))
$day = Date_Calc :: dateNow( " %d " );
$weekday_names = Date_Calc :: getWeekDays();
$weekday = Date_Calc :: dayOfWeek( $day , $month , $year );
return $weekday_names [ $weekday ];
} // end func getWeekdayFullname
//****************************************************************
//得到星期短名稱
function getWeekdayAbbrname( $day = "" , $month = "" , $year = "" , $length = 3 )
{
if ( empty ( $year ))
$year = Date_Calc ::dateNow( " %Y " );
if ( empty ( $month ))
$month = Date_Calc :: dateNow( " %m " );
if ( empty ( $day ))
$day = Date_Calc :: dateNow( " %d " );
$weekday_names = Date_Calc :: getShortWeekDays();
$weekday = Date_Calc :: dayOfWeek( $day , $month , $year );
return $weekday_names [ $weekday ];
} // end func getWeekdayAbbrname

以上幾個函數有的是修改的,有的是新加的,由於較多,不再一一詳細說明。思路是明顯的,就是通過對日期列表的的映射獲得相應語言名稱。有興趣的朋友可能進行補充和更正。
3)修改日期顯示的亂碼處,在modules/calendar/calendar.class.php文件把function _drawDays()函數中
foreach ( $wk as $day ) {
$s .= " <th width="14%"> " . htmlentities ( utf8_encode ( $day ) , ENT_COMPAT , $locale_char_set ) . " </th> " ;
} utf8_encode函數去掉修改為
foreach ( $wk as $day ) {
$s .= " <th width="14%"> " . htmlentities ( $day , ENT_COMPAT , $locale_char_set ) . " </th> " ;
}完整的函數如下:
// ***************************************************************
function_drawDays() {
global $locale_char_set ;
$bow = Date_Calc :: beginOfWeek( null , null , null , null , LOCALE_FIRST_DAY );
$y = substr ( $bow , 0 , 4 );
$m = substr ( $bow , 4 , 2 );
$d= substr ( $bow , 6 , 2 );
$wk = Date_Calc :: getCalendarWeek( $d , $m , $y , " %a " , LOCALE_FIRST_DAY );
$s = $this -> showWeek ? " <th> </th> " : "" ;
foreach ( $wk as $day ) {
$s .= " <th width="14%"> " . htmlentities ( $day , ENT_COMPAT , $locale_char_set ) . " </th> " ;
}
return " <tr>$s </tr> " ;
}
// ****************************************************************

修改文件module/tasks/ae_dates.php
function cal_work_day_conv( $val ) {
GLOBAL $locale_char_set ;
$wk = Date_Calc :: getCalendarWeek( null , null , null , " %a " , LOCALE_FIRST_DAY );
$day_name = $wk [( $val - LOCALE_FIRST_DAY) % 7 ];
// 把utf8_encode調用處註釋,不進行編碼轉換
/*
if ($locale_char_set == "utf-8" && function_exists("utf8_encode")) {
$day_name = utf8_encode($day_name);
}
*/
return htmlentities ( $day_name , ENT_COMPAT , $locale_char_set );
}

以上是把顯示日期名稱時的utf8_encode轉碼去掉,因為讀到的日期名稱本來就是utf-8編碼,類似的地方可能還有,如果找到都要去掉。
3. 修正在中文下Gantt圖的亂碼問題
DotProject的圖形模塊使用了jpgraph。JpGraph是PHP專門進行繪製圖表的類庫。它使得作圖變成了一件非常簡單的事情,你只需從數據庫中取出相關數據,定義標題,圖表類型,然後的事情就交給JpGraph,只需掌握為數不多的JpGraph內置函數(可以參照JpGraph附帶例子學習),就可以畫出非常炫目的圖表!
JpGraph要求PHP版本為4.04以上,並且支持GD庫且GD庫的版本應為2.0,而不是1.0。JpGraph有PHP4和PHP5兩種版本(由於我的環境是PHP5,所以下載了最新PHP5版本,在附件中修改過的DotProject包含這個版本,請使用PHP4更換相應的版本)。
Gantt圖的亂碼問題的在於jpgraph中沒有對中文及其他語言文字處理好。
1)修改jpgraph配置文件
新建字體文件夾和修改lib/jpgraph/src/jpg-config.inc.php文件,在lib/jpgraph路徑新建fonts文件夾,把所要的字庫複製到該文件夾下。
在文件lib/jpgraph/src/jpg-config.inc.php中加入如下語句(或把相應的註釋去掉後修改)
DEFINE ( ' TTF_DIR ' , ' ./lib/jpgraph/fonts/ ' ); // 設置jpgraphTTF(字體)文件夾
DEFINE ( ' SIMSUN_TTF_FONT ' , ' mingliu.ttc ' ); // 使用'mingliu.ttc'(windows下的細明體)
DEFINE ( ' CHINESE_TTF_FONT ' , ' mingliu.ttc ' ); 2)修改文件module/tasks/gantt.php和module/projects/gantt.php
新版在繪製Gantt圖時報錯:You are trying to use the locale (%s) which your PHP installation does not support. Hint: Use ‘ ’ to indicate the default locale for this geographic region.
這是由於jpgraph沒有加入選定的日期格式如'chs',可修改SetDateLocale處如下:
$jpLocale = dPgetConfig( ' jpLocale ' );
if ( $jpLocale ) {
$graph -> scale -> SetDateLocale( $jpLocale );
}
else {
$graph -> scale -> SetDateLocale( $AppUI -> user_lang[ 0 ] ); // 第一個估計會有或註釋掉和設為 ‘ ’
}
Gantt圖繪製分兩部分,一部分是由DotProject生成的項目管理的標題等,一部分是用戶業務產生的內容區部分如項目和任務名稱。對於第一部分繪製字體編碼保持和DotProject一致。
在語言包文件夾下locales.php文件中加入如下定義(本例是簡體中文)
$LOCALE_FONT=30;
30是在jpgraph中定義的語言字體標識(如中文為DEFINE("FF_SIMSUN",30);),詳見jpgraph.php文件。 這樣在繪製標題部分取FF_SIMSUN索引的字體。
在文件module/tasks/gantt.php和module/projects/gantt.php中定義當前標題要使用的字體,加入如下語句:
if ( ! empty ( $LOCALE_FONT )){
define ( " CRURRENT_FONT " , $LOCALE_FONT );
}
else {
define ( " CRURRENT_FONT " , FF_ARIAL);
}
這樣在設定字體的地方設定CRURRENT_FONT就可以了。把
//$graph->scale->actinfo->SetFont(FF_ARIAL);改為
$graph->scale->actinfo->SetFont(CRURRENT_FONT, FS_NORMAL, 10);//標題信息
找到
if (is_file( TTF_DIR."arialbd.ttf" ))
$graph->scale->tableTitle->SetFont(FF_ARIAL,FS_BOLD,12); 改為
$graph->scale->tableTitle->SetFont(CRURRENT_FONT, FS_NORMAL, 11);//標題頭
在最後
$vline = new GanttVLine($today, $AppUI->_('Today', UI_OUTPUT_RAW));語句後插入如下語句:
$vline->title->SetFont(CRURRENT_FONT, FS_NORMAL, 10);//顯示today(今天)
這樣繪製標題部分就修改完畢。
第二部分內容區則要根據要繪製的文字編碼動態設定字體。因此在module/tasks/gantt.php和module/projects/gantt.php文件中加入判斷字符在什麼語言區返回相應的字體(根據utf-8)本例只實現中文區,可以有不對地方,望大家指正。
// utf-8 region segment 一-鿿
function GetutfTTF( $str )
{
if ( preg_match ( " /^([ " . chr ( 228 ) . " - " . chr ( 233 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . "]{1}){1}/ " , $word ) == true || preg_match ( " /([ " . chr ( 228 ) . " - " . chr ( 233 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . " ]{1}){1}$/ " , $word ) == true || preg_match ( " /([ " . chr ( 228 ) . " - " . chr ( 233 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . " ]{1}[ " . chr ( 128 ) . " - " . chr ( 191 ) . " ]{1}){2,}/ " , $str ) == true )
{
return (FF_CHINESE); // 返回中文字體標識FF_CHINESE
}
return (FF_ARIAL); // 返回默認字體標識FF_ARIAL
}
然後在畫gantt圖相應的項目和任務要顯示的名稱判斷是否在中文,設置對應的字體,如:
$bar->title->SetFont(GetutfTTF($name), FS_NORMAL, 10);
$bar2->title->SetFont(GetutfTTF($t["task_name"]), FS_NORMAL, 10);
…
所有$bar繪製都屬於內容區,都把相應部分用如上方法設定字體。繪製Gantt圖部分就修改完畢,看看效果:
英文:

中文:

4. 其他補充說明
以上所有的編碼都設定為utf-8,因此建議把數據庫的服務器端和客戶端字符集都改為utf-8。Mysql修改配置文件my.cnf或my.ini即可。
如phpmyadmin瀏覽DotProject的mysql數據庫,設定為”中文-Chinese Simplified(utf-8)”,如果發現在亂碼,請在includes/db_adodb.php文件中function db_connect()函數加上
$db->Query("Set Names 'utf8'");
這樣字符編碼就和phpmyadmin保持一致,用phpmyadmin瀏覽數據就沒有亂碼了。
後話:
本文雖然是解決亂碼問題,其中包含軟件國際化思想。在軟件支持多語言文字時,可以分為兩部分,一部分軟件本身通過配置有多種語言版本,一部分就是支持不同語言文字的處理。就像瀏覽器,雖然各種語言版本,但可以正常瀏覽不同語言的網頁。這主要是由於採用了統一編碼utf8-unicode(大多採用此種編碼)。可以預見,不久亂碼問題由於都採用統一編碼將不復存在。軟件的多語言只是軟件國際化第一步,中國軟件業國際化任重道遠。
在附件中是由DotProject2.1rc版修改過的壓縮文件,並加入了最新的JpGraph for PHP5版本。有興趣的朋友可以下載大家共同研究。(由於不能上傳附件,需要的朋友可以留下email地址)
原文出處:
DotProject中文乱码解决总结 - (Andrew)的专栏 - 博客频道 - CSDN.NET
