Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Программы Обои   Экспорт RSS E-Books
 
 

  Раздел: Компьютерная документация -> Интернет технологии -> PHP

 

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

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

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

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

Автор: Карпенко Кирилл
Источник: www.codenet.ru

Ссылки по теме
Внутренние функции
Обработка строк в РНР
PHP и всё такое...
Пишем PHP код, устойчивый к ошибкам
Полезные скрипты на PHP
Почтовые функции в РНР
Полезные функции для работы с файловой системой

Вся документация PHP

 

Компьютерная документация от А до Я - Главная

 

 
Интересное в сети
 
10 новых программ
CodeLobster PHP Edition 3.7.2
WinToFlash 0.7.0008
Free Video to Flash Converter 4.7.24
Total Commander v7.55
aTunes 2.0.1
Process Explorer v12.04
Backup42 v3.0
Predator 2.0.1
FastStone Image Viewer 4.1
Process Lasso 3.70.4
FastStone Image Viewer 4.0
Xion Audio Player 1.0.125
Notepad GNU v.2.2.8.7.7
K-Lite Codec Pack 5.3.0 Full


Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
Реклама на сайте. Размещая рекламу у нас, вы получите новых посетителей, которые могут стать вашими клиентами.
 
Это интересно
 

Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.