в общем
этот плагин просто выгружает из таблицы переводы. а заносить их надо всё равно руками.
Что делает моё решение:
а) Переписывает стандартные __ и __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
Буду рад комментариям, может подскажете что не так или как улучшить.