Подтвердить что ты не робот

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];
    }
}
4b9b3361

Ответ 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 файлы с массивами, а затем просто вернуть массив, но я предлагаю вам разделить эти переводы в более крупных проектах. если ваш проект не такой большой, ваша функция абсолютно нормально.