冬季/wn-translate-plugin

Winter CMS 的翻译插件

资助包维护!
wintercms
Open Collective

安装次数: 17,924

依赖关系: 3

建议者: 0

安全: 0

星标: 13

关注者: 6

分支: 18

开放问题: 5

类型: winter-plugin

v2.1.6 2024-03-19 10:49 UTC

README

启用多语言站点。

入门指南

使用 composer 安装插件

composer require winter/wn-translate-plugin

然后,运行迁移以确保插件已启用

php artisan winter:up

如果您使用的是 公开文件夹,则需要使用 php artisan winter:mirror public --relative 命令重新生成它

选择语言

可以在后端区域设置不同的语言,同时选择一个默认语言。这将在前端和后端 UI 中激活语言的使用。

访客可以通过在 URL 前缀语言代码来选择语言,这将存储在用户的会话中作为他们选择的语言。例如

  • http://website/ru/ 将显示为俄语的网站
  • http://website/fr/ 将显示为法语的网站
  • http://website/ 将显示为默认语言或用户的选定语言。

语言选择组件

访客可以使用 LocalePicker 组件来选择他们选择的语言。该组件将显示一个简单的下拉菜单,根据选择更改页面语言。

title = "Home"
url = "/"

[localePicker]
==

<h3>{{ 'Please select your language:'|_ }}</h3>
{% component 'localePicker' %}

如果已翻译,上面的文本将显示为用户选择的任何语言。下拉菜单非常基础,并打算重设样式。一个更简单的例子可能是

[...]
==

<p>
    Switch language to:
    <a href="javascript:;" data-request="onSwitchLocale" data-request-data="locale: 'en'">English</a>,
    <a href="javascript:;" data-request="onSwitchLocale" data-request-data="locale: 'ru'">Russian</a>
</p>

消息翻译

消息或字符串翻译是网站中使用的即兴字符串的转换。可以使用参数对消息进行翻译。

{{ 'site.name'|_ }}

{{ 'Welcome to our website!'|_ }}

{{ 'Hello :name!'|_({ name: 'Friend' }) }}

消息还可以用于选择用途进行翻译。

{{ 'There are no apples|There are :number applies!'|__(2, { number: 'two' }) }}

或者您可以通过传递第二个参数来手动设置区域。

{{ 'this is always english'|_({}, 'en') }}

主题可以通过在主题目录中定义 theme.yaml 文件中的 translate 键来为这些消息提供默认值。

name: My Theme
# [...]

translate:
    en:
        site.name: 'My Website'
        nav.home: 'Home'
        nav.video: 'Video'
        title.home: 'Welcome Home'
        title.video: 'Screencast Video'

您还可以在相对主题的单独文件中定义翻译,路径相对于主题。以下定义将从主题中的 config/lang.yaml 文件中获取默认消息。

name: My Theme
# [...]

translate: config/lang.yaml

这是具有两种语言的 config/lang.yaml 文件的一个示例

en:
    site.name: 'My Website'
    nav.home: 'Home'
    nav.video: 'Video'
    title.home: 'Welcome Home'
hr:
    site.name: 'Moje web stranice'
    nav.home: 'Početna'
    nav.video: 'Video'
    title.home: 'Dobrodošli'

您还可以为每个区域定义单独的翻译文件,路径相对于主题。以下定义将从主题中的 config/lang-en.yaml 文件中获取英文区域的默认消息,以及从 config/lang-fr.yaml 文件中获取法语区域的默认消息。

name: My Theme
# [...]

translate:
en: config/lang-en.yaml
fr: config/lang-fr.yaml

这是 config/lang-en.yaml 文件的一个示例

site.name: 'My Website'
nav.home: 'Home'
nav.video: 'Video'
title.home: 'Welcome Home'

为了使这些默认值反映到您的网站前端,请转到后端中的 设置 -> 翻译消息,然后单击 扫描消息。当主题激活时,它们也将自动加载。

可以使用 translate:scan artisan 命令执行相同的操作。将其包含在部署脚本中,以自动获取更新的消息可能是有价值的

php artisan translate:scan

添加 --purge 选项以首先清除旧消息

php artisan translate:scan --purge

内容翻译

此插件激活了 CMS 中的一个功能,允许内容文件使用语言后缀,例如

  • welcome.htm 将包含默认语言的内容。
  • welcome.ru.htm 将包含俄语的内容。
  • welcome.fr.htm 将包含法语的内容。

