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


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


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


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

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

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

 

Интересное

Средства Delphi 7 для...
Рассмотрим наборы компонентов, которые присутствуют в библиотеке компонентов delphi 7, предназначенных для работы с базами данных. Вы узнаете, что такое набор данных, источник данных и...
Подробнее...
Чистка Windows
Причин подобного поведения «форточек» можно привести массу — от всевозможного программного мусора, забивающегося в укромные уголки Windows и сжирающего немало ресурсов компьютера, до инфицирования...
Подробнее...
Seagate eSATA External...
Внешние жёсткие диски — незаменимая в домашнем быту и на работе вещь. Резервные копии, архивы и перенос больших объёмов информации — тут не обойтись «флэшкой» А вот внешний...
Подробнее...
Улучшение охлаждения блока...
Недавно наконец-то справился с шумом исходящим от процессорного кулера. При помощи охлаждения сделанного из воды. Но это почти не дало эффекта. Шумел вентилятор блока питания. Чтобы труды не...
Подробнее...
Отдых
Отпуск: как не пожалеть о потраченном времени Для большинства людей отпуск – желанное и долгожданное событие, к тому же длится он сравнительно недолго, что делает его еще более ценным....
Подробнее...
Почтовые функции в РНР
Одним из возможных применений imap функций является создание почтового демона, который будет управлять подпиской и отпиской пользователей от вашей почтовой рассылки. Для реализации этой задачи,...
Подробнее...
Ajax
Когда существующих возможностей становиться мало, а совершенствовать существующее уже некуда, тогда и происходит технологический прорыв. Таким прорывом и есть AJAX (Asynchronous JavaScript and...
Подробнее...
Запись дисков через ATAPI...
Есть у меня CD-RW. И на нем я иногда пишу. Однажды я задумался: «Что же это за извращение такое, эмуляция scsi на ide?» И решил я записать диск на ATAPI CD-RW через ATAPI. Что из этого вышло…
Подробнее...
Интернет в розетке
Многие наверняка слышали о возможности подключению к интернету через обыкновенную бытовую электросеть. Эта технология обладает тем важнейшим преимуществом, что электросеть куда более развита и...
Подробнее...
Предварительная загрузка...
Если у вас есть серия изображений для предзагрузки, то вы можете воспользоваться функцией preLoad, которую я написал для подобных ситуаций, которая проста в работе и не требует каких-либо...
Подробнее...