Есть вопрос?
Зайди на форум

Поиск на сайте: Advanced

Denix - новый дистрибутив Linux. Русификация Ubuntu и установка кодеков

dkws.org.ua
Форум сайта dkws.org.ua
 
Главная    ТемыТемы    АльбомАльбом    РегистрацияРегистрация 
 ПрофильПрофиль   Войти и проверить личные сообщенияВойти и проверить личные сообщения   ВходВход 

Многоязычность в РНР

 
Начать новую тему Ответить на тему    Список форумов dkws.org.ua -> PHP
 
Автор Сообщение
den

Старожил


Зарегистрирован: 31.01.2006
Сообщения: 13870
Откуда: Кировоград, Украина

СообщениеДобавлено: Вс Dec 10, 2006 2:19 pm    Заголовок сообщения: Многоязычность в РНР
Ответить с цитатой

Автор: К.Карпенко
Привет всем читателям!
Сегодня мы рассмотрим довольно важную тему, которая выдвигается многими работодателями, а именно мультиязычность.
Что же я имел ввиду, говоря о мультиязычности.
Ну, наверняка каждый из моих достопочтенных читателей не раз видели крутые порталы и между всем сбродом информации находили две маленькие иконки, преимущественно со всем известным звёздно-полосатым и родным бело-сине-красным флагами. Безусловно, после нажатия на одну из них привычная нам русская речь превращалась в буржуйский language (), или наоборот.
Но задавали ли вы себе когда-либо вопрос о том, как это всё делается.
Что ж, именно об этом я и буду вести речь.
Сразу скажу, что для работы с тем материалом, который будет здесь изложен вам понадобиться поддержка РНР не ниже 4.39. Итак, как вы знаете, содержание нашего сайта разделяется на динамическое и статическое. К статическому содержанию мы отнесём то, что не будет изменять своё значение в процессе работы (ключевые слова, текст ошибок, и прочая белиберда). С этого мы и начнём. Но давайте проанализируем, как именно мы будем изменять язык данного текстового значения. Надеюсь, никто не предложил воспользоваться исключениями, ибо это настолько нерационально, что нерациональней и быть не может. Вместо этого я предлагаю воспользоваться константами (о типе данных читайте на php.net). Мы просто будем объявлять служебное слово, которое в зависимости от значения языка соответственно будет менять и своё значение. Как мы это сделаем? Да как и все, создадим два (к примеру) разных файла, имена которых будут носить такой шаблон: Язык_map.php; Как вы уже поняли вместо слова `язык`
мы подставим значение, характеризующее данный язык. В нашем случае мы будем использовать двухсимвольный код языка (ru, en, ua, pl и т.д.). Что ж теорию выяснили теперь давайте, применим наши знания на практике. Создаём два файла. Я создал файлы с английским переводом и русским, а как создадите вы это уже на ваш вкус. Файл: en_map.php

<?
If(!defined(“DEF”)){
Define(“DEF”,true);
Define(“TIME_ELAPCED”,”Time what you can spend in this site was elapced !”);
Define(“ADMIN_WELCOM”,”Welcom dear administrator !”);
//И хватит
}
?>
Файл: ru_map.php

<?
If(!defined(“DEF”)){
Define(“DEF”,true);
Define(“TIME_ELAPCED”,”Время которое вы можете проводить на сайте истекло!”);
Define(“ADMIN_WELCOM”,”Добро пожаловать дорогой администратор !”);
}
?>
Итак, на мой взгляд, ничего сложного нет, и всё написанное подчиняется самым банальным законам РНР. Сначала мы делаем проверку, не были ли константы уже объявлены, если были, то не объявляем, в противном же случае объявляем. Это была лёгкая часть, теперь давайте перейдём к более сложной теме - к переводу динамической части. Допустим, у вас есть большая портальная система или простой сайт, но вы, талантливый программист знающий все аспекты РНР, не являетесь его владельцем, а сделали его под заказ. Владельцем же является полным дизайнером Smile, который не слухом, не духом о каких-то там программистских тонкостях, но у него есть одно лишь желание, чтобы всё работало, и он мог изменять всё. Насчёт всего,
это уже другая история, а вот языковые параметры сайта мы ему всё-таки разрешим изменить (да что там, чем бы дитя ни тешилось ). Но опять возвращаясь к дилемме о «Дизайнерах и Программистах» нужно опять упомянуть что такой сайт должен полностью быть, так сказать «Что Видишь То И Получишь», иначе нельзя. Поэтому я постараюсь сделать всё так, чтобы оно не вызывало нервного тика у программистов, и могло удовлетворить дизайнеров (имеется ввиду юзабилити). Итак, долой пустые слова и вперёд на Берлин. Мы начнём с теории. Итак, как же мы будем различать языки у динамического содержания, которое в лучшем случае удаляется, изменяется, а то и чего хорошего вообще накроется. Константами тут никак не обойтись, что же делать? Я уже слышу витающие вокруг вас мысли Smile Лично когда я пытался воплотить это в жизнь, то сначала я это сделал самым нерациональным способом, а именно для перевода статей разделил поля в таблице, которые подлежали переводу надвое (то есть, создал поле_eng и поле_ru) таким образом, и так большие по объёму таблицы превратились просто в непристойно огромные. Поэтому я начал искать альтернативу, и не поверите, нашёл её. Чувствуете уже теплее, да, скоро мы подойдём к самому горячему. Я нашёл выхода из этой ситуации, и сейчас намерен объяснить на пальцах его вам, и то поймёте ли вы его или нет, будет зависеть от вас. Сначала давайте, согласуем все детали. Для начала нам нужна, будет таблица, в которой будут размещаться данные для перевода. Скажем у нас есть таблица `articles` в которой будут размещены некоторые статьи, и они должны иметь, скажем, два перевода, но один обязательно. Нас будут интересовать лишь два ключевых, в нашем случае, поля: название, описание. Мы будем осуществлять структурирование текста таким образом:

