Всем доброго времени суток! И все-таки, как я давно сюда не заходил! Был я последний раз тут аж месяц назад, когда опубликовал статью о кросспостинге, но в общем то я не жалею, что со временем у меня были реальные проблемы. Мои проблемы во времени - это мое счастье. Я устроился на человеческую нефрилансерную работу PHP - девелопером, работаю, радуюсь, и все прямо так и прет - очень везет во всем. К слову, я даже переехал из своего города в другой, в общем, все круто изменилось. 

На данный момент моя работа в фирме направлена на мультиязычность наших сайтов - я выкладываю новые переводы сайта, а так же разрабатываю систему автоматизации тиражирования сайта. С учетом всего вышесказанного, я хотел поделиться своими мыслями по поводу "Как сделать мультиязычный сайт?".

Задача: реализовать мультиязычный сайт и выделить в отдельный класс, где: сайт использует шаблоны, сессии и куки; не использует бд для хранения статического контента (если использовать бд - класс модифицируется очень просто); использует файлы для хранения статических страниц и шаблонного контента.

 Рассмотрим структуру такого сайта сверху вниз, т.е. начиная с вывода содержимого сайта в шаблоне и заканчивая методами класса.

<?
//указываем, что сайт будет использовать сессии
@session_start();
//подключаем файл класса
require_once 'system/main.class.php';
//создаем объект класса langSys
$langsys = new langSys;
//управление навигацией
	if (isset($_GET['do'])) {
		$page = $_GET['do'];
		$_SESSION['langsys_page'] = $page;
		header("Location: " . $langsys->domain . '?page=' . $page);
	}
	else if (!isset($_GET['page']) && !isset($_GET['lang'])) {
		$_SESSION['langsys_page'] = $langsys->startPage;
		$langsys->page = $_SESSION['langsys_page'];
	}
//если меняем язык
(!isset($_GET['lang'])) ? '' : $langsys->changeLang($_GET['lang']);
?>
//шаблон
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta name="KeyWords" content="">
    <meta name="Description" content="">
	<meta http-equiv="content-type" content="text/html; charset=utf-8">
	<title>I am Language System</title>
</head>
<body>
	<div style="width:95%;margin:auto;padding-top:15px;">
      //форма для управления языком
		<div style="text-align:center;margin-bottom:15px;">
			<a href="?lang=ru">Rus</a>&nbsp;|&nbsp;
			<a href="?lang=en">Eng</a>
		</div>
      //вывод содержимого страницы
		<div style="float:left;width:80%">
			<?=$langsys->getContentByLang($langsys->page, $langsys->lang)?>
		</div>
      //вывод меню
		<div style="width:10%; margin-left:30px;float:right;">
			<ul>
				<li><a href="?do=about"><?$langsys->translate(1);?></a></li>
				<li><a href="?do=faq"><?$langsys->translate(2);?></a></li>
				<li><a href="?do=advert"><?$langsys->translate(3);?></a></li>
				<li><a href="?do=descript"><?$langsys->translate(4);?></a></li>
			</ul>
		</div>
	</div>
</body>
</html>

Перед вами собственно шаблон сайта, где будет выводиться наш мультиязычный контент. Если идея уже понятна, то хорошо, но все же я хочу пояснить, что же я тут написал и как оно работает.

 

Организация навигации по сайту.

Всем передвижением по сайту управляет два GET - параметра, передаем мы их в адресной строке, как известно. Первый параметр "do" управляет страницами и хранится он в суперглобальном массиве $_SESSION под индексом "langsys_page". Второй параметр “lang“ управляет используемым языком на сайте, хранится он в куки (суперглобальный массив $_COOKIE) под индексом "langsys_lang".

Как происходит перенаправление по страницам и языкам? В этом, кстати, был основной интерес. Проблема заключалась в том, что как только пользователь решал изменить язык сайта, то ему показывалась стартовая страница, а не просто изменялась на другой язык та, которую он просматривал. Это хорошо, если сайт маленький, а если в нем бесчисленное количество статей - потом попробуй найти ту, которую читал. Проблему решил вариант добавления текущей страницы в сессию, то есть наша функция getContentByLang($page, $lang); смотрела в массив сессий, чтобы знать какую страницу показывать, но никак напрямую не общалась с изменение GET – параметра.

Размещение текущей страницы в массиве сессий решило первую проблему, но привлекло другую – теперь если мы находились на странице http://site.ru/?do=about и вдруг решили перейти на заглавную страницу http://site.ru/, то мы останемся и никуда не перейдем. Почему? – мы не передали никакого значения $_GET[‘do’], соответственно текущая страница не изменилась в сессии и сайт никак не отреагировал. Делать главную страницу под ссылкой, например http://site.ru/?do=main глупо, поэтому существует еще одно условие в управлении навигацией: 

else if (!isset($_GET['page']) && !isset($_GET['lang'])) {
		$_SESSION['langsys_page'] = $langsys->startPage;
		$langsys->page = $_SESSION['langsys_page'];
	}