邮件模板翻译

此插件激活了CMS中的一个功能,允许邮件模板文件使用语言后缀,例如:

  • mail-notify.htm将包含默认语言的邮件模板。
  • mail-notify-ru.htm将包含俄语的邮件模板。
  • mail-notify-fr.htm将包含法语的邮件模板。

扩展插件以支持可翻译字段

如果你正在扩展插件,并且希望在后台添加的字段可翻译,你必须使用'backend.form.extendFieldsBefore',并告诉插件哪些字段需要可翻译,通过将它们推送到数组中来实现。

public function boot() {
    Event::listen('backend.form.extendFieldsBefore', function($widget) {

        // Only apply this listener when the Page controller is being used
        if (!$widget->getController() instanceof \Winter\Pages\Controllers\Index) {
            return;
        }

        // Only apply this listener when the Page model is being modified
        if (!$widget->model instanceof \Winter\Pages\Classes\Page) {
            return;
        }

        // Only apply this listener when the Form widget in question is a root-level
        // Form widget (not a repeater, nestedform, etc)
        if ($widget->isNested) {
            return;
        }

        // Add fields
        $widget->tabs['fields']['viewBag[myField]'] = [
            'tab' => 'mytab',
            'label' => 'myLabel',
            'type' => 'text'
        ];

        // Translate fields
        $translatable = [
            'viewBag[myField]'
        ];

        // Merge the fields in the translatable array
        $widget->model->translatable = array_merge($widget->model->translatable, $translatable);

    });
}

模型翻译

可以通过使用Winter.Translate.Behaviors.TranslatableModel行为并指定在类中哪些属性需要翻译,使模型具有可翻译的属性。

class User
{
    public $implement = ['Winter.Translate.Behaviors.TranslatableModel'];

    public $translatable = ['name'];
}

该属性将包含默认语言值,可以通过使用translateContext()方法创建其他语言代码值。

$user = User::first();

// Outputs the name in the default language
echo $user->name;

$user->translateContext('fr');

// Outputs the name in French
echo $user->name;

您可以使用相同的过程来设置值。

$user = User::first();

// Sets the name in the default language
$user->name = 'English';

$user->translateContext('fr');

// Sets the name in French
$user->name = 'Anglais';

lang()方法是translateContext()的简写版本,并且也是可链式的。

// Outputs the name in French
echo $user->lang('fr')->name;

这在 Twig 模板内部很有用。

{{ user.lang('fr').name }}

有方法在不更改上下文的情况下获取和设置属性。

// Gets a single translated attribute for a language
$user->getAttributeTranslated('name', 'fr');

// Sets a single translated attribute for a language
$user->setAttributeTranslated('name', 'Jean-Claude', 'fr');

主题数据翻译

还可以翻译主题自定义选项。只需将表单字段标记为具有translatable属性,插件就会处理其他所有事情。

tabs:
  fields:
    website_name:
      tab: Info
      label: Website Name
      type: text
      default: Your website name
      translatable: true    

回退属性值

默认情况下,未翻译的属性将回退到默认区域设置。可以通过调用setTranslatableUseFallback()方法禁用此行为。

$user = User::first();

$user->setTranslatableUseFallback(false)->lang('fr');

// Returns NULL if there is no French translation
$user->name;

索引属性

可翻译模型属性也可以声明为索引,通过将$transatable属性值作为数组传递。第一个值是属性名,其他值表示选项,在本例中设置选项indextrue

public $translatable = [
    'name',
    ['slug', 'index' => true]
];

一旦属性被索引,就可以使用transWhere方法对模型应用基本查询。

Post::transWhere('slug', 'hello-world')->first();

transWhere方法接受第三个参数,用于明确传递区域值,否则它将从环境中检测到。

Post::transWhere('slug', 'hello-world', 'en')->first();

URL翻译

CMS中的页面支持翻译URL属性。假设你设置了3种语言

  • en: 英语
  • fr: 法语
  • ru: 俄语

有一个包含以下内容的页面

url = "/contact"

[viewBag]
localeUrl[ru] = "/контакт"
==
<p>Page content</p>

法语中“Contact”一词相同,因此不需要或未提供翻译后的URL。如果没有指定URL覆盖,则将使用默认URL。不会为给定的语言重复页面。

  • /fr/contact - 法语页面
  • /en/contact - 英语页面
  • /ru/контакт - 俄语页面
  • /ru/contact - 404

