@Alastor

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

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

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

Как я вижу решение данного вопроса - создать метод какой нибудь свой __perevod() и через него всё переводить. Но вдруго есть более элегантные решения?
  • Вопрос задан
  • 294 просмотра
Решения вопроса 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

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

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

Войти через центр авторизации
Похожие вопросы