Ошибка: Failed to parse the Currency Converter XML document.
$30 689.39


Ошибка: Failed to parse the Currency Converter XML document.
$1 351.02


Ошибка: Failed to parse the Currency Converter XML document.
$2 614.25


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

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

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

 

Интересное

Установка IIS на Windows...
Internet Information Services (IIS) 6.0 – полнофункциональный веб-сервер, обеспечивающий основу для систем семейства Windows Server 2003, а также существующих веб-приложений и веб-служб XML. В IIS...
Подробнее...
Ещё о защите e-mail...
Оригинальная защита e-mail адресов от сканирования спам-роботами. Мы не будем кодировать адреса, мы будем их… рисовать!О защите e-mail адресов от сканирования спам-роботами в Интернете писалось...
Подробнее...
Программное управление...
ВведениеТема нашего сегодняшнего разговора — ODBC. Если вы хоть раз писали СУБД (системы управления базами данных), то вам, наверное, знакома эта аббревиатура. Расшифровывается ODBC как Open...
Подробнее...
Синхронный перевод
В современных условиях при широком общении людей, для которых родные языки не совпадают, все большую популярность приобретает синхронный перевод. Он, конечно, использовался и раньше, но...
Подробнее...
Эксплойты
Эксплойт (англ. exploit — использовать) — это общий термин в сообществе компьютерной безопасности для обозначения фрагмента программного кода который, используя возможности предоставляемые...
Подробнее...
Windows Vista - архивация...
В Windows Vista добавлены новые ценные возможности, помогающие избежать потери важной информации. Windows Vista предлагает несколько уровней архивации и восстановления данных, обеспечивающих...
Подробнее...
Модемы: Наиболее часто...
Если у вас используется модем со стандартной прошивкой любого исполнения, кроме us/canada, то изменяя состояние регистра s39, можно регулировать уровень выходного сигнала. Чем меньшее число...
Подробнее...
FAQ по методам...
Многие, насмотревшись фильмов про «хакеров», где половина вымысла имеют искаженное представление про взломы удаленных компьютеров. Скажу сразу, что не каждый камп можно ломануть. Хотя, может я...
Подробнее...
Динамика украинских...
Первое полугодие 2007 характеризуется позитивной динамикой развития домена .UA – сообщает администратор домена .UA ООО «Хостмастер». За период с 01.01.07 по 01.07.07 количество...
Подробнее...
Поисковый маркетинг губит...
Что такое «маркетинг веб-сайта»? В 99% случаев первое, что приходит на ум — это SEM, то есть поисковый маркетинг, во многом из-за сходства терминов. Однако подобная логика часто приводит к...
Подробнее...