То есть, в случае, когда у нас не установлены GET – параметры page и lang, мы присваиваем элементу массива сессий “langsys_page” значение startPage, которое заранее мы объявили в конструкторе наше класса langSys, но об этом уже далее.

 

Основные конструкции для хранения данных.

В этой системе я воспользовался двумя методами хранения данных: 

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

//английский
<?
$this->arr_lang{$lang} = array(
	1=>'About us',
	2=>'FAQ',
	3=>'Advertisement',
	4=>'Descript'
)
?>
//русский
<?
$this->arr_lang{$lang} = array(
	1=>'О нас',
	2=>'Частые вопросы',
	3=>'Реклама на сайте',
	4=>'Описание'
)
?>

Если вы обратите внимание на шаблон, то можете увидеть, что мы вызывали функцию translate($id). Эта функция как раз и выводит нужное слово, обращаясь в куку за значением текущего языка: 

function translate($id, $lang = '') {
	//если язык на передан в функции
		if ($lang == '')
		  $lang = $this->lang;
	//загружаем языковой файл
		if (!isset($this->loaded[$lang])) {
			include_once $this->rootLang.''.$lang.'/'.$lang.'.php';
			$this->loaded[$lang] = true;
		}
	//дополнительная фишка для показа индекса слова на странице
		if (isset($this->arr_lang{$lang}[$id]) && (isset($_GET['show_index']))){
			($_GET['show_index'] == 'true') ? print '<font color="red" size="5">'.$id.'</font> '.$this->arr_lang{$lang}[$id] : '';
		}
	//вывод слова на страницу
		else if (isset($this->arr_lang{$lang}[$id])) {
			print $this->arr_lang{$lang}[$id];
		}
	//если перевод по id не найден
		else {
		  print $id;
		}
	}

Функции так же можно передавать напрямую значение текущего языка, а не брать его из куки. Нужно для того, чтобы программист мог вручную выбирать, на каком языке показывать то или иное слово/фразу. 

2. Второй вариант хранения информации на сайте предназначен для больших блоков информации, а не отдельных фраз или же слов. По этому методу мы создаем отдельные страницы, например about.php, faq.php и т.д. с обычной html версткой (параграфы, форматирование текста, гиперссылки) и нужным содержимым, которое мы бы хотели видеть на странице. Его можно брать как из этого же файла, так и подгружать из базы. Взял я первое, потому что в моем случае – чем меньше обращений к базе данных, тем лучше. На самом деле, если вы будете использовать подобную структуру на сайте с небольшой нагрузкой, то можно грузить и из базы, если же у вас на сайте до десятков тысяч посещений в день, то соответственно понимаете, что лишняя нагрузка на базу неуместна. 

Сама функция, подгружающая нужную страницу: 

function getContentByLang ($page, $lang) {
			$file = $page.'_'.$lang.'.php';
			include_once $this->rootLang.''.$lang.'/'.$file;
	}

Ничего замысловатого.

 

Смена языка.

В начале этой статьи я рассказывал о том, как организуется навигация по сайту и про то, как меняется язык. Еще раз напоминая, текущий язык записывается в куки под индексом “langsys_lang”, после чего им могут воспользоваться функции, выводящие содержимое сайта, чтобы показать информацию на нужном языке. 

Для того чтобы изменить язык мы используем простое условие, реагирующее на изменение GET – параметра “lang”: 

(!isset($_GET['lang'])) ? '' : $langsys->changeLang($_GET['lang']);

Которое, при получении такового вызывает функцию changeLang($lang) (смена текущего языка): 

function changeLang($lang) {
	//устанавливаем текущий язык
	$this->lang = $lang;
	//записываем в куки его значение
	setcookie("langsys_lang", $this->lang, time()+60*60*24*30);
	//возвращаемся на ту же страницу
	$this->page = $_SESSION['langsys_page'];
}

С языками на этом все, теперь можно перейти к конструктору класса langSys.

 

Конструктор класса langSys.

Когда все вышесказанное было прочитано, можно перейти и к настройкам нашей системы. Для этого используется конструктор, в котором мы можем перечислить все настройки, используемые потом в системе и на сайте в целом, например root – директория или же язык по умолчанию. 

Выглядит это так: 

function __construct() {
		$this->root = $_SERVER['DOCUMENT_ROOT'];
		$this->domain = 'http://' . $_SERVER['HTTP_HOST'];
		$this->rootLang = $this->root . '/lang/';
		$this->startLang = 'ru';
		$this->startPage = 'about';
		$this->page = $_SESSION['langsys_page'];
		if (!isset($_COOKIE['langsys_lang'])) {
			setcookie("langsys_lang", $this->startLang, time()+60*60*24*30);
			header("Location: " . $this->domain);
		}
		$this->lang = $_COOKIE['langsys_lang'];
	}

Я думаю, в этом случае комментарии не нужны. Но скажу, что это все упрощает модификацию, и использование класса в дальнейшем. 

Вот и все, что думал по этому поводу я сказал, надеюсь, это поможет кому-нибудь, с уважением, Я!


Знаете ли вы?

Что человек - единственный представитель животного мира, способный рисовать прямые линии.

KDPsite...