douzhimao8656 2012-10-03 14:21
浏览 41
已采纳

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

I am working on multilingual application with a centralized language system. It's based on language files for each language and a simple helper function:

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

Up til now, all strings were system messages addressed to the current user, hence I always could do it that way. Now, I need create other messages, where the string should depend on a dynamic value. E.g. in a template file I want to echo the number of action points. If the user only has 1 point, it should echo "You have 1 point."; but for zero or more than 1 point it should be "You have 12 points."

For substitution purposes (both strings and numbers) I created a new function

function __s($line, $subs = array()) {
  $text = $lang[$line];
  while (count($subs) > 0) {
    $text = preg_replace('/%s/', array_shift($subs), $text, 1);
  }
  return $text;
}

Call to function looks like __s('current_points', array($points)).

$lang['current_points'] in this case would be "You have %s point(s).", which works well.

Taking it a step further, I want to get rid of the "(s)" part. So I created yet another function

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;
}

Call to function looks still like __s('current_points', array($points)).

$lang['current_points'] is now array("You have %d point.","You have %d points.").

How would I now combine these two functions. E.g. if I want to print the username along with the points (like in a ranking). The function call would be something like __x('current_points', array($username,$points)) with $lang['current_points'] being array("$s has %d point.","%s has %d points.").

I tried to employ preg_replace_callback() but I am having trouble passing the substitute values to that callback function.

$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);

Apparently, $subs is not defined as I am getting "out of memory" errors as if the function is not leaving the while loop.

Could anyone point me in the right direction? There's probably a complete different (and better) way to approach this problem. Also, I still want to expand it like this:

$lang['invite_party'] = "%u invited you to $g party."; should become Adam invited you to his party." for males and "Betty invited you to her party." for females. The passed $subs value for both $u and $g would be an user object.

  • 写回答

3条回答 默认 最新

  • dongshi1914 2012-10-03 15:09
    关注

    As mentionned by comments, I guess gettext() is an alternative

    However if you need an alternative approach, here is a workaround

    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
    ";
    echo $ll->__('access'),"
    
    ";
    echo "Call : points - 1
    ";
    echo $ll->__('points',1),"
    
    ";
    echo "Call : points - 175
    ";
    echo $ll->__('points',175),"
    
    ";
    echo "Call : party - Male
    ";
    echo $ll->__('party','Male'),"
    
    ";
    echo "Call : party - Female
    ";
    echo $ll->__('party','Female'),"
    
    ";
    echo "Call : toto - empty
    ";
    echo $ll->__('toto','empty'),"
    
    ";
    echo "Call : toto - full
    ";
    echo $ll->__('toto','full');
    

    This outputs

    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 !
    

    This may give you an idea on how you could centralize your language possibilities, creating your own functions to resolve one or another text.

    Hope this helps you.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 springboot 3.0 实现Security 6.x版本集成
  • ¥15 PHP-8.1 镜像无法用dockerfile里的CMD命令启动 只能进入容器启动,如何解决?(操作系统-ubuntu)
  • ¥15 请帮我解决一下下面六个代码
  • ¥15 关于资源监视工具的e-care有知道的嘛
  • ¥35 MIMO天线稀疏阵列排布问题
  • ¥60 用visual studio编写程序,利用间接平差求解水准网
  • ¥15 Llama如何调用shell或者Python
  • ¥20 谁能帮我挨个解读这个php语言编的代码什么意思?
  • ¥15 win10权限管理,限制普通用户使用删除功能
  • ¥15 minnio内存占用过大,内存没被回收(Windows环境)