Сайт на нескольких языках. Часть 1. Статический текст.
cakephp, Программирование Добавить коментарийДанная статья предназначена для того, чтобы показать, насколько просто создавать мультиязычные сайты с использованием CakePHP.
Итак - каждый сайт содержит текст в том или ином виде. Текст может быть “статическим” и “динамическим”.
Для начала определимся с тем, что такое “статический текст”. Статический текст - это текст, который не меняется на протяжении существования сайта. То есть он жестко прописан в html-коде страницы. Примерами такого текста могут быть подписи к полям формы и так далее.
Существует много различных подходов к тому, как сделать несколько копий текстовых данных на разных языках и как их потом выводить. Но способ, который предлагает CakePHP, кажется мне наиболее логичным и удобным для использования.
Заранее договоримся, что пользоваться будем CakePHP v1.2.0.5427alpha
Итак - задача: Есть страница, которая доступна по адресу
http://mysite/staticpages/simplepage
Соответственно имеем контроллер
StaticpagesController
метод в нем
function simplepage()
и вид для него
simplepage.ctp
Вид содержит статический текст:
Необходимо, чтобы строка “hello” выводилась на разных языках, в завасимости от настроек. Для примера пусть будет английский и русский языки.
Приступим.
Для начала нам необходимо в папке “app/locale” создать две папки с названиями “eng” и “rus”. Далее в каждой из этих папок создать папку “LC_MESSAGES”, где создать текстовый файл “staticpages.po”. Схема именования файла такая же, как и для контроллера, только без слова “_controller”.
Данные, которые находятся в этом файле, будут доступны только в контроллене Staticpages. Если есть какие-то данные, которые общие для разных контроллеров, их нужно поместить в файл “default.po”
В файл нужно записать (в папке “app/locale/eng/LC_MESSAGES/staticpages.po”):
-
"Project-Id-Version: CakePHP language demon\n"
-
"POT-Creation-Date: \n"
-
"PO-Revision-Date: 2007-09-10 11:01+0200\n"
-
"Last-Translator: Vyacheslav Enis <venis@difane.com>\n"
-
"Language-Team: Difane team <support@difane.com>\n"
-
"MIME-Version: 1.0\n"
-
"Content-Type: text/plain; charset=windows-1251\n"
-
"Content-Transfer-Encoding: 8bit\n"
-
msgid "hello"
-
msgstr "Hello, World!"
Здесь msgid - строка, которая выступает как идентификатор. По нему мы будем обращаться к данной строке.
msgstr - строка, которая является текстом, который соответствует msgid. Остальные пункты определяют кодировку файла, так что при использовании UTF-8 необходимо поменять
-
"Content-Type: text/plain; charset=windows-1251\n"
-
"Content-Transfer-Encoding: 8bit\n"
на
-
"Content-Type: text/plain; charset=utf-8\n"
-
"Content-Transfer-Encoding: 16bit\n"
Аналогично для файла “app/locale/rus/LC_MESSAGES/staticpages.po”:
-
"Project-Id-Version: CakePHP language demon\n"
-
"POT-Creation-Date: \n"
-
"PO-Revision-Date: 2007-09-10 11:01+0200\n"
-
"Last-Translator: Vyacheslav Enis <venis@difane.com>\n"
-
"Language-Team: Difane team <support@difane.com>\n"
-
"MIME-Version: 1.0\n"
-
"Content-Type: text/plain; charset=windows-1251\n"
-
"Content-Transfer-Encoding: 8bit\n"
-
-
msgid "hello"
-
msgstr "Привет, Мир!"
Пол дела сделано.
Теперь открываем файл вида (simplepage.ctp), и меняем строку
на
Как видно, вместо строки “hello”, мы вызываем функцию “__” (да-да, именно так, два подчеркивания) и передаем ей в качестве параметра идентификатор из соответствующего файла (msgid, который в нашем случае равен “hello”).
Все, открываем страницу “http://mysite/staticpages/simplepage” в броузере и видим на экране
Hello, World!
Как видим - работает. И более того - попробуйте изменить язык, который броузер передает, как язык по умолчанию (в опере это “Tools->Preferences”, закладка “General”, поле “Language” внизу.) В зависимости от выбранного языка будет использоваться текст на правильном языке.
Автоопределение языка это конечно хорошо, но должны же быть возможность установить его вручную. И эта возможность есть. Для установки языка нужно вызвать следующую функцию:
-
Configure::write(‘Config.language’, ‘ru’);
Здесь “ru” - идентификатор требуемого языка.
Предугадывая вопрос о том, откуда взять название для директории и название языка для установки, вот таблица для всех языков поддерживаемых данной версией CakePHP. Данные взяты из файла “\cake\libs\l10n.php”.
Языки
| Название языка | Сокращение (используется как имя каталога в LC_MESSAGES) | Сокращение (используется при установке языка) |
|---|---|---|
| Albanian | alb | sq |
| Arabic | ara | ar |
| Basque | baq | eu |
| Bulgarian | bul | bg |
| Byelorussian | bel | be |
| Catalan | cat | ca |
| Chinese | chi | zh |
| Chinese | zho | zh |
| Croatian | hrv | hr |
| Croatian | scr | hr |
| Czech | ces | cs |
| Czech | cze | cs |
| Danish | dan | da |
| Dutch (Standard) | dut | nl |
| Dutch (Standard) | nld | nl |
| English | eng | en |
| Estonian | est | et |
| Faeroese | fao | fo |
| Farsi | fas | fa |
| Farsi | per | fa |
| Finnish | fin | fi |
| French (Standard) | fra | fr |
| French (Standard) | fre | fr |
| Gaelic (Scots) | gla | gd |
| Galician | glg | gl |
| German (Standard) | deu | de |
| German (Standard) | ger | de |
| Greek | ell | el |
| Greek | gre | el |
| Hebrew | heb | he |
| Hindi | hin | hi |
| Hungarian | hun | hu |
| Icelandic | ice | is |
| Icelandic | isl | is |
| Indonesian | ind | id |
| Irish | gle | ga |
| Italian | ita | it |
| Japanese | jpn | ja |
| Korean | kor | ko |
| Latvian | lav | lv |
| Lithuanian | lit | lt |
| Macedonian | mac | mk |
| Macedonian | mkd | mk |
| Malaysian | may | ms |
| Malaysian | msa | ms |
| Maltese | mlt | mt |
| Norwegian | nor | no |
| Norwegian Bokmal | nob | nb |
| Norwegian Nynorsk | nno | nn |
| Polish | pol | pl |
| Portuguese (Portugal) | por | pt |
| Rhaeto-Romanic | roh | rm |
| Romanian | ron | ro |
| Romanian | rum | ro |
| Russian | rus | ru |
| Sami (Lappish) | smi | sz |
| Scots Gaelic | gla | gd |
| Serbian | scc | sr |
| Serbian | srp | sr |
| Slovack | slk | sk |
| Slovack | slo | sk |
| Slovenian | slv | sl |
| Sorbian | wen | sb |
| Spanish (Spain - Traditional) | spa | es |
| Swedish | swe | sv |
| Thai | tha | th |
| Tsonga | tso | ts |
| Tswana | tsn | tn |
| Turkish | tur | tr |
| Ukrainian | ukr | uk |
| Urdu | urd | ur |
| Venda | ven | ve |
| Vietnamese | vie | vi |
| Xhosa | xho | xh |
| Yiddish | yid | yi |
| Zulu | zul | zu |
Вот в принципе и все, если бы не одно “НО”. Иногда необходимо в контроллере установить значение некоторой переменной. И это значение тоже должно быть на нескольких языках. Сразу напрашивается способ:
-
$this->set(‘test’, __($domain, “hello”));
и в виде:
В этом случае получим немного непредсказуемое поведение - строка с идентификатором “hello” будет выведена на экран. Стобы этого избежать, необходимо добавить второй параметр и сделать его равным true
-
$this->set(‘test’, __($domain, “hello”, true));
Но на этом подводные камни не заканчиваются. Таким способом можно установить только текст, который содержится в файлах “default.po”. То есть данный текст должен быть общим для всех доменов.
Попытка использовать текст из “staticpages.po” завершится тем, что в качестве текста будет использована строка “hello”.
Это не ошибка, такое поведение вполне ожидаемо. Функция “__” предназначена для работы со “статическим” текстом. А он в 99,9% случаев содержится в виде. В контроллере же мы работаем с данными, которые получаем из базы (в большинстве случаев), а не жестко прописываем в коде. А для этого есть свой универсальный способ ( о нем разговор пойдет в отдельной статье).
А пока, если все-же жизненно необходимо в контроллере установить значение переменной и идентификатор находится не в “default.po”, а например в “staticpages.po”, то можно воспользоваться следующим способом:
Данный код позвольте оставить без комментариев.
Ниже представлены исходные коды всех необходимых файлов:
app\locale\eng\LC_MESSAGES\staticpages.po
-
"Project-Id-Version: CakePHP language demo\n"
-
"POT-Creation-Date: \n"
-
"PO-Revision-Date: 2007-09-10 11:01+0200\n"
-
"Last-Translator: Vyacheslav Enis <venis@difane.com>\n"
-
"Language-Team: Difane team <support@difane.com>\n"
-
"MIME-Version: 1.0\n"
-
"Content-Type: text/plain; charset=windows-1251\n"
-
"Content-Transfer-Encoding: 8bit\n"
-
-
msgid "hello"
-
msgstr "Hello, World!"
app\locale\rus\LC_MESSAGES\staticpages.po
-
"Project-Id-Version: CakePHP language demo\n"
-
"POT-Creation-Date: \n"
-
"PO-Revision-Date: 2007-09-10 11:01+0200\n"
-
"Last-Translator: Vyacheslav Enis <venis@difane.com>\n"
-
"Language-Team: Difane team <support@difane.com>\n"
-
"MIME-Version: 1.0\n"
-
"Content-Type: text/plain; charset=windows-1251\n"
-
"Content-Transfer-Encoding: 8bit\n"
-
-
msgid "hello"
-
msgstr "Привет, Мир!"
app\controllers\staticpages_controller.php
-
<?php
-
class StaticpagesController extends AppController {
-
function simplepage()
-
{
-
Configure::write(‘Config.language’, ‘ru’);
-
}
-
}
-
?>
app\views\staticpages\simplepage.ctp
Также вы можете скачать исходные коды одним архивом здесь
С Уважением,
Difane-team
Сентябрь 10th, 2007 в 16:44
Отличная статья, спасибо большое! А как просто реализовать переключение языковой среды пользователем? Делать отдельные lang_controller с одной функцией set_lang()? Или можно как-то более щтатными методами обойтись?
Сентябрь 10th, 2007 в 17:24
По поводу переключения языка - следующий код
Можно вызывать в любом месте (в принципе). Как вариант можно сделать так:
1) В AppController добавить функцию beforeFilter:
В “config/routes.php” в начале добавить:
Теперь на все ссылки вида:
http://mysite/ru/controller/action будет устанавливаться рус. язык
http://mysite/en/controller/action будет устанавливаться англ. язык
и т.д.
Сентябрь 10th, 2007 в 19:45
Отличная статья и хорошо оформлена. Надеюсь ты на этом не остановишься и статьи будут появляться регулярно.
Ещё раз спасибо за статью
И ни одного упоминания об i18n? ;-))
Сентябрь 10th, 2007 в 20:00
Спасибо за теплые слова. Будем стараться. Есть еще много о чем рассказать касательно CakePHP.
А по поводу i18n - не все сразу. Статья расчитана на то, чтобы человек, не знакомый с тонкостями, сразу смог получить результат. Подробнее обо всех механизмах - в следующих статьях.
P.S. Кстати, может есть вопросы либо темы, которые будут интересны - пишите, предлагайте. Постараемся осветить все.
Сентябрь 10th, 2007 в 20:37
Программа poEdit (http://www.poedit.net/) сильно облегчит создание .po и .mo файлов.
Сентябрь 11th, 2007 в 08:03
Спасибо за упоминание “poEdit”.
Программа несомненно заслуживает внимания, но работа с ней может показаться немного нетривиальной. И к сожалению есть небольшие нестабильности в ее работе.
Возможно позже будет выложен инструментарий, которым мы пользуемся для своих нужд.
Сентябрь 20th, 2007 в 12:23
А вот небольшое изменение в коде cake, которое позволит тем же механизмом переводить сообщения об ошибках:
в файле /cake/libs/view/helpers/form.php, в функции error, перед строкой (у меня она 341) :
вставляем следующий код:
в модели валидация выглядит так:
В нашем .po файле вставляем
и в результате при использовании функции $form->error (а она же вызывается при всех ошибках валидации в хелпере form) вместо minLength получаем необходимую строку
Сентябрь 26th, 2007 в 17:02
Что-то у меня после добавления в “config/routes.php” строки
$Route->connect(’/:locale/:controller/:action/*’);
появилась ошибка “You are seeing this error because controller Controller could not be found.”
Сентябрь 26th, 2007 в 17:12
Если вы о том, что было сказано в комментарии выше - то там нужно название контроллера заменить на свое.
Сентябрь 26th, 2007 в 18:10
Не помогло…
Я о коментарии №2
Сентябрь 26th, 2007 в 20:03
Разобрался. Извините, тупил (:
Сентябрь 27th, 2007 в 01:14
Не надо извиняться. Не во всем удается разобраться с первого раза. Если у Вас будут возникать какие-либо вопросы - не стесняйтесь, пишите. Постараемся помочь в меру сил.
Сентябрь 27th, 2007 в 18:00
А можно ли сделать так, чтобы значение msgstr бралось из default.po, если соответствующего msgid нету в файле “контроллер”.po?
Сентябрь 27th, 2007 в 18:01
Это так и происходит.
Сентябрь 27th, 2007 в 18:11
У меня почему-то так происходит лишь при отсутствии файла “контроллер”.po…
Сентябрь 28th, 2007 в 09:04
Да, действительно. Изначально проверяется файл “controller”.po и только если его нет - сообщение берется из default.po
Так как данный момент действительно необходим - постараемся выложить решение проблемы.
Октябрь 24th, 2007 в 08:27
Не забывайте про вывод языка текущего в атрибутах lang (и xml:lang для xHTML) тега . Например, чтобы Google знал на каком языке страница :). Или вообще их не используйте, а то странно выглядит страница набранная на русском с атрибутами установленными в “en”. И уж совсем хорошо, если другой язык будете выделять теми же тегами, например на русской странице ссылку на английский вариант делать так
Октябрь 24th, 2007 в 08:28
Чёрт, теги съелись
Октябрь 24th, 2007 в 08:34
VolCh, Спасибо за дополнение. Комментарий поправлен.
Январь 31st, 2008 в 22:57
Уважаемый, когда продолжения ждать? Очень нетерпится,) За статью спаибо!
Февраль 1st, 2008 в 15:08
Спасибо за то, что вы все еще с нами. Постараемся при первой же возможности выкладывать интересные статьи. К сожалению после праздников времени на написание не настолько много, как хотелось бы.
Февраль 1st, 2008 в 18:53
Да, знакомая ситуация, со времением. Удачи вам!
Май 24th, 2008 в 15:03
Привет!
Использую кейк 1.2. Не читаются сообщения из файлов “контроллер”.ро а только из дефолтного. Кто знает в чем может быть проблема?
Спасибо!
Май 24th, 2008 в 15:21
Странно. Должно работать. Предоставить фрагмент примера, где это наблюдается (описать здесь) или выслать минимальный проект. постараемся помочь.
Май 26th, 2008 в 12:05
Не работает. И нигде не читал такого. Только что пробовал с нуля с чистым кейком. не работает. мне тут подсказали юзать метод __d(’ФАЙЛ’, ‘КЛЮЧ’); Это работает… Буду искать дальше… я юзаю кейк версии 1.2.0.6311. Использую все стандартно, даже не представляю что бы тут публиковать…