Программирование многоязычных PHP-приложений

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

Я владею PHP, но никогда не разрабатывал ничего с поддержкой других языков.

Я думал о том, чтобы поместить язык в PHP файл с константами, например:

ru.php может содержать:

define('HZ_DB_CONN_ERR', 'There was an error connecting to the database.');

и fr.php могут содержать:

define('HZ_DB_CONN_ERR', 'whatever the french is for the above...');

Затем я мог бы вызвать функцию и автоматически передать правильный язык.

hz_die('HZ_DB_CONN_ERR', $this);

Это хороший способ обойти это?

- morristhebear.

Ответ 1

Weird. Люди, похоже, игнорируют очевидное решение. Ваша идея локальных файлов отлично. Имейте en.php:

define('LOGIN_INCORRECT','Your login details are incorrect.');
...

Затем, если у вас есть глобальный файл config/констант (который я бы предложил по многим причинам), введите такой код:

if ($language == 'en') {
  reqire_once 'en.php';
} else if ($language == 'de') {
  require_once 'de.php';
}

Вы можете определить функции для отображения числа и валюты, сравнение/сортировка (т.е. у немецкого, французского и английского языков все разные методы сопоставления) и т.д.

Люди часто забывают, что PHP - это динамический язык, поэтому такие вещи:

if ($language == 'en') {
  function cmp($a, $b) { ... }
} else if ($language == 'de') {
  function cmp($a, $b) { ... }
}

на самом деле совершенно законны. Используйте их.

Ответ 2

Вы можете использовать gettext или что-то, что поддерживает gettext, а также больше, например Zend_Translate.

Edit:

Просто для точности, Zend_Translate поддерживает gettext без модуля gettext. Вы можете увидеть здесь множество различных типов ввода, которые он поддерживает.

Если вы используете массивы, а также предложите использовать это с Zend_Translate. Дело в том, что если вы используете сегодня массивы и gettext, xml или что-то еще, вам нужно изменить свой конфиг для Zend_Translate.

Ответ 3

Мне очень нравится следующий подход:

один файл - сам переводчик:

class Translator{
    private static $strs = array();
    private static $currlang = 'en';

    public static function loadTranslation($lang, $strs){
        if (empty(self::$strs[$lang]))
            self::$strs[$lang] = array();

        self::$strs[$lang] = array_merge(self::$strs[$lang], $strs);        
    }

    public static function setDefaultLang($lang){
        self::$currlang = $lang;        
    }

    public static function translate($key, $lang=""){
        if ($lang == "") $lang = self::$currlang;
        $str = self::$strs[$lang][$key];
        if (empty($str)){
            $str = "$lang.$key";            
        } 
        return $str;       
    }    

    public static function freeUnused(){
        foreach(self::$strs as $lang => $data){
            if ($lang != self::$currlang){
                $lstr = self::$strs[$lang]['langname'];
                self::$strs[$lang] = array();
                self::$strs[$lang]['langname'] = $lstr;                
            }            
        }        
    }

    public static function getLangList(){
        $list = array();
        foreach(self::$strs as $lang => $data){
            $h['name'] = $lang;
            $h['desc'] = self::$strs[$lang]['langname'];
            $h['current'] = $lang == self::$currlang;
            $list[] = $h;
        }
        return $list;        
    }

    public static function &getAllStrings($lang){
        return self::$strs[$lang];
    }

}

function generateTemplateStrings($arr){
    $trans = array();
    foreach($arr as $totrans){
        $trans[$totrans] = Translator::translate($totrans);
    }
    return $trans;    
}

языковые файлы могут быть просто include() d и выглядеть следующим образом:

В файле En.php:

Translator::loadTranslation('en', array(
  'textfield_1'          => 'This is some Textfield',
  'another_textfield '   => 'This is a longer Text showing how this is used',
));

de.php:

Translator::loadTranslation('de', array(
  'textfield_1'          => 'Dies ist ein Textfeld',
  'another_textfield '   => 'Dies ist ein längerer Text, welcher aufzeigt, wie das hier funktioniert.',
));

в вашем приложении вы можете либо перевести одну строку следующим образом:

$string = Translator::translate('textfield_1')

или даже набор строк:

$strings = generateTemplateStrings(array('textfield_1', 'another_textfield'));

Поскольку языковые файлы могут быть просто включены, вы можете легко складывать и, скажем, включать глобальный файл, а затем включать файлы из подмодулей, которые могут либо добавлять новые строки, либо заменять уже определенные (что не работайте с define() -методом).

Поскольку он является чистым PHP, он может даже легко кэшироваться с помощью операции opcode.

