PHP простой подход к переводу - ваше мнение
Я занимаюсь веб-сайтом PHP без использования каких-либо фреймворков. Мне нужно, чтобы сайт был доступен на нескольких языках, и я читал об этом, и, похоже, он немного запутан. Существует несколько решений, но все они зависят от конкретной структуры.
Что вы думаете об использовании простой функции перевода, как показано ниже?
Я имею в виду, я хотел бы знать, что может быть недостатком использования такого кода.
Вот он (это простой и неполный образец):
class Translator{
private $translations;
public function __construct(){
$this->translations = array(
'Inbox' => array(
'en' => 'Inbox',
'fr' => 'the french word for this'
),
'Messages' => array(
'en' => 'Messages',
'fr' => 'the french word for this'
)
//And so on...
);
}
public function translate($word,$lang){
echo $this->translations[$word][$lang];
}
}
Ответы
Ответ 1
Это не выглядит плохо. Я видел это много раз.
Я бы выделил разные строки в одном файле на каждый язык. По крайней мере, или если файлы становятся большими, по одному файлу на модуль на язык.
Затем ваш класс перевода может загружать и кэшировать языковые файлы (если вы не полагаетесь на какую-либо другую систему кеширования) каждый раз, когда будет использоваться новый язык.
Небольшой пример того, что я имею в виду
class Translator {
private $lang = array();
private function findString($str,$lang) {
if (array_key_exists($str, $this->lang[$lang])) {
return $this->lang[$lang][$str];
}
return $str;
}
private function splitStrings($str) {
return explode('=',trim($str));
}
public function __($str,$lang) {
if (!array_key_exists($lang, $this->lang)) {
if (file_exists($lang.'.txt')) {
$strings = array_map(array($this,'splitStrings'),file($lang.'.txt'));
foreach ($strings as $k => $v) {
$this->lang[$lang][$v[0]] = $v[1];
}
return $this->findString($str, $lang);
}
else {
return $str;
}
}
else {
return $this->findString($str, $lang);
}
}
}
Это будет искать .txt файлы, названные в честь языка, содержащего такие записи, как
Foo = FOO
Bar = BAR
Он всегда возвращается к исходной строке, если он не находит перевода.
Это очень простой пример. Но, по моему мнению, нет ничего плохого в этом, если у вас нет необходимости в более крупной структуре.
Чтобы использовать его гораздо проще, вы всегда можете сделать это и создать файл с именем "EN_Example.txt"
class Example extends Translator {
private $lang = 'EN';
private $package = 'Example';
public function __($str) {
return parent::__($str, $this->lang . '_' . $this->package);
}
}
Иногда вы хотите перевести строки, содержащие переменные. Один из таких подходов - это тот, который я нахожу достаточно простым для использования время от времени.
// Translate string "Fox=FOX %s %s"
$e = new Example();
// Translated string with substituted arguments
$s = printf($e->__('Fox'),'arg 1','arg 2');
Чтобы дополнительно интегрировать подстановку переменных, функция printf может быть помещена внутри функции __()
, подобной этой
public function __() {
if (func_num_args() < 1) {
return false;
}
$args = func_get_args();
$str = array_shift($args);
if (count($args)) {
return vsprintf(parent::__($str, $this->lang . '_' . $this->package),$args);
}
else {
return parent::__($str, $this->lang . '_' . $this->package);
}
}
Ответ 2
Есть несколько вещей, которые вы не учли:
- Вы просто переводите отдельные слова? Что относительно структуры предложения и синтаксиса, который отличается между языками?
- Что вы делаете, когда слово или предложение еще не переведено на язык?
- Поддерживаются ли ваши переводы в переменных? Порядок слов в предложении может различаться на разных языках, и если у вас есть переменная, то обычно недостаточно будет просто разбить слово вокруг предложения.
Есть два решения, которые я использовал и рекомендую для PHP:
- gettext - хорошо поддерживается на нескольких языках
- intsmarty - на основе шаблонов Smarty
Ответ 3
Хорошо, если вы не используете фреймворк. Единственная проблема, которую я вижу с вашей функцией, заключается в том, что она загружает много данных в память. Я бы рекомендовал иметь массивы для каждого языка, таким образом вам нужно было бы только загрузить язык, который используется.
Ответ 4
Преимущество использования класса или функций для этого заключается в том, что вы можете изменить хранилище языков по мере роста проекта. Если у вас всего несколько строк, у вас нет проблем с вашим решением.
Если у вас много строк, для загрузки языковых массивов на все загрузки страниц может потребоваться время, память и ресурсы жесткого диска. Затем вы, вероятно, захотите разделить его на разные файлы или, возможно, даже использовать бэкэнд базы данных. Если вы используете i-базу данных, подумайте об использовании кеширования (например memcached), поэтому вам не нужно запрашивать базу данных сотни раз с каждым Загрузка страницы.
Вы также можете проверить gettext, который использует прекомпилированные языковые файлы, которые очень быстр.
Ответ 5
Я бы подумал, что было бы проще просто использовать include для каждого языка, содержимое которого может быть просто списком определений.
Таким образом, вы избегаете как накладных расходов, включая все данные языка, так и накладные расходы на регулярную вызов функции "переводить".
Опять же, этот подход ограничит ситуацию с точки зрения будущей гибкости. (Это может быть не тем фактором.)
Ответ 6
Когда у меня была проблема вроде этого (но для очень маленького сайта, всего несколько страниц) давным-давно, я создал файл с именем langpack.php
, и любая строка текста на моем сайте должна была запускаться через этот, Теперь я бы использовал аналогичный подход, но разбился на несколько файлов.
Пример подхода ООП
langpack.php
abstract class langpack {
public static $language = array();
public static function get($n) {
return isset(self::$language[$n]) ? self::$language[$n] : null;
}
}
english.php
final class English extends langpack {
public static $language = array(
'inbox' => 'Inbox',
'messages' => 'Messages',
'downloadError' => 'There was an error downloading your files',
);
}
french.php
final class French extends langpack {
public static $language = array(
'inbox' => 'Inbioux',
'messages' => 'Omelette du Fromage',
'downloadError' => 'C\'est la vie',
);
}
Вы должны получить эту идею оттуда. Внесите автозагрузчик в файл конфигурации, а затем загрузка этого языка должна быть чем-то, что вы могли бы легко сделать из сеанса, URL-адреса и т.д., Используя Характер переменной PHP в сочетании с созданием класса, что-то вроде этого:
$langpack = new $_SESSION['language'];
echo $langpack::get('inbox');
Конечно, все это можно было бы сделать с помощью простых массивов и получить доступ в императивном стиле (с абсолютными ссылками, обрабатываемыми через $GLOBALS
), чтобы уменьшить некоторые накладные расходы и, возможно, даже создать механизмы, с помощью которых все это обрабатывается немного больше прозрачный, но эй, это было бы не очень OO, не так ли?
Ответ 7
Использует константы (определяет) плохую практику?
Вот как мне это настроить. Это просто поддержка нескольких языков.
У меня есть один португальский файл и английские файлы, заполненные:
define('CONST','Meaning');
Возможно, это немного бодрость памяти, но я могу получить доступ ко всем, где хочу:)
Я могу изменить подход, но на данный момент у меня есть это.
Ответ 8
Можно также использовать компонент Symfony translation, никакая структура не требуется и composer помогает справляться с зависимостями:
composer install --prefer-dist "symfony/translation":"@stable"
Ответ 9
Я думаю, что хорошо, если вы не используете какие-либо рамки по другим причинам. Мы были в том же сценарии, что и ваш, когда вы не можете/не хотите использовать более структурированную структуру перевода:
Мы работали над небольшим проектом PHP и искали какой-то простой механизм перевода. Мы использовали подход массива, похожий на ваш, но с отдельными файлами для каждого языкового текста. Мы вкладываем небольшой компонент, чтобы сделать чистые как можно чище.
Если вы хотите посмотреть, мы разделили это на https://github.com/BrainCrumbz/simple-php-translate. Пожалуйста, не стесняйтесь улучшать его!
Ответ 10
i просто использовал бы функцию с входами контроллера и языка, например:
function getLanguageFile($controller, $lang){
// security: only allow letters a-z in both strings
$controller = preg_replace('/([^a-z]*)/', '', $controller);
$lang = preg_replace('/([^a-z]*)/', '', $lang);
// return language details if present on disk
if (is_file('lang/'.$controller.'/'.$lang.'.json')){
return json_decode(file_get_contents('lang/'.$controller.'/'.$lang.'.json'));
}
return false;
}
вам просто нужно поместить ваши форматированные строки json в lang/index/en.json, если контроллер является индексом, а язык - en.
вы можете добавить функцию для зависимостей (например, вы хотите загрузить значения индекса контроллера при доступе к другому контроллеру), все, что вам нужно сделать, это объединить результаты.
вы могли бы просто включить php файлы с массивами, а затем просто вернуть массив, но я предлагаю вам разделить эти переводы в более крупных проектах. если ваш проект не такой большой, ваша функция абсолютно нормально.