@Alastor

Как перенести переводы в базу данных в CakePHP?

Согласно документации, текст который мы хотим в дальнейшем перевести обёртываем в __() и далее через консоль генерируем .pot файлы
book.cakephp.org/3.0/en/core-libraries/internation...

А у меня сайт динамично развивается. И над ним будут работать несколько переводчиков. Поэтому после каждой фразы добавленной командой в шаблон, не вариант запускать генерацию файлов для перевода, совмещать со старыми и давать переводчикам.

Как я вижу решение данного вопроса - создать метод какой нибудь свой __perevod() и через него всё переводить. Но вдруго есть более элегантные решения?
  • Вопрос задан
  • 295 просмотров
Решения вопроса 1
@Alastor Автор вопроса
в общем этот плагин просто выгружает из таблицы переводы. а заносить их надо всё равно руками.

Что делает моё решение:
а) Переписывает стандартные __ и __d функции. Там есть ещё несколько функций перевода но мне они пока не нужны.
б) Автоматически записывает в базу все переводы обернутые в выше указанные функции. Причём запись происходит на все языки сразу. А потом переводчик заходит в админку и переводит (на сайте 7 языков)
в) Избавляет от pot. файлов. Как они меня раздражают, кто бы знал.
г) Поддерживает аргументы. Не стал изобретать велосипед и воспользовался стандартным методом что предоставляет I18n

Чего нет в моём решении:
а) Полной поддержки доменов перевода. Сейчас пишется всё в один массив.
Если будет в домене default и в домене скажем cake одинаковый текст, то один перезапишется другим. Тем что раньше возьмется из базы. Потом доделаю.

Что ешё есть:
а) В админке где переводят, кнопка обновить кеш. То есть переводчик закончил перевод, нажал обновить и всё переводы появились на сайте.

Дамп из Postgres (надо указать первичные ключи (id) и сделать автоинкременты)

Таблица с языками
CREATE TABLE languages (
    id integer NOT NULL,
    created timestamp with time zone,
    modified timestamp with time zone,
    user_id integer NOT NULL,
    priority integer NOT NULL,
    name character varying(16) NOT NULL,
    enabled boolean DEFAULT false NOT NULL,
    translations integer DEFAULT 0 NOT NULL,
    locale character varying(5) NOT NULL,
    code character varying(3) NOT NULL,
    deleted boolean DEFAULT false NOT NULL
);

translations - количество переводов для этого языка (просто для информации переводчика), code - код в ссылке сайта скажем site.com/en/page, deleted - маркер удалён язык или нет. я не стираю данные в некоторых таблицах и просто ставлю отметку что они удалены. А то бывает юзеры чего то удаляет и надо восстановить. Не в бэкапы же лезть. priority - приоритет языка при отображении в списке на сайте. locale - код языка для переводов. Можно использовать code для этой цели, но мне нужен locale.

Таблица для языковых фраз. Почему такая структура можете почитать в документации CakePHP
CREATE TABLE i18n_messages (
    id integer NOT NULL,
    domain character varying(100),
    locale character varying(5),
    singular character varying(255),
    plural character varying(255),
    context character varying(50),
    value_0 character varying(255),
    value_1 character varying(255),
    value_2 character varying(255),
    created timestamp with time zone,
    modified timestamp with time zone
);
ALTER TABLE ONLY i18n_messages
    ADD CONSTRAINT i18n_messages_domain_locale_singular_key UNIQUE (domain, locale, singular);
ALTER TABLE ONLY i18n_messages
    ADD CONSTRAINT i18n_messages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY i18n_messages
    ADD CONSTRAINT i18n_messages_locale_fkey FOREIGN KEY (locale) REFERENCES languages(locale) ON UPDATE RESTRICT ON DELETE RESTRICT;


В config/app.php разместил свой кеш для переводов. Использую APC.
'Cache' => [
        'translations' => [
            'className' => 'Apc',
            'path' => CACHE,
            'duration' => '+2 days'
        ],
]


В config/bootstrap.php разместил
Объявил переменную $translations чтобы каждый раз не обращаться в кеш.
// указать в начале файла
use Cake\I18n\I18n;
use Cake\ORM\TableRegistry;

$translations = Cache::read('translations', 'translations');

// Custom localization
function __loadTranslations() {
    $languages = [];
    $translations = [];
    $languageList = TableRegistry::get('languages')->find('all', ['fields' => ['locale']])->hydrate(false)->toArray();

    foreach($languageList as $language) {
        $languages[$language['locale']] = TableRegistry::get('i18n_messages')->find('list', ['keyField' => 'singular', 'valueField' => 'value_0'])->where(['locale' => $language['locale']])->order('singular')->hydrate(false)->toArray();   
    }
    foreach($languages['rus'] as $key=>$value) {
        foreach($languageList as $language) { 
            if ($language['locale'] !== 'rus') {
                $translations[$key][$language['locale']] = $languages[$language['locale']][$key];
            }
        }
    }
    Cache::write('translations', $translations, 'translations');
    unset($languages, $translations, $languageList);
}




function __($text = null, $args = null) {
    global $translations;
    $phrase = isset($translations[$text]) ? $translations[$text][Locale::getDefault()] : false; 

    if ($phrase === false) { 
        $messagesTable = TableRegistry::get('i18n_messages');
        $languages = TableRegistry::get('languages')->find('all', ['fields' => ['locale']])->hydrate(false)->toArray();

        foreach($languages as $language) {
            $exists = $messagesTable->find('all', ['fields' => ['id']])->where(['domain' => 'default', 'singular' => $text, 'locale' => $language['locale']])->hydrate(false)->toArray();

            if (empty($exists)) {
                $message = $messagesTable->newEntity();
                $message->domain = 'default';
                $message->singular = $text;
                $message->locale = $language['locale'];

                $messagesTable->save($message);
            }
        }
        return empty($args) ? $text : I18n::translator()->translate($text, $args);
    } else {
        return empty($args) ? (is_null($phrase) ? $text : $phrase) : I18n::translator()->translate(is_null($phrase) ? $text : $phrase, $args);
    }
}

function __d($domain = null, $text = null, $args = null) {
    global $translations;

    $args   = func_num_args() === 3 ? (array) $args : array_slice(func_get_args(), 2);
    $phrase = isset($translations[$text]) ? $translations[$text][Locale::getDefault()] : false; 
    $domain = is_null($domain) ? 'default' : $domain;

    if ($phrase === false) {
        $messagesTable = TableRegistry::get('i18n_messages');
        $languages = TableRegistry::get('languages')->find('all', ['fields' => ['locale']])->hydrate(false)->toArray();

        foreach($languages as $language) {
            $exists = $messagesTable->find('all', ['fields' => ['id']])->where(['domain' => $domain, 'singular' => $text, 'locale' => $language['locale']])->hydrate(false)->toArray();

            if (empty($exists)) {
                $message = $messagesTable->newEntity();
                $message->domain = $domain;
                $message->singular = $text;
                $message->locale = $language['locale'];

                $messagesTable->save($message);
            }
        }
        return empty($args) ? $text : I18n::translator($domain)->translate($text, $args);
    } else {
        return empty($args) ? (is_null($phrase) ? $text : $phrase) : I18n::translator($domain)->translate(is_null($phrase) ? $text : $phrase, $args);
    }
}
if ($translations === false) {
    __loadTranslations();
}


Язык сайта задаю через метод I18n::locale

Буду рад комментариям, может подскажете что не так или как улучшить.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы