Symfony学习笔记之翻译组件-----translation总结

2023-05-16

        “过际化”(internationalization,常被简写为i18n),是指将字符串和其他一些具有区域特征片段,从你的程序中提取(abstract)出来,并基于用户所在区域(比如语言、国家)而将其置于一个能被翻译和转化的的过程。对于文本来说(text),这意味着用一个能够把它(或“信息”)翻译成用户所需语言的函数,来剥离文本的每一部分:

// 文本始终以英语输出
echo 'Hello World';
 
// 文本将以用户指定语言或默认英语输出
echo $translator->trans('Hello World');

        locale的意思,单纯来说就是用户的语言和国家。在程序中它可以是任何一个字符串,用来管理和翻译(translation)其他格式信息(比如币种)。推荐使用以 ISO 639-1 语言代码,加一个下划线(_),再跟一个 ISO 3166-1 alpha-2 国家代码(比如fr_FR这个locale是指“法国法语”French/France)。

        在本章中,你将学习如何使用Symfony框架中的translation组件。你可以阅读翻译组件来了解更多。整体上,翻译的过程有如下几步:

  1. 开启和配置Symfony的翻译服务;

  2. 将字符串抽象出来(如“xxxx”),这是通过调用Translator去剥离它们来实现的 (参考 翻译基础);

  3. 针对每个被支持的locale,创建翻译资源/文件,用于翻译程序中每一个待译字串;

  4. 针对request(请求)和可选的 基于用户整个session过程,来 确定、设置和管理用户的locale信息。

一、配置 

        翻译的过程,是通过translator服务来处理的。该服务使用用户指定的locale,来查找并返回翻译过的信息。使用translator之前,在配置文件中开启它:

# app/config/config.yml
framework:
    translator: { fallbacks: [en] }

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:framework="http://symfony.com/schema/dic/symfony"
           xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/symfony
        http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
 
    <framework:config>
        <framework:translator>
            <framework:fallback>en</framework:fallback>
        </framework:translator>
    </framework:config>
</container>

// app/config/config.php
$container->loadFromExtension('framework', array(
                'translator' => array('fallbacks' => array('en')),
));

        关于 fallbacks关键字 以及Symfony在找不到翻译语种时该如何处理,请参考接下来的 翻译时的Locales回滚 以了解细节。

翻译时用到的locale信息,被存于request对象中。一般在路由中被设为 _locale 属性。请参考 The Locale and the URL。、

二、翻译基础

  translator 服务负责完成对文本的翻译。为了翻译一个文本块(被称为“message”,以下称作“信息”),使用trans()方法。例如,你要在controller中翻译一个简单的信息:

// ...
use Symfony\Component\HttpFoundation\Response;
 
public function indexAction()
{
$translated = $this->get('translator')->trans('Symfony is great');
 
return new Response($translated);
}

        上述代码被执行后,Symfony就尝试基于用户的 locale 来翻译 “Symfony is great”信息。为了让这个过程实现,你需要告诉Symfony如何通过一个“翻译源(translation resource)”来执行翻译。一般来说翻译源是一个文件,包含有成组的翻译信息,对应某一指定的locale。它就像个翻译时的“字典”,可被创建为多种格式,但推荐使用XLIFF(译注:就是后缀不同的xml格式):

XML文件

<!-- messages.fr.xlf -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" datatype="plaintext" original="file.ext">
        <body>
        <trans-unit id="symfony_is_great">
            <source>Symfony is great</source>
            <target>J'aime Symfony</target>
        </trans-unit>
        </body>
    </file>
</xliff>

YAML文件

# messages.fr.yml
Symfony is great: J'aime Symfony

PHP文件

// messages.fr.php
return array(
'Symfony is great' => 'J\'aime Symfony',
);

        关于这类文件的存放位置等信息,请参考接下来的 翻译源/文件的命名和位置。

        现在,如果用户的locale是法语(比如 fr_FR 或 fr_BE ),那么前面的信息将被翻译为 J'aime Symfony。你也可以在参考接下来的 模板中的翻译(templates) 。

三、翻译的处理过程

        为了能翻译一条信息,Symfony将执行以下简明流程:

  • 先确定request对象中所存储的当前用户的 locale 信息;

  • 然后从由 locale(比如 fr_FR )所决定的翻译源中,加载该翻译源目录下已经翻译好的信息。如果locale不存在,则使用由 fallback locale 所决定的翻译信息。最终结果,将生成了一个“翻译大词典”;

  • 如果能够从目录中找到待翻信息,翻译结果将被返回。否则, translator返回原始信息。

        当使用trans()方法时,Symfony从对应的信息目录中,寻找准确的字符串,然后返回它(如果存在的话)。

