Навигация
Главная
Поиск
Форум
FAQ's
Ссылки
Карта сайта
Чат программистов

Статьи
-Delphi
-C/C++
-Turbo Pascal
-Assembler
-Java/JS
-PHP
-Perl
-DHTML
-Prolog
-GPSS
-Сайтостроительство
-CMS: PHP Fusion
-Инвестирование

Файлы
-Для программистов
-Компонеты для Delphi
-Исходники на Delphi
-Исходники на C/C++
-Книги по Delphi
-Книги по С/С++
-Книги по JAVA/JS
-Книги по Basic/VB/.NET
-Книги по PHP/MySQL
-Книги по Assembler
-PHP Fusion MOD'ы
-by Kest
Professional Download System
Реклама
Услуги

Автоматическое добавление статей на сайты на Wordpress, Joomla, DLE
Заказать продвижение сайта
Программа для рисования блок-схем
Инженерный калькулятор онлайн
Таблица сложения онлайн
Популярные статьи
OpenGL и Delphi... 65535
Форум на вашем ... 65535
21 ошибка прогр... 65535
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Invision Power ... 65535
Пример работы с... 65535
Содержание сайт... 65535
ТЕХНОЛОГИИ ДОСТ... 65535
Организация зап... 65535
Вызов хранимых ... 65535
Создание отчето... 65535
Имитационное мо... 65535
Программируемая... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Реклама
Сейчас на сайте
Гостей: 6
На сайте нет зарегистрированных пользователей

Пользователей: 13,370
новичок: dogoco6
Новости
Реклама
Выполняем курсовые и лабораторные по разным языкам программирования
Подробнее - курсовые и лабораторные на заказ
Delphi, Turbo Pascal, Assembler, C, C++, C#, Visual Basic, Java, GPSS, Prolog, 3D MAX, Компас 3D
Заказать программу для Windows Mobile, Symbian

Моделирование станции технического обслуживания на GPSS + Отчет
Моделирование системы управления качеством производственного процесса на...
Моделирование работы класса персональных компьютеров на GPSS + Отчет + Б...

Многоязычность в РНР
Автор: Кирилл Карпенко
21 июня 2006 года

Привет всем читателям!

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

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

Что ж, я считаю, что на этом статью можно окончить. Если у вас не будет получаться, не сгоняйте зло на ваш бедный компьютер, на клавиатуру, и тем более на разработчиков такого замечательного языка как РНР, смело, все свои неудачи адресуйте в мою сторону. Я не думаю, что мне от этого станет хуже, а вот вам будет на кого согнать злость.
Опубликовал Kest November 06 2008 16:47:58 · 0 Комментариев · 6043 Прочтений · Для печати

• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •


Комментарии
Нет комментариев.
Добавить комментарий
Имя:



smiley smiley smiley smiley smiley smiley smiley smiley smiley
Запретить смайлики в комментариях

Введите проверочный код:* =
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Нет данных для оценки.
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Поделиться ссылкой
Фолловь меня в Твиттере! • Смотрите канал о путешествияхКак приготовить мидии в тайланде?
Загрузки
Новые загрузки
iChat v.7.0 Final...
iComm v.6.1 - выв...
Visual Studio 200...
CodeGear RAD Stud...
Шаблон для новост...

Случайные загрузки
Защита от спама ...
iChat v.7.0 Final...
ActiveX в Delphi
MpegPlay
Domen Name IP
Советы от Даниилы...
3D Октаэдр
PHP, MySQL и Drea...
Язык программиров...
Delphi и технолог...
HtmlLerz PRO
Правила программи...
Фундаментальные а...
AVIwriter
DragMe [Исходник ...
C++ Стандартная б...
Удаление своего EXE
Ильдар Хабибуллин...
AddPage [Исходник...
Приемы программир...

Топ загрузок
Приложение Клие... 100774
Delphi 7 Enterp... 97836
Converter AMR<-... 20268
GPSS World Stud... 17014
Borland C++Buil... 14191
Borland Delphi ... 10291
Turbo Pascal fo... 7374
Калькулятор [Ис... 5984
Visual Studio 2... 5207
Microsoft SQL S... 3661
Случайные статьи
Файл компоновки ви...
Возможность обыгра...
, специализирующая...
Если вы не перевед...
Функции to_string(...
Аутентификация в с...
Пион уклоняющийся ...
Protocol* SNTP)
Что визуализирует ...
Объектно-ориентиро...
Протокол RPC
Займ под залог
Основные понятия о...
Типичные примеры в...
Септик
Символьный (литерн...
10 Заблуждений при...
Казино Jet
Операции управлени...
Программирование п...
Пассивные интерфей...
Терминология языка...
Функции
Процедуры ShowMess...
Создание рекламног...
Статистика



Друзья сайта
Программы, игры


Полезно
В какую объединенную сеть входит классовая сеть? Суммирование маршрутов Занимают ли таблицы память маршрутизатора?