URL参数翻译

可以通过监听在切换语言时触发的translate.localePicker.translateParams事件来翻译URL参数。

Event::listen('translate.localePicker.translateParams', function($page, $params, $oldLocale, $newLocale) {
    if ($page->baseFileName == 'your-page-filename') {
        return YourModel::translateParams($params, $oldLocale, $newLocale);
    }
});

在YourModel中,一种可能的实现方式如下:

public static function translateParams($params, $oldLocale, $newLocale) {
    $newParams = $params;
    foreach ($params as $paramName => $paramValue) {
        $records = self::transWhere($paramName, $paramValue, $oldLocale)->first();
        if ($records) {
            $records->translateContext($newLocale);
            $newParams[$paramName] = $records->$paramName;
        }
    }
    return $newParams;
}

查询字符串翻译

可以通过监听在切换语言时触发的translate.localePicker.translateQuery事件来翻译查询字符串参数。

Event::listen('translate.localePicker.translateQuery', function($page, $params, $oldLocale, $newLocale) {
    if ($page->baseFileName == 'your-page-filename') {
        return YourModel::translateParams($params, $oldLocale, $newLocale);
    }
});

有关YourModel::translateParams方法的可能实现,请参阅上述“URL参数翻译”部分的示例。

扩展主题扫描

  Event::listen('winter.translate.themeScanner.afterScan', function (ThemeScanner $scanner) {
       ...
  });

设置模型翻译

可以像任何其他模型一样翻译设置模型。要检索翻译值,请使用

Settings::instance()->getAttributeTranslated('your_attribute_name');

有条件地扩展插件

模型

可以通过在行为定义之前放置一个@符号,有条件地扩展插件模型以支持翻译。这是一个软实现,只有当安装了Translate插件时,才会使用TranslatableModel,否则不会引发任何错误。

/**
 * Blog Post Model
 */
class Post extends Model
{

    [...]

    /**
     * Softly implement the TranslatableModel behavior.
     */
    public $implement = ['@Winter.Translate.Behaviors.TranslatableModel'];

    /**
     * @var array Attributes that support translation, if available.
     */
    public $translatable = ['title'];

    [...]

}

后端表单将自动检测可翻译字段的存在,并将它们的控件替换为多语言等效控件。

消息

由于Twig过滤器并不总是可用,我们可以将它们传递给本地的Laravel翻译方法。这确保了翻译消息始终在前端有效。

/**
 * Register new Twig variables
 * @return array
 */
public function registerMarkupTags()
{
    // Check the translate plugin is installed
    if (!class_exists('Winter\Translate\Behaviors\TranslatableModel'))
        return;

    return [
        'filters' => [
            '_' => ['Lang', 'get'],
            '__' => ['Lang', 'choice'],
        ]
    ];
}

用户界面

切换语言

用户可以通过点击多语言输入右侧的语言指示器在语言之间切换。按住CMD/CTRL键,所有多语言输入字段将切换到所选的语言。

不使用jQuery和Winter CMS框架文件集成

可以通过手动发出AJAX API请求,在不使用jQuery或Winter CMS AJAX框架的情况下使用前端语言切换器。以下是如何做到这一点的示例。

document.querySelector('#languageSelect').addEventListener('change', function () {
    const details = {
        _session_key: document.querySelector('input[name="_session_key"]').value,
        _token: document.querySelector('input[name="_token"]').value,
        locale: this.value
    }

    let formBody = []

    for (var property in details) {
        let encodedKey = encodeURIComponent(property)
        let encodedValue = encodeURIComponent(details[property])
        formBody.push(encodedKey + '=' + encodedValue)
    }

    formBody = formBody.join('&')

    fetch(location.href + '/', {
        method: 'POST',
        body: formBody,
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-WINTER-REQUEST-HANDLER': 'onSwitchLocale',
            'X-WINTER-REQUEST-PARTIALS': '',
            'X-Requested-With': 'XMLHttpRequest'
        }
    })
    .then(res => res.json())
    .then(res => window.location.replace(res.X_WINTER_REDIRECT))
    .catch(err => console.log(err))
})

HTML代码

{{ form_open() }}
    <select id="languageSelect">
        <option value="none" hidden></option>
        {% for code, name in locales %}
            {% if code != activeLocale %}
                <option value="{{code}}" name="locale">{{code | upper }}</option>
            {% endif %}
        {% endfor %}
    </select>
{{ form_close() }}