Маршруты перевода на основе URL Zend Framework

Я пытаюсь реализовать перевод на основе URL-адресов в Zend Framework, чтобы мой сайт был оптимизирован для SEO. Это означает, что мне нужны URL-адреса, подобные приведенным ниже, в дополнение к маршрутам по умолчанию.

zend.local/en/module
zend.local/en/controller
zend.local/en/module/controller
zend.local/en/controller/action

Вышеупомянутые - это те, с которыми у меня сейчас проблемы; остальное должно быть в порядке. Я добавил плагин контроллера, который выбирает параметр lang, чтобы я мог установить языковой стандарт и объект перевода в методе preDispatch. Вот некоторые из моих маршрутов (хранятся в файле .ini):

; Language + module
; Language + controller
resources.router.routes.lang1.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang1.route = "(^[a-zA-Z]{2})/(\w+$)"
resources.router.routes.lang1.defaults.controller = index
resources.router.routes.lang1.defaults.action = index
resources.router.routes.lang1.map.1 = "lang"
resources.router.routes.lang1.map.2 = "module"


; Language + module + controller
; Language + controller + action
resources.router.routes.lang2.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.lang2.route = "(^[a-zA-Z]{2})/(\w+)/(\w+$)"
resources.router.routes.lang2.defaults.module = default
resources.router.routes.lang2.defaults.action = index
resources.router.routes.lang2.map.1 = "lang"
resources.router.routes.lang2.map.2 = "controller"
resources.router.routes.lang2.map.3 = "action"

Как указано в комментариях, несколько структур URL-адресов будут соответствовать одному и тому же маршруту, что заставляет мое приложение неправильно интерпретировать формат. Например, следующие два URL-адреса будут сопоставлены маршрутом lang1:

zend.local/en/mymodule
zend.local/en/mycontroller

В первом URL-адресе "mymodule" используется как имя модуля, что является правильным. Однако во втором URL-адресе «mycontroller» используется как имя модуля, чего я не хочу. Здесь я хочу, чтобы он использовал модуль «по умолчанию» и «mycontroller» в качестве контроллера. То же самое относится к предыдущему маршруту lang2. Поэтому я не знаю, как отличить URL-адрес от структуры /en/module или /en/controller.

Чтобы исправить это, я поэкспериментировал с приведенным ниже кодом в моем плагине контроллера.

// Get module names as array
$dirs = Zend_Controller_Front::getInstance()->getControllerDirectory();
$modules = array_keys($dirs);

// Module variable contains a module that does not exist
if (!in_array($request->getModuleName(), $modules)) {
   // Try to use it as controller name instead
   $request->setControllerName($request->getModuleName());
   $request->setModuleName('default');
}

Это отлично работает в сценариях, которые я тестировал, но тогда мне пришлось бы сделать что-то подобное, чтобы заставить работать lang2 маршрут (который, возможно, включает сканирование каталогов для получения списка контроллеров). Это просто кажется плохим решением, поэтому, если это возможно, я хотел бы выполнить все это только с помощью маршрутов (или простого кода, который не является таким «хакерским»). Я мог бы также составлять маршруты каждый раз, когда хочу /en/controller, например, но это компромисс, на который я бы не пошел. Итак, если кто-то знает, как решить эту проблему, или знает другой подход к достижению того же результата, я все слышу!


person ba0708    schedule 29.08.2012    source источник
comment
У него всегда будет язык в URI? Я имею в виду, что нет языка по умолчанию для чего-то вроде localhost/module/controller = deafult-lang = en   -  person Keyne Viana    schedule 04.09.2012
comment
@Keyne Если в URL-адресе не указан язык, в моем подключаемом модуле контроллера будет установлен язык по умолчанию. В этом случае используются маршруты по умолчанию, и все должно работать.   -  person ba0708    schedule 04.09.2012
comment
Я отправил ответ, но не уверен насчет вашего lang2 маршрута. В своем ответе я только что добавил часть lang в один маршрут и позволил маршрутам модуля по умолчанию выполнять свою работу. Сообщите мне, правильно ли я понял.   -  person Keyne Viana    schedule 04.09.2012


Ответы (1)


Я воспроизвел вашу проблему здесь и сделал следующее (правда, не используя файлы конфигурации):

Маршрутизатор

/**
 * Initializes the router
 * @return Zend_Controller_Router_Interface
 */
