seablast/i18n 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

seablast/i18n

Composer 安装命令:

composer require seablast/i18n

包简介

A lightweight internationalization (i18n) module designed for apps using the Seablast for PHP framework

README 文档

README

Total Downloads Latest Stable Version Polish the code

A lightweight internationalization (i18n) module for apps using the Seablast for PHP framework. It provides a Latte translation filter, a language-selection API, optional Universal Language Selector templates, and Phinx migrations for translation data. Installable via Composer, it integrates seamlessly and activates only when needed, allowing you to effortlessly provide multilingual support and manage user language preferences.

Usage

UI

The Latte filter translate uses the dictionary loaded by Seablast\I18n\SeablastTranslate. Seablast registers it in Seablast\SeablastView::renderLatte() from the SeablastConstant::TRANSLATE_CLASS setting defined in app.conf.php.

Use as: const back = {="Zpět"|translate};

Note: In Latte, SB:LANGUAGE is defined lazily by the translator. For that reason, {=''|translate} in views/uls.menu.latte runs before SB:LANGUAGE is read.

const back = {="Zpět"|translate};
const lang = {$configuration->getString('SB:LANGUAGE')};

To display the language selector, include the three uls.*.latte files as follows:

<!DOCTYPE html>
<html>
<head>
    ...
    {include '../vendor/seablast/i18n/views/uls.css.latte'}
    ...
</head>
<body>
    ...
            <nav>
                <ul id="menu">
                    ...
                    <li>{include '../vendor/seablast/i18n/views/uls.menu.latte'}</li>
                </ul>
            </nav>
    ...
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    {block script}{/block}
    {include '../vendor/seablast/i18n/views/uls.js.latte'}
</body>
</html>

Note: The I18n:SHOW_LANGUAGE_SELECTOR flag controls whether the contents of all uls.*.latte templates are rendered. As a result, you do not need to wrap uls.*.latte includes in custom Latte conditions; include them, and the application decides whether they take effect.

Instead of using the language selector, you can switch the language programmatically by calling:

window.languageSelector(string language); // language is one of the configured language codes, for example: en, cs

The caller is responsible for reloading or re-rendering the page so translated strings update:

// switch language using Seablast/I18n mechanics and jQuery
$("select.language_selector").change(function () {
  window.languageSelector($(this).val()).then(
    function () {
      location.reload();
    },
    function (err) {
      console.error(err);
    },
  );
});

The window.languageSelector function is declared in uls.js.latte. That function returns jQuery.Promise (a promise-like object with .then(), .done(), .fail(), .always()) and in the fulfillment value, there's JSON, e.g. {message: 'en'}.

The language switching endpoint exists independently, but the UI selector is gated by I18n:SHOW_LANGUAGE_SELECTOR to prevent exposing unfinished or tenant-specific i18n:

const flags = [
  "I18n:SHOW_LANGUAGE_SELECTOR", // turned on by default also in `conf/app.conf.php`
];

Note: only languages from the configuration (e.g. ->setArrayString(I18nConstant::LANGUAGE_LIST, ['en', 'cs'])) are accepted. The first configured language is the default.

Database structure

To create the expected database table structure (for dictionary and localised items), just add the seablast/i18n migration path to your phinx.php configuration, e.g.

    'paths' => [
        'migrations' => [
            '%%PHINX_CONFIG_DIR%%/db/migrations',
            '%%PHINX_CONFIG_DIR%%/../vendor/seablast/i18n/conf/db/migrations',
        ],
        'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds'
    ],

Dictionary table: translations

Column Type Attributes Description
id integer Primary key, auto-increment (identity) Unique identifier for each translation entry.
language string(5) Indexed, part of unique constraint Configured language code (for example en or cs).
translation_key string(255) Indexed, part of unique constraint The lookup key used in the application (e.g., "Save PDF", "Back").
translation_value text Localized string corresponding to the key in the given language.

Integration

  • Seablast/Seablast::v0.2.11 contains APP_DIR . '/vendor/seablast/i18n/conf/app.conf.php', // Seablast/i18n extension configuration so use at least this Seablast version.
  • "seablast/seablast": "^0.2.7" is in the require-dev section of composer.json because the app that uses Seablast I18n may use whatever dev version of Seablast.

Language API

  • API '/api/language' using 'model' => '\Seablast\I18n\Models\ApiLanguageModel' returns the selected language or accepts a language to store in the cookie 'sbLanguage'.
  • The cookie 'sbLanguage' is created after a successful language-selection request.

