通常一个Yii的app目录结构如下:
app
----assets
----images
----css
----protected
--------commands
--------components
--------config
--------controllers
--------extensions
--------models
--------modules
--------views
----themes
--------classic
------------views
----------------layout
----------------system
----------------site
Theme是Yii系统支持的。当设置了Yii::app()->theme="xxxx"的时候,app根目录下的themes中的相应的theme就会起作用。比如设置Yii::app()->theme="classic",那么themes/classic/views就会替代protected/views作为视图资源存放的地方,除非视图文件不存在,才会启用protected/views下的视图文件。
Yii内置了翻译器,可以使用Yii::t()来进行翻译,这个可以通过Yii::app()->language来控制目标语言。
Module是一个模块,包含了controller、model、view。
在原生的Yii系统里是支持Theme和Module结合。比如系统里建立一个Module名字为test,在protected/modules下结构如下:
protected
----modules
--------test
------------components
------------controllers
------------models
------------views
----------------default
----------------layouts
------------TestModule.php
这个时候设置了Yii::app()->theme="classic",就可以将protected/modules/test/views复制到themes/classic/views/下,并起名为test。
但是如果要再结合language做i18n支持,就不是那么容易了。首先将i18n相关语言使用Yii::t()来翻译,本身是一个效率低下的事情;其次,因为语言不同,势必会影响到界面排版,所以用一个view文件来控制就显得捉襟见肘。
解决的思路是在controller解析view文件路径的时候,加上language标识。优先级是先找themes下带language的路经,如果没有则找themes下不带language的路径,如果没有则找modules下的views的带language的路径,如果没有则找modules下的views的不带language的路径。
我的做法如下:
1、修改CController.php
public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null) { if(empty($viewName)) return false; if($moduleViewPath===null) $moduleViewPath=$basePath; if(($renderer=Yii::app()->getViewRenderer())!==null) $extension=$renderer->fileExtension; else $extension='.php'; if($viewName[0]==='/') { if(strncmp($viewName,'//',2)===0) $viewFile=$basePath.$viewName; else $viewFile=$moduleViewPath.$viewName; } else if(strpos($viewName,'.')) $viewFile=Yii::getPathOfAlias($viewName); else { $viewFileBak = $viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName; if (isset(Yii::app()->language)) { $viewFile=$viewPath.DIRECTORY_SEPARATOR.Yii::app()->language.DIRECTORY_SEPARATOR.$viewName; } } if(is_file($viewFile.$extension)) return Yii::app()->findLocalizedFile($viewFile.$extension); else if($extension!=='.php' && is_file($viewFile.'.php')) return Yii::app()->findLocalizedFile($viewFile.'.php'); else { if (isset($viewFileBak)) { if(is_file($viewFileBak.$extension)) return Yii::app()->findLocalizedFile($viewFileBak.$extension); else if($extension!=='.php' && is_file($viewFileBak.'.php')) return Yii::app()->findLocalizedFile($viewFileBak.'.php'); } } return false; }
2、在protected/components里新增TLController.php
/** * Abstract controller class for Theme&Language Controller.<br> * @author henry * */ abstract class TLController extends CController { /** * @var array context menu items. This property will be assigned to {@link CMenu::items}. */ public $menu=array(); /** * @var array the breadcrumbs of the current page. The value of this property will * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links} * for more details on how to specify this property. */ public $breadcrumbs=array(); public function init() { parent::init(); if ($this->hasEventhandler('onControllerInit')) { $this->onControllerInit(new CEvent($this)); } //$this->layout = '/layouts/'.Yii::app()->language.'/column1'; } /** * Supported befaviors * @return array */ public function behaviors() { return array_merge(parent::behaviors(), array( 'themeBehavior'=>array( 'class'=>'application.behaviors.ThemeBehavior', ), 'langBehavior'=>array( 'class'=>'application.behaviors.LangBehavior', ), )); } public function onControllerInit($event) { $this->raiseEvent('onControllerInit', $event); } }
3、在protected/behaviors增加2个Behavior:
//ThemeBehavior.php class ThemeBehavior extends CBehavior { const COOKIE_KEY = '__theme'; public function events() { return array_merge(parent::events(), array( 'onControllerInit'=>'controllerInit', )); } public function controllerInit($event) { $v = Yii::app()->request->getParam(self::COOKIE_KEY); if (!isset($v)) { $v = Yii::app()->request->cookies[self::COOKIE_KEY]; if (!isset($v)) { $v = Yii::app()->theme->name; } else { $v = $v->value; } } Yii::app()->theme = $v; Yii::app()->request->cookies[self::COOKIE_KEY] = new CHttpCookie(self::COOKIE_KEY, $v); } }
//LangBehavior.php class LangBehavior extends CBehavior { const COOKIE_KEY = '__lang'; private $_lang = ''; public function events() { return array_merge(parent::events(), array( 'onControllerInit'=>'controllerInit', )); } public function controllerInit($event) { $v = Yii::app()->request->getParam(self::COOKIE_KEY); if (!isset($v)) { $v = Yii::app()->request->cookies[self::COOKIE_KEY]; if (!isset($v)) { $v = Yii::app()->language; } else { $v = $v->value; } } Yii::app()->language = $v; Yii::app()->request->cookies[self::COOKIE_KEY] = new CHttpCookie(self::COOKIE_KEY, $v); } }