冬季 / wn-translate-plugin
Winter CMS 的翻译插件
Requires
- php: >=7.2
- composer/installers: ~1.0
Replaces
- rainlab/translate-plugin: ~1.6
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
属性值作为数组传递。第一个值是属性名,其他值表示选项,在本例中设置选项index
为true
。
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() }}