四、信息占位符

        有时,一条包含变量的信息,也需要被翻译:

use Symfony\Component\HttpFoundation\Response;
 
public function indexAction($name)
{
$translated = $this->get('translator')->trans('Hello '.$name);
 
return new Response($translated);
}

        可是,对于这样一个字符串,创建相应的翻译是不可能的,因为translator始终在尝试寻找“确定信息”,包括变量值本身(比如“Hello Ryan”和“Hello Fabien”在translator看来是两条不同的信息)。其实,我们不必为 $name 变量编写每一种可能的翻译,你只需要在待译信息和翻译信息中同时使用相同的 "占位符" ,翻译时就会将 “占位符” 替换为此 “变量” 。代码如下:

...

$translated = $this->get('translator')->trans( 'Hello %name%',
                                                array('%name%' => $name)
                                        );
...

        Symfony将查找原始信息(Hello %name%)的翻译,然后用它们的值来替换占位符。创建翻译时仍和以前一样:

<!-- messages.fr.xlf -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" datatype="plaintext" original="file.ext">
        <body>
            <trans-unit id="1">
                <source>Hello %name%</source>
                <target>Bonjour %name%</target>
            </trans-unit>
        </body>
    </file>
</xliff>

// messages.fr.php
return array(
    'Hello %name%' => 'Bonjour %name%',
);

# messages.fr.yml
'Hello %name%': Bonjour %name%

        同样情况在模板中的处理方法,请参考接下来的 模板中的翻译之Twig Templates。

五、复数处理

(一)隐式的复数

        另一个复杂场面,则是你在翻译中要面对基于某些变量的“复数状况”:

There is one apple.
There are 5 apples.

        要处理这种特殊情况,可以使用 transChoice() 方法(模板中使用transchoice标签/过滤器,可参考接下来的 模板中的翻译)。解决方法如下例所示:

        例如,这里有一个俄语复数规则的数学呈现:

(($number % 10 == 1) && ($number % 100 != 11))
    ? 0
    : ((($number % 10 >= 2)
        && ($number % 10 <= 4)
        && (($number % 100 < 10)
        || ($number % 100 >= 20)))
            ? 1
            : 2
);

        你已看到,在俄语中,可以有三种不同的复数形式,每一种的索引是0,1,2。对于每种形式,复数形态不同,所以翻译也是不同的。

        当翻译因复数而有不同的形式时,你可以通过一个由pipe(|)分隔的字符串,来提供出全部的形式。

'There is one apple|There are %count% apples'

        要翻译成复数信息,使用 transChoice() 方法:

$translator->transChoice(
    'There is one apple|There are %count% apples',
    10,
    array('%count%' => 10)
);

        第二个参数 (本例是 10) 是被描述对象的 number(数量),用于决定要使用哪种翻译,同时还要装载 %count% 占位符。

        根据给定的数字,translator 会选择正确的复数形式。在英文中,当仅有一个物体时,多数单词只有一个单数形式,而其复数形式可以是所有其他数字 (0, 2, 3...)。因此,如果 count 是 1,translator 将使用第一个字符串 (There is one apple) 作为翻译。否则它就使用 There are %count% apples

        这里有一个法语翻译:

'Il y a %count% pomme|Il y a %count% pommes'

        就算字符串看起来(和英语的)很相似 (它由两个通过pipe分隔的子串构成),法语的规则却不同: 第一种形式 (没有复数) 用于当 count 是 0 或 1。因此,当 count 是 0 或 1 时,translator将自动使用第一个字符串 (Il y a %count% pomme)。

        每一种locale有其自己的规则集,其中某些甚至有高达六种不同的复数形式,配合着背后的复杂规则,即哪个数字映射的是哪种复数形式。对于英语和法语来说,规则十分简单,但对于俄语,你可能不太想去搞清哪个规则对应哪个字符串。要帮助translators,你可以可选地对每个字符串“打标签”:

'one: There is one apple|some: There are %count% apples'
 
'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'

        标签(tag)真的是只能对translator进行提示而不会影响到“用于决定使用哪种复数形式”的规则。标签可以是任何描述性的字符串,它以冒号(:)结尾。标签并不需要和原始的待译信息相同,它们是可选的,translator并不使用它们(translator只根据tag在字符串中的位置来获取字符串)。

(二)显式的区间复数

        对message(待译信息)进行复数处理的最简单方式就是,让translator使用内部逻辑,基于给定的数字,来决定要使用哪个字符串。有时,你会需要更多的控制权,或者希望在个别情况下(例如对于0或负值的count)使用不同的翻译。对于这类场景,你可以使用显式的算术区间:

'{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples'

        区间(interval)遵循的是 ISO 31-11 注释。上面的字符串指定了四个区间: 确切的 0, 确切的 12-19, 和 20 以及更高。

        你也可以显式地把 算术规则(math rules)和 标准规则 混合起来指定。本例中,如果count没有匹配到一个特定区间,标准规则(standard rules)将在移除显式规则之后生效:

'{0} There are no apples|[20,Inf[ There are many apples|There is one apple|a_few: There are %count% apples'

        例如,对于 1 个苹果,标准规则 There is one apple 将被使用。对于 2-19 个苹果,第二个标准规则 There are %count% apples 将被使用。

        一个 Interval 可以呈现出无限的数字组合: {1,2,3,4} 或者是两个数字之外的数字:

[1, +Inf[
]-1,2[

        左边的分隔符可以是 [ (inclusive/包括) 或 ] (exclusive/排除)。 右边的分隔符可以是 [ (exclusive/排除) 或 ] (inclusive/包括)。除去数字,你还可以使用 -Inf 和 +Inf 来表达无限。

六、模板中的翻译

        大多数情况下,翻译发生在模板中,对于Twig和PHP模板,Symfony提供了原生支持。

(一)Twig模板 

        Symfony提供了特殊的Twig标签trans 和 transchoice),用于对“静态文本块”信息提供翻译帮助。

{% trans %}Hello %name%{% endtrans %}
 
{% transchoice count %}
{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples
{% endtranschoice %}

        使用 transchoice标签,自动地从当前上下文关系中得到 %count%变量,并将其传给translator。这种机制,只在你使用 %var%这种格式的占位符时生效。

        如果你需要在字符串中使用百分号%,要写两次来为它转义:

{% trans %}Percent: %percent%%%{% endtrans %}

        你也可以指定 信息域(message domain),并传递一些附加的变量进来:

{% trans with {'%name%': 'Fabien'} from "app" %}Hello %name%{% endtrans %}
 
{% trans with {'%name%': 'Fabien'} from "app" into "fr" %}Hello %name%{% endtrans %}
 
{% transchoice count with {'%name%': 'Fabien'} from "app" %}
{0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples
{% endtranschoice %}

        使用 trans 和 transchoice 过滤器,可用于翻译变量文本和复杂表达式:

{{ message|trans }}
 
{{ message|transchoice(5) }}
 
{{ message|trans({'%name%': 'Fabien'}, "app") }}
 
{{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }}

        在翻译时无论使用标签还是过滤器,效果是相同的,但有一个微小区别:自动输出转义功能 仅对过滤器有效。换言之,如果你需要翻译出来的信息“不被转义”,则必须在translation调节器后面再跟一个raw过滤器:

1
2
3
4
5
6
7
8
9
10
{# 标签中被翻译的文本从不被转义#}
{% trans %}
<h3>foo</h3>
{% endtrans %}
 
{% set message = '<h3>foo</h3>' %}
 
{# 变量调节器翻译的字符串和变量,默认将被转义 #}
{{ message|trans|raw }}
{{ '<h3>bar</h3>'|trans|raw }}

        你可以通过单一标签,为整个twig模板设置一个翻译域(translation domain):

1
{% trans_default_domain "app" %}

        注意这时仅会影响到当前模板,而不包括任何“被包容(included)”的模板(为的是减少副作用)。

(二)PHP模板

        translator也可以在PHP模板中,通过translator helper来使用:

<?php echo $view['translator']->trans('Symfony is great') ?>
 
<?php echo $view['translator']->transChoice(
'{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
10,
array('%count%' => 10)
) ?>

(三)翻译源/文件的命名和位置 

Symfony在以下位置寻找信息文件(即是translations/翻译信息):

  • app/Resources/translations 目录;

  • app/Resources/<bundle name>/translations 目录;

  • 任何bundle下的 Resources/translations/ 目录

        上面的位置,是按照“优先权由高至低”的顺序排列的。这意味着,你可以用前面两个目录之一,来覆写某个bundle中的翻译信息。

        覆写机制,基于键等级(key level)而执行:只有被覆写的键,才需要被列在高优先级的信息文件中。当一个键没有在信息文件中被找到时,translator将自动回滚到低优先级的信息文件中。

        信息文件的文件名也很重要,每一个信息文件必须按下列命名路径来命名domain.locale.loader

  • domain: 这是一个可选项,用于组织信息文件成为群组(例如admin, navigation 或default messages)。参阅 使用翻译信息的域 Using Message Domains;

  • locale: 这是翻译信息的locale (例如 en_GB, en, 等等);

  • loader: 这是Symfony如何来加载和解析信息文件 的加载器(也就是 xlfphpyml 等文件后缀).

        加载器(loader)可以是任何已注册加载器的名称。Symfony默认提供了许多加载器,包括:

  • xlf: 加载XLIFF文件;

  • php: 加载PHP文件;

  • yml: 加载YAML文件;

       使用何种加载器的选择权完全在你,随你喜好。推荐使用 xlf 作为翻译的信息文件。更多选择,参考 加载信息目录 Loading Message Catalogs。

        你也可以将翻译信息存在数据库中,或任何其他介质,只要提供一个自定义的类去实现 LoaderInterface 接口即可。参考 translation.loader 标签,以了解更多。

        你可以在配置文件中通过paths选项添加一个目录:

# app/config/config.yml
framework:
translator:
paths:
- '%kernel.root_dir%/../translations'

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:framework="http://symfony.com/schema/dic/symfony"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services
    http://symfony.com/schema/dic/services/services-1.0.xsd
    http://symfony.com/schema/dic/symfony
    http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
>
 
    <framework:config>
        <framework:translator>
            <framework:path>%kernel.root_dir%/../translations</framework:path>
        </framework:translator>
    </framework:config>
</container>

// app/config/config.php
$container->loadFromExtension('framework', array(
'translator' => array(
'paths' => array(
'%kernel.root_dir%/../translations',
),
),
));

        每次你创建一个新的翻译源(translation resource),或安装了一个包含翻译源的bundle时,一定要清除缓存,这样Symfony才能发现这个新的翻译源。

$  php app/console cache:clear

(四)翻译时Locale的回滚

        假设一名用户的locale信息是 fr_FR,而你正翻译的键是 Symfony is great。为了找到法语信息,Symfony切实地检查若干locale的翻译源:

  1. 首先,Symfony在一个 fr_FR 的翻译源(例如messages.fr_FR.xlf)寻找翻译信息;

  2. 如果没找到,Symfony在一个 fr 翻译源(比如messages.fr.xlf)继续寻找翻译信息;

  3. 如果仍然没找到,Symfony使用fallbacks这个配置参数, 它被默认设为 en (参阅 FrameworkBundle配置信息)。

        2.6版Symfony引入了将缺少的翻译信息写入日志的能力。当Symfony无法找到给定locale的翻译信息时,它会把缺少的翻译信息给添加到日志文件中。参阅 logging。

(五)翻译数据库内容

        翻译数据库内容时要用到Doctrine扩展中的 Translatable Extension 或 Translatable Behavior(PHP 5.4+)。更多信息请参考相应文档。

译注:使用Doctrine扩展有两种方式

        一个是类库方式,一个是bundle方式。此处的Translatable Behavior,是指用bundle安装之后,翻译数据库内容时所应参考的用法)

(六)翻译约束信息 

 

参考 如何翻译验证约束消息 以了解更多。

(七)处理用户的Locale 

        翻译的过程是取决于用户的locale的。阅读 如何操作用户的Locale 以了解如何处理,如下所示:

        当前用户的locale被存在请求中,可以通过 Request 对象访问到:

use Symfony\Component\HttpFoundation\Request;
 
public function indexAction(Request $request)
{
    $locale = $request->getLocale();
}

        要设置用户的locale,你可能希望创建一个自定义的事件监听,以便它在系统的其他部分(比如translator)需要它之前就被设置好:

public function onKernelRequest(GetResponseEvent $event)
{
    $request = $event->getRequest();
 
    // some logic to determine the $locale
    $request->setLocale($locale);
}

         在控制器中使用 $request->setLocale() 来设置locale以便左右translator,实在是太迟了。可以通过监听 (如上), URL (见下文) 或是直接对 translator 服务来调用 setLocale()。下面参考 把locale信息“粘连”到用户的Session周期中 以了解更多内容:

        symfony将locale设置,存储在Request中,这意味着该设置不可用在后续请求。在本章,你将学习如何保存这个locale到session中,以便相同的locale可以用在所有后续请求中。

a)创建LocaleListener

        为了模拟该locale被存储在session,你需要去创建和注册一个新的事件监听。监听器看起来像是这样的。通常情况下,_locale作为路由参数来表示locale,虽然他对你如何确定一个请求所需的locale无关紧要:

// src/AppBundle/EventListener/LocaleListener.php
namespace AppBundle\EventListener;
 
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
class LocaleListener implements EventSubscriberInterface
{
    private $defaultLocale;
 
    public function __construct($defaultLocale = 'en')
    {
        $this->defaultLocale = $defaultLocale;
    }
 
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        if (!$request->hasPreviousSession()) {
            return;
        }
 
        // try to see if the locale has been set as a _locale routing parameter
        if ($locale = $request->attributes->get('_locale')) {
            $request->getSession()->set('_locale', $locale);
        } else {
            // if no explicit locale has been set on this request, use one from the session
            $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
        }
    }
 
    public static function getSubscribedEvents()
    {
        return array(
            // must be registered after the default Locale listener
            KernelEvents::REQUEST => array(array('onKernelRequest', 15)),
        );
    }
}

然后注册监听器:

# config.yaml #
services:
    app.locale_listener:
        class: AppBundle\EventListener\LocaleListener
        arguments: ['%kernel.default_locale%']
        tags:
            - { name: kernel.event_subscriber }
# config.xml #
<service id="app.locale_listener"
    class="AppBundle\EventListener\LocaleListener">
    <argument>%kernel.default_locale%</argument>
 
    <tag name="kernel.event_subscriber" />
</service>

#config.php #
use Symfony\Component\DependencyInjection\Definition; 
$container
    ->setDefinition('app.locale_listener', new Definition(
        'AppBundle\EventListener\LocaleListener',
        array('%kernel.default_locale%')
    ))
    ->addTag('kernel.event_subscriber')

        就是这样!改变用户locale并看到他粘在整个请求中,现在庆祝一下吧。记住,去获取用户locale,请始终使用这个Request::getLocale方法:

// from a controller...
use Symfony\Component\HttpFoundation\Request;
 
public function indexAction(Request $request)
{
    $locale = $request->getLocale();
}

b)设置基于用户喜好的Locale 

        你可能希望进一步提高这一技术,并基于登录用户的用户实体定义locale。然而,由于LocaleListener 在 FirewallListener 之前调用,你无法访问已登录用户,FirewallListener来负责处理认证并设置用户token在TokenStorage上。

        假设你已经在你的User实体中定义了一个locale 属性,并且你想要去使用它作为给定用户的locale。要做到这一点,你可以挂钩巧取到登录过程,并在他们被重定向到第一页之前更新用户session的locale值。

        要做到这一点,你需要一个security.interactive_login事件的事件监听器:

// src/AppBundle/EventListener/UserLocaleListener.php
namespace AppBundle\EventListener;
 
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
 
/**
 * Stores the locale of the user in the session after the
 * login. This can be used by the LocaleListener afterwards.
 */
class UserLocaleListener
{
    /**
     * @var Session
     */
    private $session;
 
    public function __construct(Session $session)
    {
        $this->session = $session;
    }
 
    /**
     * @param InteractiveLoginEvent $event
     */
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
 
        if (null !== $user->getLocale()) {
            $this->session->set('_locale', $user->getLocale());
        }
    }
}