Language selector

  • Because typically .htaccess uses RedirectMatch 404 vendor\/(?!seablast\/) to make vendor folder off limits for web access except the seablast library, the jquery.uls is in Seablast for PHP since v0.2.11 and not in this module.
  • However, it's useful to know that to make the SVG icon in .uls-trigger adopt the font-color of the surrounding element, the following style was added into uls/images/language.svg: fill="currentColor". Also uls/css/jquery.uls.css was changed (changed: .uls-trigger, added: .uls-trigger icon and .uls-trigger .icon svg).
  • The selector uses I18nConstant::LANGUAGE_LIST as both the allowed ULS language set and its quickList; language labels come from ULS data instead of hardcoded labels in this library.
  • Language is lazy-initialised in SeablastView: $translator = new $translatorClass($this->model->getConfiguration()); instantiates SeablastTranslate, which calls $lang = new ApiLanguageModel($this->configuration, new \Seablast\Seablast\Superglobals());. There $this->configuration->setString('SB:LANGUAGE', $result); is set.
  • '/api/language' using 'model' => '\Seablast\I18n\Models\ApiLanguageModel' is called from window.languageSelector when uls.onSelect with parameter.

Localised data access

Extend the class FetchLocalisedItemsModel with preset of these three properties

    /** @var int itemTypeId set in the child class */
    protected $itemTypeId;
    /** @var string page title beginning set in the child class */
    protected $titlePrefix = "";
    /** @var string page title ending  set in the child class*/
    protected $titleSuffix = "";

in order to access the localised items filtered by their type.

The full class looks like this:

<?php

declare(strict_types=1);

namespace WorkOfStan\Protokronika\Models;

use Seablast\I18n\Models\FetchLocalisedItemsModel;
use Seablast\Seablast\SeablastConfiguration;
use Seablast\Seablast\Superglobals;

/**
 * Retrieve items from database
 */
class BlogModel extends FetchLocalisedItemsModel
{
    use \Nette\SmartObject;

    /**
     * @param SeablastConfiguration $configuration
     * @param Superglobals $superglobals
     */
    public function __construct(SeablastConfiguration $configuration, Superglobals $superglobals)
    {
        $this->itemTypeId = 1; // Blog type ID
        $this->titlePrefix = "My special web - ";
        $this->titleSuffix = "Blog";
        $configuration->mysqli(); // dbms prefix set up even if Seablast\Auth is not present and thus it's not
        //already set up in SeablastController: `$this->identity = new $identityManager($this->configuration->mysqli());
        parent::__construct($configuration, $superglobals);
    }
}

TODO: Find a way to initialise mysqli() automatically, so that it is not dependent on the Seablast\Auth presence.

(See Seablast\Dist BlogModel.php.)

This MODEL yields items one by one from the database in a lazy, memory efficient, way. The VIEW can display it as follows:

{layout 'BlueprintWeb.latte'}
{block mainblock}
<h1>
    {ifset $itemId}
        <a href="{$configuration->getString('SB_APP_ROOT_ABSOLUTE_URL')}/blog-r">Blog (read-only)</a>
    {else}
        Blog
    {/ifset}
</h1>

{foreach $items as $item}
    <article class="post">
        {* Check if $itemId is set to decide if we have multiple posts - If we have an “id” parameter, we’re in single‐post mode *}
        {ifset $itemId}
            {* single‐item mode: editable fields for admins *}
            <h2>{$item['title']}</h2>
            {*<p class="post-date">Created at: {$item['created_at']}</p>*}
            <div class="post-content">{$item['content']|breakLines}</div>
        {else}
            {* listing mode: links into each item TODO paging *}
            <h2><a href="?id={$item['item_id']}">{$item['title']}</a></h2>
            {*<p class="post-date">Created at: {$item['created_at']}</p>*}
            <div class="post-content">{$item['content']|breakLines}...</div>
        {/ifset}
    </article>
{/foreach}
{/block}

This code can be seen live in Seablast\Dist blog-readonly.latte. The texts can also be directly editable by admins as seen in Seablast\Dist blog-editable.latte. (This of course requires users to be logged in, hence Seablast\Auth is required.)

统计信息

  • 总下载量: 1.18k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 10
  • 依赖项目数: 0
  • 推荐数: 1

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-08-03

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固