У меня даже есть скрипты, вокруг которых генерируются CSV файлы, содержащие нетранслируемые строки из файла и даже лучше: конвертируйте переведенный CSV файл обратно в языковой файл.

У меня есть это решение в продуктивном использовании с 2004 года, и я так доволен этим.

Конечно, вы можете даже расширить его с помощью условностей для плюрализации, например. Локализация форматов чисел - это то, что вам нужно было бы сделать, используя другие средства, - вспоминает intl-extension.

Ответ 4

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

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

О том, что это невозможно переопределить; ну, в общем случае, если вы определяете константы, это потому, что они не нужны или не должны быть изменены, поэтому при необходимости вы можете использовать раздел для определенных строк и переменных.

Я добавляю, что мне не нравится, как выглядят те, которые определяют файлы, но учитывайте, что ваши посетители не беспокоятся о том, как идиома реализована, - это идеальные правильные реализации, и вы можете легко получить некоторые Traductions.

Ответ 5

Возможно, вам захочется взглянуть на структуру, такую ​​как CakePHP или CodeIgniter, которые значительно упрощают запись интернационализированных приложений. Это не просто строки, которые вы должны учитывать - такие вещи, как числовые форматы и форматы дат, также должны учитываться

Ответ 6

Ваше решение должно работать нормально, если оно предназначено исключительно для перевода. Как отмечали другие, существует множество других переменных на основе языка, таких как форматы валюты и даты.

Твердый подход к обеспечению локализации вашего приложения будет заключаться в использовании Zend_Locale в сочетании с Zend_Translate.

Zend_Locale позволяет вам легко определить локаль пользователя или установить его, если хотите. Этот класс полезен, например, для автоматической настройки правильного формата валюты.

Zend_Translate позволяет легко переводить текст с использованием нескольких форматов :

  • Массив
  • CSV
  • Gettext
  • Ini
  • TBX
  • Ттх
  • Qt
  • XLIFF
  • XmlTm

Ответ 7

Ваш подход работоспособен в том случае, если вы включаете разные константные файлы в зависимости от того, какой язык выбран пользователем.

Вы также можете пропустить константную часть и просто определить большую хэш-таблицу с константой = > перевод, чтобы предотвратить сбои в пространстве имен.

file: ru.php

$constants = Array(
 'CONSTANT_KEY' => 'Translated string'
);

file: functions.php

function getConstant($key, $fallback) {
   // ... return constant or fallback here.
}

Однако, когда дело доходит до большого объема данных, этот подход будет трудно поддерживать. Существует несколько других подходов, которые могли бы лучше служить вашей цели, например, идеальным решением является то, где все ваши константы хранятся в глобальном пространстве памяти для всего вашего сайта, чтобы избежать того, чтобы каждый регест или поток сохраняли все эти переведенные данные в Память. Для этого требуется некоторый подход php-модуля. Gettext, как предложил кто-то, может использовать этот подход.

Google для локализации PHP, чтобы найти полезные ресурсы.

Ответ 8

Один совет для вас - создать функцию, которая возвращает переведенную строку, и если она не находится в хеш-таблице, тогда верните хеш-ключ, который вы запросили, с чем-то вроде * за ним, чтобы уведомить вас о необходимости перевода.

Ответ 9

Вот как я это делаю:

parent_script.php:

$lang_pick = "EN";    //use your own method to set language choice

require_once('trans_index.php'); 

echo $txt['hello'];

trans_index.php:

    $text = array();

    $text['hello'] = array (
     "EN"=> "Hello",
     "FR"=> "Bonjour",
     "DE"=> "Guten Tag",
     "IT"=> "Ciao"
    );                          //as many as needed



foreach($text as $key => $val) {

$txt[$key] = $text[$key][$lang_pick];

}

Это может быть слишком просто для ваших нужд, но я считаю это очень работоспособным. Это также упрощает поддержку нескольких версий текста.

Ответ 10

Вы можете использовать мой любимый язык PHP Multi Language Class - LangQuery

Это самый простой из всех, что я когда-либо видел.

Вы можете сохранить свои языковые данные в .ini файлы, чем просто называть его

include("LangQuery.php");

$L=new LangQuery();

// Write Hello World
echo($L('hello_world'));

// Write Hello World Easier - In-line Echo Feature
// You don't have to write echo. Just add '>'
$L('>hello_world');

// Use in Strings
echo("Hello Universe. {$L('hello_world')} Hello Europe");

// Write my age with parameter
$L(">my_age",25);

// Write my name and age with parameters
$L(">my_name_age","Recep",25);