如何在PHP中根据性别和计数翻译字符串?

如何在PHP中根据性别和计数翻译字符串?,php,preg-replace,multilingual,Php,Preg Replace,Multilingual,我正在使用一个集中的语言系统开发多语言应用程序。它基于每种语言的语言文件和一个简单的辅助函数: en.php $lang['access_denied'] = "Access denied."; $lang['action-required'] = "You need to choose an action."; ... return $lang; language_helper.php ... function __($line) { return $lang[$line]; } 到目

我正在使用一个集中的语言系统开发多语言应用程序。它基于每种语言的语言文件和一个简单的辅助函数:

en.php

$lang['access_denied'] = "Access denied.";
$lang['action-required'] = "You need to choose an action.";
...
return $lang;
language_helper.php

...
function __($line) {
  return $lang[$line];
}
到目前为止,所有字符串都是发送给当前用户的系统消息,因此我总是可以这样做。现在,我需要创建其他消息,其中字符串应该依赖于动态值。例如,在模板文件中,我要回显动作点的数量。如果用户只有1分,则应回显“您有1分。”;但如果分数为零或超过1分,则应为“你得了12分。”

出于替换目的(字符串和数字),我创建了一个新函数

function __s($line, $subs = array()) {
  $text = $lang[$line];
  while (count($subs) > 0) {
    $text = preg_replace('/%s/', array_shift($subs), $text, 1);
  }
  return $text;
}
对函数的调用类似于
\u s('current_points',array($points))

$lang['current_points']
在这种情况下将是
“您有%s个点。”
,这很有效

更进一步,我想去掉“(s)”部分。所以我创建了另一个函数

function __c($line, $subs = array()) {
  $text = $lang[$line];
  $text = (isset($sub[0] && $sub[0] == 1) ? $text[0] : $text[1];
  while (count($subs) > 0) {
    $text = preg_replace('/%d/', array_shift($subs), $text, 1);
  }
  return $text;
}
对函数的调用看起来仍然像
\uu s('current_points',array($points))

$lang['current_points']
现在是
数组(“您有%d个点。”,“您有%d个点。”)

现在我将如何组合这两个功能。例如,如果我想打印用户名和分数(如排名)。函数调用类似于
\uux('current_upoints',array($username,$points))
,其中
$lang['current_upoints']
数组($s有%d个点,“%s有%d个点)。

我尝试使用
preg\u replace\u callback()
,但是我在将替换值传递给该回调函数时遇到了问题

$text = preg_replace_callback('/%([sd])/', 
  create_function(
    '$type',
    'switch($type) {
      case "s": return array_shift($subs); break;
      case "d": return array_shift($subs); break;
    }'),
  $text);
显然,$subs并没有被定义为“内存不足”错误,就好像函数没有离开while循环一样

谁能给我指一下正确的方向吗?可能有一种完全不同(更好)的方法来解决这个问题。另外,我还想这样扩展它:


$lang['invite_party']=%u邀请您参加$g派对。“
应该变成
亚当邀请你参加他的派对。“
对于男性和
”贝蒂邀请你参加她的派对。“
适用于女性。为
$u
$g
传递的
$subs
值都将是一个用户对象。

如果之前做过类似的事情,但通过分离关注点避免了所有陷阱

在较低的级别上,我在模板中注入了一个格式化程序,负责处理特定于语言的所有内容。设置数字或日期等格式。它有一个带有三个参数的“复数”函数:$value、$singular、$plural,并根据返回的值选择后两个参数中的一个。它没有回显值本身,因为这是留给数字格式化的

整个翻译是在模板引擎中完成的。它是Dwoo,可以进行模板继承,所以我设置了一个主模板,其中包含所有HTML结构和大量占位符。每种语言都继承了此HTML母版,并用正确的语言输出替换了所有占位符。但由于我们仍处于模板引擎领域,所以有可能“翻译”格式化程序函数的用法。Dwoo将在第一次调用时编译模板继承,包括对格式化程序的所有后续调用,包括所有转换的参数


性别问题将得到基本相同的解决方案:性别($sex,$male,$female),其中$sex是主题的性别,其他参数是男性或女性的措辞。

如果之前做过类似的事情,但通过分离关注点避免了所有陷阱

在较低的级别上,我在模板中注入了一个格式化程序,负责处理特定于语言的所有内容。设置数字或日期等格式。它有一个带有三个参数的“复数”函数:$value、$singular、$plural,并根据返回的值选择后两个参数中的一个。它没有回显值本身,因为这是留给数字格式化的

整个翻译是在模板引擎中完成的。它是Dwoo,可以进行模板继承,所以我设置了一个主模板,其中包含所有HTML结构和大量占位符。每种语言都继承了此HTML母版,并用正确的语言输出替换了所有占位符。但由于我们仍处于模板引擎领域,所以有可能“翻译”格式化程序函数的用法。Dwoo将在第一次调用时编译模板继承,包括对格式化程序的所有后续调用,包括所有转换的参数


性别问题将得到基本相同的解决方案:性别($sex,$male,$femal),其中$sex是主题的性别,其他参数是男性或女性措辞。

也许更好的方法是Drupal中函数t使用的方法,请看:


也许更好的方法是Drupal中的函数t使用的方法,请看:


正如评论中提到的,我想这是另一种选择

但是,如果您需要另一种方法,这里有一个变通方法

class ll
{
    private $lang = array(),
            $langFuncs = array(),
            $langFlags = array();

    function __construct()
    {
        $this->lang['access'] = 'Access denied';
        $this->lang['points'] = 'You have %s point{{s|}}';
        $this->lang['party'] = 'A %s invited you to {{his|her}} parteh !';
        $this->lang['toto'] = 'This glass seems %s, {{no one drank in already|someone came here !}}';

        $this->langFuncs['count'] = function($in) { return ($in>1)?true:false; };
        $this->langFuncs['gender'] = function($in) { return (strtolower($in)=='male')?true:false; };
        $this->langFuncs['emptfull'] = function($in) { return ($in=='empty')?true:false; };

        $this->langFlags['points'] = 'count';
        $this->langFlags['toto'] = 'emptfull';
        $this->langFlags['party'] = 'gender';
    }

    public function __($type,$param=null)
    {
        if (isset($this->langFlags[$type])) {
            $f = $this->lang[$type];
            preg_match("/{{(.*?)}}/",$f,$m);

            list ($ifTrue,$ifFalse) = explode("|",$m[1]);

            if($this->langFuncs[$this->langFlags[$type]]($param)) {
                return $this->__s(preg_replace("/{{(.*?)}}/",$ifTrue,$this->lang[$type]),$param);
            } else {
                return $this->__s(preg_replace("/{{(.*?)}}/",$ifFalse,$this->lang[$type]),$param);
            }
        } else {
            return $this->__s($this->lang[$type],$param);
        }
    }
    private function __s($s,$i=null)
    {
        return str_replace("%s",$i,$s);
    }
}

$ll = new ll();

echo "Call : access - NULL\n";
echo $ll->__('access'),"\n\n";
echo "Call : points - 1\n";
echo $ll->__('points',1),"\n\n";
echo "Call : points - 175\n";
echo $ll->__('points',175),"\n\n";
echo "Call : party - Male\n";
echo $ll->__('party','Male'),"\n\n";
echo "Call : party - Female\n";
echo $ll->__('party','Female'),"\n\n";
echo "Call : toto - empty\n";
echo $ll->__('toto','empty'),"\n\n";
echo "Call : toto - full\n";
echo $ll->__('toto','full');
这个输出

Call : access - NULL
Access denied

Call : points - 1
You have 1 point

Call : points - 175
You have 175 points

Call : party - Male
A Male invited you to his parteh !

Call : party - Female
A Female invited you to her parteh !

Call : toto - empty
This glass seems empty, no one drank in already

Call : toto - full
This glass seems full, someone came here !
这可能会让您了解如何集中您的语言可能性,创建自己的函数来解析一个或另一个文本


希望这对您有所帮助。

正如评论中提到的,我想这是另一种选择

但是,如果您需要另一种方法,这里有一个变通方法

class ll
{
    private $lang = array(),
            $langFuncs = array(),
            $langFlags = array();

    function __construct()
    {
        $this->lang['access'] = 'Access denied';
        $this->lang['points'] = 'You have %s point{{s|}}';
        $this->lang['party'] = 'A %s invited you to {{his|her}} parteh !';
        $this->lang['toto'] = 'This glass seems %s, {{no one drank in already|someone came here !}}';

        $this->langFuncs['count'] = function($in) { return ($in>1)?true:false; };
        $this->langFuncs['gender'] = function($in) { return (strtolower($in)=='male')?true:false; };
        $this->langFuncs['emptfull'] = function($in) { return ($in=='empty')?true:false; };

        $this->langFlags['points'] = 'count';
        $this->langFlags['toto'] = 'emptfull';
        $this->langFlags['party'] = 'gender';
    }

    public function __($type,$param=null)
    {
        if (isset($this->langFlags[$type])) {
            $f = $this->lang[$type];
            preg_match("/{{(.*?)}}/",$f,$m);

            list ($ifTrue,$ifFalse) = explode("|",$m[1]);

            if($this->langFuncs[$this->langFlags[$type]]($param)) {
                return $this->__s(preg_replace("/{{(.*?)}}/",$ifTrue,$this->lang[$type]),$param);
            } else {
                return $this->__s(preg_replace("/{{(.*?)}}/",$ifFalse,$this->lang[$type]),$param);
            }
        } else {
            return $this->__s($this->lang[$type],$param);
        }
    }
    private function __s($s,$i=null)
    {
        return str_replace("%s",$i,$s);
    }
}

$ll = new ll();

echo "Call : access - NULL\n";
echo $ll->__('access'),"\n\n";
echo "Call : points - 1\n";
echo $ll->__('points',1),"\n\n";
echo "Call : points - 175\n";
echo $ll->__('points',175),"\n\n";
echo "Call : party - Male\n";
echo $ll->__('party','Male'),"\n\n";
echo "Call : party - Female\n";
echo $ll->__('party','Female'),"\n\n";
echo "Call : toto - empty\n";
echo $ll->__('toto','empty'),"\n\n";
echo "Call : toto - full\n";
echo $ll->__('toto','full');
这个输出

Call : access - NULL
Access denied

Call : points - 1
You have 1 point

Call : points - 175
You have 175 points

Call : party - Male
A Male invited you to his parteh !

Call : party - Female
A Female invited you to her parteh !

Call : toto - empty
This glass seems empty, no one drank in already

Call : toto - full
This glass seems full, someone came here !
这可能会让您了解如何集中您的语言可能性,创建自己的函数来解析一个或另一个文本


希望这对您有所帮助。

您正在重新发明车轮。你看过
gettext
扩展了吗?你正在重新发明轮子。你看过
gettext
扩展了吗?哇,谢谢你,我来看看。我真的