考虑编写一个PHP库,它将通过Packagist或Pear发布。它适用于在任意设置中使用它的同行开发人员。
此库将包含为客户端确定的一些状态消息。如何使此代码国际化,以便使用该库的开发人员可以尽可能自由地插入自己的本地化方法?我不想假设任何事情,特别是不要强迫dev使用gettext。
要处理一个例子,让我们来看看这个类:
class Example {
protected $message = "I'd like to be translated in your client's language.";
public function callMe() {
return $this->message;
}
public function callMeToo($user) {
return sprintf('Hi %s, nice to meet you!', $user);
}
}
这里有两个问题:如何标记私有$message
进行翻译,以及如何让开发人员在callMeToo()
内本地化字符串?
一个(非常不方便)选项是,在构造函数中要求一些i18n方法,如下所示:
public function __construct($i18n) {
$this->i18n = $i18n;
$this->message = $this->i18n($this->message);
}
public function callMeToo($user) {
return sprintf($this->i18n('Hi %s, nice to meet you!'), $user);
}
但我非常希望有一个更优雅的解决方案。
编辑1:除了简单的字符串替换外,i18n的字段很宽。前提是,我不想在我的库中打包任何i18n解决方案,或强迫用户专门选择一个以满足我的代码。
然后,我如何构建我的代码,以便为不同方面提供最佳和最灵活的本地化:字符串翻译,数字和货币格式,日期和时间,......?假设一个或另一个显示为我的库的输出。消费者可以在哪个位置或界面插入她的本地化解决方案?
答案 0 :(得分:7)
最常用的解决方案是字符串文件。例如。如下:
# library
class Foo {
public function __construct($lang = 'en') {
$this->strings = require('path/to/langfile.' . $lang . '.php');
$this->message = $this->strings['callMeToo'];
}
public function callMeToo($user) {
return sprintf($this->strings['callMeToo'], $user);
}
}
# strings file
return Array(
'callMeToo' => 'Hi %s, nice to meet you!'
);
为避免$this->message
作业,你可以使用魔法吸气剂:
# library again
class Foo {
# … code from above
function __get($name) {
if(!empty($this->strings[$name])) {
return $this->strings[$name];
}
return null;
}
}
您甚至可以添加loadStrings
方法,该方法从用户获取字符串数组并将其与内部字符串表合并。
编辑1:为了获得更大的灵活性,我会稍微改变上述方法。我会添加一个翻译函数作为对象属性,并在我想要本地化字符串时始终调用它。默认函数只查找字符串表中的字符串,如果找不到本地化字符串,则返回值本身,就像gettext一样。然后,使用您的库的开发人员可以将功能更改为他自己提供的功能,以执行完全不同的本地化方法。
日期本地化不是问题。设置区域设置取决于您的库使用的软件。格式本身是一个本地化的字符串,例如$this->translate('%Y-%m-%d')
将返回日期格式字符串的本地化版本。
通过设置正确的区域设置并使用sprintf()
等函数来完成数字本地化。
但货币本地化是一个问题。我认为最好的方法是添加货币转换功能(并且,也许是为了更好的灵活性,另一个数字格式化功能),如果开发人员想要更改货币格式,可以覆盖该功能。或者,您也可以为货币实现格式字符串。例如%CUR %.02f
- 在此示例中,您将使用货币符号替换%CUR
。货币符号本身也是本地化的字符串。
编辑2:如果你不想使用setlocale
,你必须做很多工作......基本上你必须重写strftime()
和sprintf()
来实现本地化的日期和数字。当然可能,但很多工作。
答案 1 :(得分:2)
基本方法是为消费者提供一些定义映射的方法。它可以采用任何形式,只要用户可以定义双射映射。
例如,Mantis Bug Tracker使用简单的globals file:
<?php
require_once "strings_$language.txt";
echo $s_actiongroup_menu_move;
他们的方法很基本,但效果很好。如果您愿意,请将其包装在课堂中:
<?php
$translator = new Translator(Translator::ENGLISH); // or make it a singleton
echo $translator->translate('actiongroup_menu_move');
使用XML文件,或者使用INI文件或CSV文件......实际上,无论您喜欢什么样的格式。
回答您以后的修改/评论
是的,上述与其他解决方案没有太大差别。但我相信没有什么可说的了:
答案 2 :(得分:2)
这里有一个主要问题。你不想在你的国际化问题中制作代码。
让我解释一下。主译者可能是程序员。第二个和第三个可能是,但是你想将它翻译成任何语言,即使是非程序员。这对非程序员来说应该很容易。通过课程,功能等为非程序员进行狩猎绝对不行。
所以我建议这样做:用不可知格式保留你的源句(英语),这对每个人来说都很容易理解。这可能是一个xml文件,一个数据库或您认为适合的任何其他形式。然后在您需要的地方使用您的翻译。你可以这样做:
class Example {
// Fetch them as you prefer and store them in $messages.
protected $messages = array(
'en' => array(
"message" => "I'd like to be translated in your client's language.",
"greeting" => "Hi %s, nice to meet you!"
)
);
public function __construct($lang = 'en') {
$this->lang = $lang;
}
protected function get($key, $args = null) {
// Store the string
$message = $this->messages[$this->lang][$key];
if ($args == null)
return $this->translator($message);
else {
$string = $this->translator($message);
// Merge the two arrays so they can be passed as values
$sprintf_args = array_merge(array($string), $args);
return call_user_func_array('sprintf', $sprintf_args);
}
}
public function callMe() {
return $this->get("message");
}
public function callMeToo($user) {
return $this->get("greeting", $user);
}
}
此外,如果您想使用small translation script I did,则可以进一步简化它。它使用数据库,因此它可能没有您想要的那么多灵活性。您需要注入它,并在初始化中设置语言。请注意,如果不存在,文本将自动添加到数据库中。
class Example {
protected $translator;
// Translator already knows the language to translate the text to
public function __construct($Translator) {
$this->translator = $Translator;
}
public function callMe() {
return $this->translator("I'd like to be translated in your client's language.");
}
public function callMeToo($user) {
return sprintf($this->translator("Hi %s, nice to meet you!"), $user));
}
}
可以很容易地修改它以使用xml文件或任何其他来源来翻译字符串。
注意第二种方法:
这与您提出的解决方案不同,因为它在输出中进行工作,而不是在初始化中,因此无需跟踪每个字符串。
你只需要用英语写一次你的句子。我编写的类将它放在数据库中,只要它被正确初始化,使你的代码非常干。这正是我启动它的原因,而不仅仅是使用gettext(以及我的简单要求的gettext的荒谬大小)。
Con:这是一个老班。那时我不知道很多。现在我改变了一些事情:制作一个language
字段,而不是en
,es
等,在这里和那里抛出一些例外并上传我做过的一些测试。