protected function _initRouter() {
    $locale = Zend_Registry::get('Zend_Locale');

    $routeLang = new Zend_Controller_Router_Route(
        ':lang',
        array(
        'lang' => $locale->getLanguage()
        ),
        array('lang' => '[a-z]{2}_?([a-z]{2})?')
    );

    $frontController  = Zend_Controller_Front::getInstance();
    $router = $frontController->getRouter();

    // Instantiate default module route
    $routeDefault = new Zend_Controller_Router_Route_Module(
        array(),
        $frontController->getDispatcher(),
        $frontController->getRequest()
    );

    // Chain it with language route
    $routeLangDefault = $routeLang->chain($routeDefault);

    // Add both language route chained with default route and
    // plain language route
    $router->addRoute('default', $routeLangDefault);

    // Register plugin to handle language changes
    $frontController->registerPlugin(new Plugin_Language());

    return $router;
}

Плагин

/**
 * Language controller plugin
 */
class Plugin_Language extends Zend_Controller_Plugin_Abstract
{
    /**
     * @var array The available languages
     */
    private $languages = array('en', 'pt');

    /**
     * Check the URI before starting the route process
     * @param Zend_Controller_Request_Abstract $request
     */
    public function routeStartup(Zend_Controller_Request_Abstract $request)
    {
        $translate = Zend_Registry::get('Zend_Translate');
        $lang = $translate->getLocale();

        // Extracts the URI (part of the URL after the project public folder)
        $uri = str_replace($request->getBaseUrl() . '/', '', $request->getRequestUri());
        $langParam = substr($uri, 0, 3);

        // Fix: Checks if the language was specified (if not, set it on the URI)
        if((isset($langParam[2]) && $langParam[2] !== '/') || !in_array(substr($langParam, 0, 2), $this->languages)) { {
            $request->setRequestUri($request->getBaseUrl() . '/' . $lang . "/" . $uri);
            $request->setParam('lang', $lang);
        }
    }
}

По сути, есть цепочка маршрутов для применения языковых настроек в маршруте модуля по умолчанию. В качестве исправления мы гарантируем, что URI будет содержать язык, если пользователь его пропустил.

Вам нужно будет адаптировать его, так как я использую Zend_Registry::get('Zend_Locale') и Zend_Registry::get('Zend_Translate'). Измените его на настоящие ключи в вашем приложении.

Что касается маршрута lang: [a-z]{2}_?([a-z]{2})?, он позволит использовать такие языки, как мой: pt_BR

Сообщите мне, сработало ли это для вас.

person Keyne Viana    schedule 03.09.2012
comment
Спасибо за ваш ответ. Надеюсь, вы не против, что я написал свой комментарий о pastebin; Я счел невозможным уместить такое небольшое количество персонажей в правильное объяснение. См. мой комментарий здесь. - person ba0708; 04.09.2012
comment
Нет проблем, я читал. Часть, в которой мы включаем параметр языка в URL-адрес (если не указан), прозрачна для конечного пользователя. Точно так же, как если бы это было необязательно (оно нам нужно, чтобы оно работало). Пользователь не увидит, что он вводится, URL-адрес в браузере останется прежним. IIRC, есть только две недостающие части: одна - переопределить язык Zend_Locale, а другая - перевести этот код в файл конфигурации. Понятно? - person Keyne Viana; 05.09.2012
comment
Вроде здесь все исправно работает. Я выясню, как поместить это в файл конфигурации, и опубликую как можно скорее. - person Keyne Viana; 05.09.2012
comment
Раньше у меня были простые настраиваемые маршруты, такие как /register, который по умолчанию был бы /default/user/register. Эти маршруты хранятся в файле конфигурации. Однако с новыми языковыми маршрутами это больше не работает. Я подозревал, что это как-то связано с порядком загрузки маршрутов. Добавление маршрутов в начальную загрузку после ваших маршрутов заставит его работать если я изменю его на /:lang/register с требованием 'lang' => '[a-z]{2}_?([a-z]{2})?' (потому что теперь все запросы будут иметь язык). Есть идеи, как я могу избежать этого для всех моих маршрутов и использовать конфигурацию? - person ba0708; 07.09.2012
comment
Вы достигли этого? Все еще ищете помощи? - person Keyne Viana; 09.09.2012
comment
Я еще не нашел решения для моего последнего комментария выше, поэтому, если у вас есть какие-либо идеи, я был бы очень признателен. - person ba0708; 09.09.2012