<%eng%>Английский вариант статьи</%eng%>
<%ru%>Русский вариант статьи</%ru%>
После строка ввиде комбинации из этих двух структур и будет добавляться в поля `title` и `description` таблицы `<b>articles</b>`. Данный способ будет заключаться в поиске первого вхождения открывающего ключевого слова (допустим <%<b>eng</b>%>), после мы найдём первое вхождение закрывающего ключевого
слова. Но нужно не забывать что нам нужно не именно вхождение, а длина конструкции. В первом случае мы к первому вхождению открывающей конструкции будем добавлять длину конструкции, вторым шагом будет нахождение длины закрывающей конструкции. Но вы спросите: «Как же мы получим текст?». Воспользовавшись функцией substr(). В качестве первого параметра будет сам текст, в качестве второго длина открывающей конструкции, в качестве третьего (самое интересное) разница между первым вхождением закрывающей конструкции и длинной текста. Да, понимаю это не так легко, но это нужно понять. Поэтому мы сейчас это и проделаем на практике. Я создал функцию, которая будет выделять текст между ключевыми тегами. Она будет принимать три параметра: текст для разбора, язык по которому нужно проводить парсинг, массив конструкций.

<?
function subTextByLang($data,$lang,
$delimiters=array('<%','%>','<%/','%>')){
$start_tag=
strpos($data,$delimiters[0].$lang.$delimiters[1])+
strlen($delimiters[0].$lang.$delimiters[1]);
$count=(strpos($data,$delimiters[2].$lang.$delimiters[3])-strlen($data))
$data=substr($data,$start_tag,$count);
if(trim($data)==''){
$data=NOT_ENTERED;
}
return $data;
}
?>
Как видите довольно длинно и можно запутаться, но если вы и не поняли этого, то это не большая беда, ибо функция для перебора уже есть, а чуть дальше я рассмотрю другой метод для этой же цели. Да, и не забудьте где-нибудь объявить языковую константу NOT_ENTERED, которая будет присваиваться результату работы функции в случае, если длина текста равна нулю. Так, с перебором выяснили, но теперь перед нами предстаёт новая задача, компиляция обычного текста в спецформатированую строку. Это уже намного проще, и если вы достаточно хорошо знаете РНР, то вы без труда напишите такую функцию, а если пока плаваете, то прошу в кабинет . Алгоритм не сложный и заключён в том, чтобы подставить в все языковые конструкции в одну строку. Сначала я в порывах лени я хотел ограничить скрипт определённым числом языков (так его воплотить легче), однако после одумался и получил вот что:

<?
function compilateLanguageString($data,
$delimiters=array('<%','%>','<%/','%>')){
if(!is_array($data)){
die(PARAM_CHECK_ERROR);
}
$data=’’;
$temp=’’;
$count=0;
foreach($data as $k=>$v){
if(!is_string($k)){
break;
}
$count++;
if($count>1 & $temp=$k){
die(ERROR_CONSTRUCTION_COUNT);
}
$temp=$k;
$data.=
$delimiters[0].$k.$delimiters[1].$v.$delimiters[2].$k.$delimiters[3];
}
return $data;
}
?>
Ну, здесь я немного поясню. В качестве параметра функция принимает массив. Структура массива должна быть такой:
“индификатор языка”=>”текст”; После мы делаем проверку, что если полученный параметр не массив то «пока Вася !». Если же это всё же массив то конечно делаем его перебор, и на место языка в конструкции ставим ключ данного элемента ассоциативного массива, а на место текста безусловно сам текст а то есть значение переменной $v. После сливаем все данные в одну строку.
Но я забыл упомянуть об одной важной детали, а другими словами о довольно большом куске текста. Сначала перед циклом мы объявили три переменные: data, temp, count;
Переменная count- это количество итераций цикла, и с каждым следующим кругом цикла счётчик увеличивается.
Переменная data- это будущая результирующая строка, в которую будут сливаться все языковые конструкции. Но более интересны переменные count и temp. Для чего они нужны?
Ну, наверное, большинство уже догадались, прочтя исходник, но тем до кого ещё не «дошло» я поясню. Это делается для проверки того, что языковая конструкция не была повторена более раза. Для этого мы и объявили переменную count. Так как её значение по умолчанию равно нулю, то мы проверяем, что цикл был выполнен хотя бы раз, поскольку если мы этого не сделаем то, выйдет что-то подобное 2=2 или 0=0, ведь значение $k ещё не успело измениться. Так как в первый раз проверка будет игнорироваться, мы после проверки присваиваем значение переменной $temp. Это делается так же не просто так. При первой итерации всё пойдёт нормально, но ведь если мы всё же присвоили значение до проверки, то проверка делала бы проверку, о которой уже упоминалось (2=2, 3=3 и т.д.). Вот зачем мы делаем именно так. Теперь как логическое завершение мы создадим небольшой сайт, где и будет применяться всё вышеизложенное:

<?
function subTextByLang($data,$lang,
$delimiters=array('<%','%>','<%/','%>')){
$data=substr(
$data,
(strpos($data,
$delimiters[0].$lang.$delimiters[1])+
strlen($delimiters[0].$lang.$delimiters[1])),
(strpos($data,$delimiters[2].$lang.$delimiters[3])-strlen($data))
);
if(trim($data)==''){
$data=NOT_ENTERED;
}
return $data;
}
function compilateLanguageString($data,
$delimiters=array('<%','%>','<%/','%>'))
{
if(!is_array($data)){
die(PARAM_CHECK_ERROR);
}
$data=’’;
$temp=’’;
$count=0;
foreach($data as $k=>$v){
if(!is_string($k)){
break;
}
$count++;
if($count>1 & $temp=$k){
die(ERROR_CONSTRUCTION_COUNT);
}
$temp=$k;
$data.=$delimiters[0].$k.$delimiters[1].$v.$delimiters[2].$k.$delimiters[3];
}
return $data;
}
//Не забываем о «статике»
if(!isset($_GET['lang'])){
setcookie("lang",$_GET['lang']);
header("Location: index.php?module=home");
}
if(isset($_COOKIE[‘lang’])){
include $_COOKIE[‘lang’].”_map.php”;
}else{
include “ru_map.php”:
}
if(isset($_POST[‘add’])){
$description=
compilateLanguageString(array($_POST[‘description_en’],
$_POST[‘description_ru’]));

$title=compilateLanguageString(array($_POST[‘titlte_eng’],$_POST[‘title_ru’]));
//Процесс добавления в базу
}
echo”<html>”;
echo”<head>”;
echo”<title>”;
$title=($_SERVER[‘REMOTE_ADDR’]==’127.0.0.1’)? ADMIN_WELCOM: ‘Гостям- Здрасте !’;
echo $title;
echo”</title>”;
echo”<meta http-equiv=\”Content-Type\” Content=\”text/html;
charset=”.CURR_CHARSET.”\”>”;
echo”</head>”:
echo”<body>”;
$conn_id=@mysql_connect(“localhost”,”root”,””);
@mysql_select_db(“somedatabase”);
$q=@mysql_query(“SELECT title, description FROM `articles` LIMIT 0,1”,$conn_id);
if(@mysql_ num_rows($q)==0){
ARTCILES_NOT_FOUNDED;
}else{
$row=@mysql_fetch_array($q);
$title=subTextByLang($row[‘title’],$lang);
$description=subTextByLang($row[‘description’],$lang);
echo”<table width=\”400\” height=\”50\” align=\”center\”>”:
echo”<tr><td>”.
ARTICLE_TITLE_TEXT.”</td><td>”.$title.”</td></tr>”;
echo”<tr><td colspan=\”2\”
style=\”text-align:center\”>”.ARTICLE_DESCRIPTION_TEXT.”</td></tr>”;
echo”<tr><td colspan=\”2\”>”.$description.”</td></tr>”;
echo”</table>”;
}
@mysql_close($conn_id);
//Это ещё полбеды, теперь нужно создать форму для добавления статьи
echo”<form action=\”\” method=\”post\”>”;
echo”<table width=\”400\” height=\”50\” align=\”center\”>”;
echo”<tr><td>”.ARTICLE_TITLE_TEXT.”(EN):</td><td>
<input type=\”text\” name=\”title\”></td></tr>”;
echo”<tr><td>”.ARTICLE_TITLE_TEXT.”(RU):</td><td>
<input type=\”text\” name=\”title\”></td></tr>”;
echo”<tr>
<td colspan=\”2\” style=\”text align:center\”>”.
ARTICLE_DESCRIPTION_TEXT.”(EN):</tr></tr>”;
echo”<tr><td colspan=\”2\” >”;
echo”<textarea name=\”description_eng\” rows=\”5\” cols=\”50\”>
English description</textarea>”;
echo”</td></tr>”;
echo”<tr><td colspan=\”2\” style=\”text-align:center\”>”.
ARTICLE_DESCRIPTION_TEXT.”(RU):</tr></tr>”;
echo”<tr><td colspan=\”2\” >”;
echo”<textarea name=\”description_ru\” rows=\”5\” cols=\”50\”>
Русское описание</textarea>”;
echo”</td></tr>”;
echo”<tr><td colspan=\”2\”>
<input type=\”submit\” name=\”add\” value=\”Добавить\”>
</td></tr>”;
echo”</table>”;
echo”</form>”;
?>
Что ж, вот и всё. Однако в скрипте есть одно «но», автор не может через форму добавить более двух вариантов перевода. Не буду, как остальные автора, что сделал это для вашей тренировки, поскольку если честно то когда я дошёл до этого места у меня уже голова почти не варила, поэтому я и оставляю это на ваших плечах.
Поверьте, вариантов решения полно, и я очень надеюсь, что вы его найдёте. Относительно функций, то не могу сказать на все 100% что они не вызовут сбоя но фатальных ошибок быть не должно, хотя всякое бывает.
Но я уверен более чем на 60% что синтаксис нарушен, так как я не тестировал примеры. А вот здесь для вас действительно хорошая тренировка ведь ловля «блох» очень полезное занятие!

