$21.41


$53.89


$53.36


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

Что же я имел ввиду, говоря о мультиязычности. Ну, наверняка каждый из моих достопочтенных читателей не раз видели крутые порталы и между всем сбродом информации находили две маленькие иконки, преимущественно со всем известным звёздно-полосатым и родным бело-сине-красным флагами. Безусловно, после нажатия на одну из них привычная нам русская речь превращалась в буржуйский language (), или наоборот. Но задавали ли вы себе когда-либо вопрос о том, как это всё делается. Что ж, именно об этом я и буду вести речь.

Сегодня мы рассмотрим довольно важную тему, которая выдвигается многими работодателями, а именно мультиязычность.


Что же я имел ввиду, говоря о мультиязычности. Ну, наверняка каждый из моих достопочтенных читателей не раз видели крутые порталы и между всем сбродом информации находили две маленькие иконки, преимущественно со всем известным звёздно-полосатым и родным бело-сине-красным флагами. Безусловно, после нажатия на одну из них привычная нам русская речь превращалась в буржуйский 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»,«Добро пожаловать дорогой администратор !»);
    }
?>
  

Итак, на мой взгляд, ничего сложного нет, и всё написанное подчиняется самым банальным законам РНР. Сначала мы делаем проверку, не были ли константы уже объявлены, если были, то не объявляем, в противном же случае объявляем.

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

Допустим, у вас есть большая портальная система или простой сайт, но вы, талантливый программист знающий все аспекты РНР, не являетесь его владельцем, а сделали его под заказ. Владельцем же является полным дизайнером (), который не слухом, не духом о каких-то там программистских тонкостях, но у него есть одно лишь желание, чтобы всё работало, и он мог изменять всё. Насчёт всего, это уже другая история, а вот языковые параметры сайта мы ему всё-таки разрешим изменить (да что там, чем бы дитя ни тешилось ).

Но опять возвращаясь к дилемме о «Дизайнерах и Программистах» нужно опять упомянуть что такой сайт должен полностью быть, так сказать «Что Видишь То И Получишь», иначе нельзя. Поэтому я постараюсь сделать всё так, чтобы оно не вызывало нервного тика у программистов, и могло удовлетворить дизайнеров (имеется ввиду юзабилити).

Итак, долой пустые слова и вперёд на Берлин. Мы начнём с теории. Итак, как же мы будем различать языки у динамического содержания, которое в лучшем случае удаляется, изменяется, а то и чего хорошего вообще накроется. Константами тут никак не обойтись, что же делать?

Я уже слышу витающие вокруг вас мысли.

Лично когда я пытался воплотить это в жизнь, то сначала я это сделал самым нерациональным способом, а именно для перевода статей разделил поля в таблице, которые подлежали переводу надвое (то есть, создал поле_eng и поле_ru) таким образом, и так большие по объёму таблицы превратились просто в непристойно огромные. Поэтому я начал искать альтернативу, и не поверите, нашёл её. Чувствуете уже теплее, да, скоро мы подойдём к самому горячему. Я нашёл выхода из этой ситуации, и сейчас намерен объяснить на пальцах его вам, и то поймёте ли вы его или нет, будет зависеть от вас. Сначала давайте, согласуем все детали.

Для начала нам нужна, будет таблица, в которой будут размещаться данные для перевода. Скажем у нас есть таблица `articles` в которой будут размещены некоторые статьи, и они должны иметь, скажем, два перевода, но один обязательно. Нас будут интересовать лишь два ключевых, в нашем случае, поля: название, описание. Мы будем осуществлять структурирование текста таким образом:
<%eng%>Английский вариант статьи</%eng%>
<%ru%>Русский вариант статьи</%ru%>
  

После строка ввиде комбинации из этих двух структур и будет добавляться в поля `title` и `description` таблицы `articles`.

Данный способ будет заключаться в поиске первого вхождения открывающего ключевого слова (допустим
<%eng%>
), после мы найдём первое вхождение закрывающего ключевого слова. Но нужно не забывать что нам нужно не именно вхождение, а длина конструкции. В первом случае мы к первому вхождению открывающей конструкции будем добавлять длину конструкции, вторым шагом будет нахождение длины закрывающей конструкции. Но вы спросите: .

Воспользовавшись функцией 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% что синтаксис нарушен, так как я не тестировал примеры. А вот здесь для вас действительно хорошая тренировка ведь ловля «блох» очень полезное занятие!

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

 

Интересное

Интернет - трагедия или...
Доброго времени суток, уважаемый читатель. Я бы хотел поведать тебе, да и всем людям, мою точку зрения на влияние Интернета на людей, да и вообще, об Интернете в целом.Естественно, такую тему...
Подробнее...
Компьютеры для офиса
На сегодняшний день компьютеры стали самой естественной и неотъемлемой частью жизни практически для каждого современного человека. Это очень удобно при ведении бизнеса, как маленькой фирмы, так и...
Подробнее...
Новый троян использует...
Специалисты по вопросам компьютерной безопасности предупреждают о появлении новой вредоносной программы под названием Mebroot, при помощи которой злоумышленники теоретически могут захватить полный...
Подробнее...
Забавное программирование...
Приведённый здесь материал можно озаглавить не иначе как «Чем заняться программисту, если нечего делать». На самом деле, Delphi настолько интересная среда, что в ней наряду с разработкой серьёзных...
Подробнее...
25 советов как ускорить...
Цель нижеописанных советов избавить установленную Windows XP Pro Russian от совершенно ненужных для домашнего пользователя украшательств и ускорить в целом работу системы. Эти 25 советов, только...
Подробнее...
Дорога в KDE4
Недавно, а именно 11 января наконец-то состоялся долгожданный релиз 4 версии популярного рабочего окружения KDE (KDE – это K Desktop Environment «К-интегрированная рабочая...
Подробнее...
CGI Perl FAQ
Что такое CGI и зачем мне все это нужно, если я не умею программировать, да и учиться на старости лет нет особого желания?CGI (Common Gateway Interface) — это шлюз, с помощью которого...
Подробнее...
Введение в протокол UDP
UDP (User Datagram Protocol, Протокол дейтаграмм пользователя) предназначен для обмена дейтаграммами между процессами компьютеров, входящих в единую сеть с коммутацией пакетов. В качестве...
Подробнее...
Как пользоваться модулем...
Наверное, все из вас знают что такое интернет, сайт и Apache (веб-сервер). Немного углубившись в структуру веб-сервера скажу, что у апача есть еще и много модулей, которые обеспечивают...
Подробнее...
Реальный домен виртуальной...
Краткая хронологияПроцедура регистрации домена .SU была инициирована в середине августа 1990 года, до окончательной регистрации дело дошло 19 сентября. Забавно, что почти ровно через год произошли...
Подробнее...