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
'
,
'
simsun.ttc
'
);
//
使用'simsun.ttc'(windows下的宋体)
DEFINE
(
'
CHINESE_TTF_FONT
'
,
'
simsun.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在Windows XP环境下遇到的中文乱码问题,包括日历、Gantt图的乱码。主要涉及修改语言包、日期格式、JPGraph配置以及数据库字符集等,以实现中文环境下的正常显示。

2159

被折叠的 条评论
为什么被折叠?