Что ж, я считаю, что на этом статью можно окончить. Если у вас не будет получаться, не сгоняйте зло на ваш бедный компьютер, на клавиатуру, и тем более на разработчиков такого замечательного языка как РНР, смело, все свои неудачи адресуйте в мою сторону. Я не думаю, что мне от этого станет хуже, а вот вам будет на кого согнать злость . Но я всё же надеюсь, что вам не придется доходить до этого и у вас всё получиться. А если будут вопросы, то смело задавайте мне их на Ik1990@list.ru Желаю всего самого чудного Карпенко Кирилл.

P.S.
Пользуясь привилегией автора, хочу поблагодарить курсы МКА за то, что они есть, а в частности Новикова Владислава Евгеньевича, он сыграл огромную роль в становлении меня как программиста, и всё ещё её играет
Вернуться к началу
Посмотреть профиль Отправить личное сообщение dhsilabs@jabber.ru
Артур

Новенький


Зарегистрирован: 14.10.2007
Сообщения: 14

СообщениеДобавлено: Пт Dec 07, 2007 5:36 pm    Заголовок сообщения:
Ответить с цитатой

Не проще ли было создать сессию в которой хранить значения языка . В зависимости от значения сессии открывать страничку на русском,на английском и т.д.Предполагается что для каждой странички несколько версий на разных языках (Например rus_index.php,eng_index.php,french_index.php).
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Maxim

Участник тусовки


Зарегистрирован: 22.02.2006
Сообщения: 245

СообщениеДобавлено: Сб Dec 08, 2007 7:32 pm    Заголовок сообщения:
Ответить с цитатой

Артур, не проще. Трудно объяснить почему, но не проще. К тому же это подход HTML - сделать кучу разных версий одной страницы для каждого конкретного случая.

Хотя описанный поход не самый передовой и производительный. Есть более гибкие и/или простые решения, а вообще это называется буржуйским словом локализация (l10n - на программерском диалекте), это одна из самых сложных задач, которые приходиться решать при проектировании web страниц.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Показать сообщения:   
Начать новую тему Ответить на тему    Список форумов dkws.org.ua -> PHP Часовой пояс: GMT
Страница 1 из 1
 Главная страница сайта
 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
© Колисниченко Денис