然后注册监听器:

# app/config/services.yml
services:
    app.user_locale_listener:
        class: AppBundle\EventListener\UserLocaleListener
        arguments: ['@session']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }

<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="app.user_locale_listener"
            class="AppBundle\EventListener\UserLocaleListener">
 
            <argument type="service" id="session"/>
 
            <tag name="kernel.event_listener"
                event="security.interactive_login"
                method="onInteractiveLogin" />
        </service>
    </services>
</container>

// app/config/services.php
$container
    ->register('app.user_locale_listener', 'AppBundle\EventListener\UserLocaleListener')
    ->addArgument('session')
    ->addTag(
        'kernel.event_listener',
        array('event' => 'security.interactive_login', 'method' => 'onInteractiveLogin'
    );

        为了在用户更改语言偏好后立即更新语言,你需要在更新 User 实体后更新session。

下文的 Locale 和 URL 小节有讲到通过路由来设置locale。

        由于你可以把locale存到用户的session中,根据用户的locale,它可能会尝试使用相同的URL来显示不同语言的资源。例如,http://www.example.com/contact 可以对某个用户显示英语内容,但对另一个用户有显示法语的。不幸的是,这会粗暴破坏互联网的一个基本原则:即,特定的URL要对用户返回相同的资源,不管是什么(语种的)用户。进一步搞砸问题的是,哪个版本的内容可以被搜索引擎检索到?

        一个很好的策略是,把locale包容到URL之中。通过使用一个特殊的 _locale 参数(parameter),该策略已为路由系统完整支持

# app/config/routing.yml
contact:
    path:     /{_locale}/contact
    defaults: { _controller: AppBundle:Contact:index }
    requirements:
        _locale: en|fr|de

<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        http://symfony.com/schema/routing/routing-1.0.xsd">
 
    <route id="contact" path="/{_locale}/contact">
        <default key="_controller">AppBundle:Contact:index</default>
        <requirement key="_locale">en|fr|de</requirement>
    </route>
</routes>

// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$collection = new RouteCollection();
$collection->add('contact', new Route(
    '/{_locale}/contact',
    array(
        '_controller' => 'AppBundle:Contact:index',
    ),
    array(
        '_locale' => 'en|fr|de',
    )
));
 
return $collection;

        当使用特殊的 _locale 路由参数时,匹配到的locale将 自动设置到Request对象中 然后可以透过 getLocale() 方法来取出。换言之,如果一个用户访问的URI是 /fr/contact,那么locale fr 将自动地被设置为当前请求的locale。

现在你可以使用 locale 来创建路由,用于程序中的其他需要翻译的页面。

参考 如何在路由中使用服务容器的参数 来了解如何避免在全部路由中写死 _locale 条件(requirement)。

设置默认的Locale 

        若无法确定用户的locale怎么办?在框架中配置好 default_locale,即可确保用户在每一次请求中皆已被设置locale:

# app/config/config.yml
framework:
    default_locale: en

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:framework="http://symfony.com/schema/dic/symfony"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/symfony
        http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
 
    <framework:config default-locale="en" />
</container>


// app/config/config.php
$container->loadFromExtension('framework', array(
    'default_locale' => 'en',
));

(八)对翻译进行调试 

debug:translation 命令行语句从Symfony 2.6起引入。 Symfony 2.5之前,这一命令是translation:debug。

        当你在不同语言的大量翻译信息中操作时,要跟踪到丢失了哪条信息以及哪条信息没有被使用,是很困难的。阅读 如何找到丢失或未使用的翻译信息 以了解如何发现这一类翻译信息。

(九)总结 

        使用Symfony的翻译组件,创建一个国际化的应用程序,将不再是一个“痛苦过程”,而是归结于以下简单步骤:

  • 从程序中抽象出待翻译的信息,把每一条信息用 trans() 或 transChoice() 方法替换(通过 使用Translator一文了解更多);

  • 通过创建信息文件(translation message file)将待译信息翻译成多个locale语种。Symfony能够找到并处理每一个文件,因为这些文件的名字遵循指定的命名约定;

  • 管理好用户的locale,它可以存在request中,但是也可以存在用户的session中。

了解更多

  • 如何操作用户的Locale
  • 如何找到丢失或未使用的翻译信息

参考:http://www.symfonychina.com/doc/current/translation.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Symfony学习笔记之翻译组件-----translation总结 的相关文章

  • 基于公开网站挖掘敏感信息的研究与分析- Fofa 搜索

    基于公开公开网站挖掘敏感信息的研究与分析 Fofa 搜索 一 引言 1 1项目概述 基于公开网站的敏感信息挖掘研究与分析 xff1a 针对目前网络安全整体的趋势我们从google等搜索引擎 Github等代码库 FOFA等空间搜索这三个方面
  • HTTP报文

    一 HTTP报文的结构 用于HTTP协议交互的信息 xff0c 称为HTTP报文 客户端的HTTP报文称为请求报文 xff0c 服务端的称为响应报文 HTTP报文结构如下图 xff1a 下面是请求报文的一个实例 xff1a 请求行 xff1
  • 用C++写一个UDP发送和接收程序

    发送程序Sender cpp include lt stdio h gt include lt string gt include lt iostream gt include lt winsock h gt using namespace
  • STM32自学笔记(五)串口通信

    xff08 想要深入理解就把前面的看下 xff0c 否则直接看使用总结即可 xff09 usart文件夹 usart 文件夹内包含了 usart c和usart h两个文件 这两个文件用于串口的初始化和中断接收 代码只针对了串口1 xff0
  • sockaddr_in详解

    struct sockaddr in short sin family Address family一般来说AF INET xff08 地址族 xff09 PF INET xff08 协议族 xff09 unsigned short sin
  • ubuntu下好用的TCP/UDP调试工具

    github官方链接 GitHub s kyo mNetAssist mNetAssist A UDP TCP Assistant 编译好的安装包 ubuntu下好用的TCP UDP调试工具 网络设备文档类资源 CSDN下载 1 解压dpk
  • Linux网络编程之connect函数分析

    在一个 CLIENT SERVER模型的网络应用中 xff0c 客户端的调用序列大致如下 xff1a socket gt connect gt recv send gt close 其中socket没有什么可疑问的 xff0c 主要是创建一
  • linux route 命令

    显示现在所有路由 route n root 64 Ubuntu route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 10
  • 大量LAST_ACK 分析过程

    记录一下自己的思想过程 现象 在netstat的时候发现大量处于LAST ACK状态的TCP连接 xff0c 达到在ESTABLISHED状态的90 以上 root 64 ccsafe netstat ant fgrep 34 34 cut
  • const 与重载

    const到底是不是一个重载的参考对象 xff0c 请看下面的例子 class A public void f int i std cout lt lt 34 1 34 函数1 void f int i const std cout lt
  • 指向成员函数的指针

    指向成员函数的指针 取一个非静态成员函数的地址 xff0c 如果该函数是nonvirtual xff0c 则得到的结果是它在内存中真正的地址 然而这个值也不是完全的 xff0c 它也需要被绑定于某个class object的地址上 xff0
  • rviz 远程显示及控制移动机器人的导航

    环境 xff1a xff08 1 xff09 虚拟机本地端IP 192 168 10 30 xff08 虚拟机需要通过桥接的形式与PC本地机连接 xff0c PC 机IP 192 168 10 21 xff09 xff08 2 xff09
  • iptables - administration tools for packet filtering and NAT

    2 iptables administration tools for packet filtering and NAT Linux Iptables Manual Incoming Traffic V 43 43 PREROUTING 4
  • C++ 内部类、嵌套类

    1 嵌套类 nested class 一个类可以定义在另一个类的内部 xff0c 前者称为嵌套类 xff0c 嵌套类时一个独立的类 xff0c 和外层类基本没什么关系 它通过提供新的类型类作用域来避免名称混乱 嵌套类必须声明在类的内部 xf
  • SONY VAIO P VPCP118KJ索尼酷袋本 鸡肋上网本初体验

    随着消费者市场需求的不断多样化和个性化 xff0c 个人电脑厂家开始不断突破创新 xff0c 在产品线拼装出各种各样的个人电脑 xff0c 主打性能的游戏本 xff0c 主打便携轻薄本 xff0c 2009年的CES上索大法推出了一款与众不
  • 第2课【CMSIS和标准库开发】标准库 CMSIS 分层

    目录 基本知识框架课堂笔记CMSISCMSIS的分层关系和作用CMSIS层文件结构内核函数层文件外设函数访问层文件 标准库开发标准库开发 LED GPIO标准库开发 KEY GPIO标准库开发 位带操作位带位带位带别名区位带区和位带别名区的
  • 第4课【STM32的时钟】时钟 时钟源 内外部时钟 高低速时钟

    目录 基本知识框架课堂笔记时钟什么是时钟 xff1f 时钟有什么作用 时钟源HSE 外部高速时钟HSI 内部高速时钟LSE 外部低速时钟LSI 内部低速时钟PLL锁相环 主要时钟和其他时钟主要时钟其他时钟 配置系统时钟实验 基本知识框架Xm
  • 第7课【SysTick定时器】中断 系统定时器 寄存器

    目录 基本知识框架课堂笔记SysTick定时器简介什么是SysTick定时器SysTick定时器的作用 SysTick寄存器CTRL控制及状态寄存器LOAD重装载数值寄存器VALUE当前数值寄存器CALIB校准数值寄存器 SysTick实战
  • 第8课【通讯的基本概念】串行并行 单工半双工全双工 同步异步 比特率波特率

    目录 基本知识框架课堂笔记通讯传送方式串行通讯并行通讯串行通讯与并行通讯的特性 通讯方向单工通讯半双工通讯全双工通讯 同步方式同步通讯异步通讯同步通讯与异步通讯的区别 传输单位比特率波特率比特率和波特率的联系 通讯简介通讯标准 基本知识框架
  • 嵌入式宏定义中do...while(0)的意义

    目录 背景do while 0 形式宏定义使得宏定义拥有一些函数的特点 分析1 封装 xff1a 使得宏定义可以包含复杂的内容而不容易出错 xff0c 提高代码健壮性2 生命周期 xff1a 宏定义内 外部定义的同名变量不会冲突3 返回 x

随机推荐

  • 第9课【USB协议】USB总线 接口 端点 管道 数据包 枚举 STM32_USB-FS-Device_Lib V4.1.0

    目录 简介背景特点版本 数据流模型总线拓扑结构主 从设备通信流端点管道 物理层接口类型USB Type AUSB Type BUSB Type C 引脚定义4PIN9PIN5PIN24PIN16PIN6PIN 电平标准 协议层 通讯方式通讯
  • 第10课【STM32 USB通讯协议实战】HID键盘+CDC虚拟串口组合设备

    目录 前言USB设备类别未定义设备设备描述符 配置描述符分析配置从机类型配置设备专用的描述符配置从机端点实现设备类特定请求 HID设备特点HID键盘描述符实例HID报文描述符短条目报文生成HID键盘报文描述符实例总结 CDC设备特点CDC
  • 创建 std_srvs::Empty 型 Service (参数为空的服务)

    参考 xff1a ros NodeHandle advertiseService API docs 1 包含头文件 include lt std srvs Empty h gt 2 创建服务 xff0c 并绑定服务的回调函数 restart
  • stm32串口+DMA环形缓冲收发保姆级

    基于HAL库的STM32串口DMA环形缓冲收发实例 首先在此感谢开源项目 xff0c 以及大佬们的无私奉献 xff0c 让每一个逐梦人能够免费学习 xff0c 再次感谢 xff01 发布只为记录 xff0c 记性不够 xff0c 笔记来凑
  • NX串口通讯windows

    我用windows与NX进行通讯 xff0c 用的线是CH340下载线 USB端连接windows xff0c 绿色的TXD连接NX的10号 RXD xff0c 白色的RXD连接NX的8号 TXD 目录 1 windows端 2 NX端 2
  • 串口接收 DMA FIFO 双缓冲区配置 + 单色OLED屏幕灰度图像显示的抖动算法

    适用于单色OLED屏幕图像显示的抖动算法 Visual studio c 43 43 STM32F4070 96寸单色IIC通信OLED5级灰度串口DMA 双缓冲github 抖动算法原理 DITHER抖动算法是指灰度可以用一定比例的黑白点
  • HTTP请求报文与响应报文

    一 HTTP的请求报文格式 xff1a HTTP的请求报文内容包括 xff1a 请求行 request line 请求头部 header 空行 和 请求数据 request data 四个部分组成 请求行主要包括 xff1a 请求方法 UR
  • c++中char转换为string类型

    写程序遇到一个Bug如下 xff1a char x 61 39 a 39 string str x 这里报错了 当时就有点蒙了 xff0c 查了下string的构造函数 xff1a string string const string am
  • C语言——内存管理问题

    常见的内存错误及解决方法 一 结构体成员指针未初始化 eg span class token macro property span class token directive hash span span class token dire
  • 大端存储和小端存储

    一 大端存储 高字节数据存储在低地址 二 小端存储 低字节数据存储在低地址 注意 xff1a 无论是大端存储还是小端存储 xff0c 读取到的数据为0x0102030405 三 大端存储或小端存储都是由系统设定的 xff0c 其二者区别在于
  • 多播的概念

    一 多播概述 多播 xff1a 数据的收发仅仅在同一组中进行 xff08 相当于我往一个群里发 xff0c 只有加入这个群的人才能收到 xff09 多播的特点 xff1a 多播地址标示一组接口 多播可以用于广域网使用 在IPv4 xff0c
  • 利用C语言编写一个网络分析器

    一 链路层数据格式 mac报文 xff1a 14个字节 二 IP数据报文格式 三 TCP数据报文格式 四 UDP数据报文格式 五 demo xff08 网络分析器 xff09 recvfrom接收链路层帧数据 xff0c 不经过网络层 传输
  • ARP攻击代码(ARP欺骗)

    一 ARP攻击 ARP欺骗攻击原理 xff1a ARP欺骗攻击建立在局域网主机间相互信任的基础上的 比如 xff1a 假设A B C在同一个局域网中 xff0c 网关为192 168 43 1 xff0c IP地址和MAC地址分别假设如下
  • 使用wireshark抓包并分析TCP三次握手

    一 实验过程 1 TCP包抓取及分析过程 确认使用的协议 xff0c 使用HTTP服务 选择https www baidu com 作为目标地址 启动Wireshark软件 xff0c 点击开始抓包后 在浏览器地址输入https www b
  • 使用wireshark抓包并分析TCP四次挥手

    一 四次挥手 第一次挥手 xff1a xff08 FIN 43 ACK xff09 客户端发送FIN 43 ACK包给服务端 xff0c 用来关闭客户端到服务器的数据传送 此时客户端进入FIN WAIT 1状态 第二次挥手 xff1a AC
  • TR-069协议介绍

    一 概念 关于TR 069 协议命名 TR 069 全称 Technical Report 069 是由DSL Forum xff08 一个非盈利性的全球行业联盟 致力于发展宽带网络范 xff09 其成员包括通讯 设备 计算机 网络和服务提
  • FFMPEG关键结构体——AVCodecContext

    一 AVCodecContext结构体 这是一个描述编码器上下文的数据结构 xff0c 包含了众多编码器需要的参数信息 该结构体在libavcodec avcode h中定义 二 常见变量
  • FFMPEG关键结构体——AVIOContext

    一 AVIOContext结构体 这个结构体 xff0c 是FFmpeg中有关io操作的顶层结构体 xff0c 是avio的核心 FFmpeg支持打开本地文件路径和流媒体协议的URL 该结构体在libavformat avio h中定义 二
  • 指针数组和数组指针

    一 什么是指针数组 一个数组 xff0c 若其元素均为指针类型数据 xff0c 称为指针数组 xff0c 也就是说 xff0c 指针数组中的每一个元素都存放一个地址 xff0c 相当于一个指针变量 span class token keyw
  • Symfony学习笔记之翻译组件-----translation总结

    过际化 xff08 internationalization xff0c 常被简写为i18n xff09 xff0c 是指将字符串和其他一些具有区域特征的片段 xff0c 从你的程序中提取 xff08 abstract xff09 出来 x