本地应用程序
In Part 2 you gained more insight into using the gettext library by learning the most important functions of the extension. In this part you’ll learn how to best use a fallback locale, switch between locales, and override the currently selected message domain. So without further ado, let’s dive in!
在第2部分中,您通过学习扩展最重要的功能,对使用gettext库有了更深入的了解。 在这一部分中,您将学习如何最好地使用后备区域设置,在区域设置之间切换以及覆盖当前选择的消息域。 因此,事不宜迟,让我们开始吧!
目录结构和后备区域设置 (Directory Structure and Fallback Locales)
If you’ve been following along in the previous articles, you should have the following directory structure for testing the gettext library. While it is typical, it is not designed to achieve the best performance.
如果您一直遵循前面的文章,则应该具有以下目录结构来测试gettext库。 尽管这是典型的,但并不是为了获得最佳性能而设计的。

Generally speaking, you should define your application’s default language/locale before getting too far into the localization process because this decision will affect how you create your core domain files. For example, if you decide to use American English as the default locale – as most people do – then you probably won’t create the en_US
directory at all! It is preferable to create each target domain with msgids that are actual strings that would have been found in the default locale. So instead of using the identifier “HELLO_WORLD”, you should be using the actual English string.
一般来说,您应该在进入本地化过程之前定义应用程序的默认语言/区域设置,因为此决定将影响您创建核心域文件的方式。 例如,如果您决定像大多数人一样使用美式英语作为默认语言环境,那么您可能根本不会创建en_US
目录! 最好使用msgids创建每个目标域,这些msgids是可以在默认语言环境中找到的实际字符串。 因此,您应该使用实际的英语字符串,而不是使用标识符“ HELLO_WORLD”。
#Test token 1
msgid "Hello World!"
msgstr "Bonjour tout le monde!"
Remember, gettext will display the msgid if it can’t find a translation domain. So it’s not about the en_US
directory itself, it’s about avoiding unnecessary translations when the user requests the default locale. Using real text strings like this saves execution time and memory by eliminating the need to translate English to English, “HELLO_WORLD” to “Hello World!”.
请记住,如果无法找到翻译域,则gettext将显示msgid。 因此,这与en_US
目录本身无关,而是在用户请求默认语言环境时避免不必要的翻译。 像这样使用真实的文本字符串,无需将英语翻译为英语,而无需将“ HELLO_WORLD”翻译为“ Hello World!”,从而节省了执行时间和内存。
While some developers prefer to keep the English-to-English translation so there is a clear distinction between application strings (“HELLO_WORLD”) and interface text (“Hello World!”), I much prefer this fallback approach. Of course you are free to choose whichever best aligns with your needs and personal style.
尽管有些开发人员喜欢保留英语到英语的翻译,因此在应用程序字符串(“ HELLO_WORLD”)和界面文本(“ Hello World!”)之间有明显的区别,但我还是更喜欢这种后备方法。 当然,您可以自由选择最适合您的需求和个人风格的东西。
After deleting the en_US
directory, I’ll add two more locales the application can target: Spanish (es_ES
) and Egyptian Arabic (ar_EG
). This is how the Locale
directory should look now:
删除en_US
目录后,我将添加应用程序可以定位的另外两个语言环境:西班牙语( es_ES
)和埃及阿拉伯语( ar_EG
)。 这是Locale
目录现在的外观:

Don’t forget to create the necessary translation domains for each using the same procedures outlined in Part 1.
不要忘记使用第1部分中概述的相同步骤为每个域创建必要的翻译域。
The French domain contains:
法语域包含:
#Test token 1
msgid "Hello World!"
msgstr "Bonjour tout le monde!"
#Test token 2
msgid "Testing Translation..."
msgstr "Test de traduction..."
The Spanish domain contains:
西班牙语域包含:
#Test token 1
msgid "Hello World!"
msgstr "¡Hola mundo!"
#Test token 2
msgid "Testing Translation..."
msgstr "Prueba de traducción..."
And the Arabic domian contains:
阿拉伯语的domian包含:
#Test token 1
msgid "Hello World!"
msgstr "!أهلا بالعالم"
#Test token 2
msgid "Testing Translation..."
msgstr "...اختبار الترجمة"
See how the msgids in all the target domains are actually strings from the en_US
locale that was removed since you are now using it as the default. Now you have a real-world directory structure with translation domains!
查看所有目标域中的msgids实际上是如何从en_US
语言环境中删除的字符串,因为您现在将其用作默认语言。 现在,您将获得带有翻译域的真实目录结构!
切换地区 (Switching Locales)
Switching between various locales is as easy as telling gettext to use another domain. Typically you will do this at the top of your application files or by putting it in a common file to be included in each script that sends output to the browser.
在各种语言环境之间切换就像告诉gettext使用另一个域一样容易。 通常,您可以在应用程序文件的顶部执行此操作,也可以将其放在一个公共文件中,以包含在将输出发送到浏览器的每个脚本中。
Create a new file named locale.php
with the following contents:
使用以下内容创建一个名为locale.php
的新文件:
<?php
// use sessions
session_start();
// get language preference
if (isset($_GET["lang"])) {
$language = $_GET["lang"];
}
else if (isset($_SESSION["lang"])) {
$language = $_SESSION["lang"];
}
else {
$language = "en_US";
}
// save language preference for future page requests
$_SESSION["Language"] = $language;
$folder = "Locale";
$domain = "messages";
$encoding = "UTF-8";
putenv("LANG=" . $language);
setlocale(LC_ALL, $language);
bindtextdomain($domain, $folder);
bind_textdomain_codeset($domain, $encoding);
textdomain($domain);
Then open the test-locale.php
script; remove the domain setup code and include the new locale.php
file instead. Your code should now look like this:
然后打开test-locale.php
脚本; 删除域设置代码,并包括新的locale.php
文件。 您的代码现在应如下所示:
<?php
// Include I18N support
require_once "locale.php";
echo _("Hello World!"), "<br>";
echo _("Testing Translation...");
Now you’re ready for the fun! Go to your browser and you’ll get the following output:
现在您已经准备好享受乐趣了! 转到浏览器,您将获得以下输出:
Hello World!
Testing Translation...
Change the URL to pass in one of the locales created earlier, for example: test-locale.php?lang=fr_FR
. gettext will display the output in French or Arabic, depending on what you provided for the parameter. Switching languages is as simple as that!
更改URL以传递先前创建的语言环境之一,例如: test-locale.php?lang=fr_FR
。 gettext将以法语或阿拉伯语显示输出,具体取决于您为参数提供的内容。 切换语言就这么简单!
Bonjour tout le monde!
Test de traduction...
The locale.php
file makes use of sessions so you only have to pass the lang
parameter once and it will be used for subsequent requests by the user. If you are performing URL-rewriting, another possibility is to make the language part of the URL and extract it from there, as in www.example.com/en_US/test-locale.php
.
locale.php
文件使用会话,因此您只需传递一次lang
参数,该参数将用于用户的后续请求。 如果您正在执行URL重写,则另一种可能性是使语言成为URL的一部分并从URL中提取它,如www.example.com/en_US/test-locale.php
。
覆盖当前域 (Overriding the Current Domain)
Let’s suppose for a moment you want to switch from one text domain to another. This might be needed if you’re using separate domains to store the core translation and system error messages, for example messages would be main application domain and errors would be the domain for error strings. You’ll find the function dgettext()
very useful for this type of setup.
让我们假设您想从一个文本域切换到另一个文本域。 如果您使用单独的域来存储核心转换和系统错误消息,则可能需要这样做,例如,消息将是主应用程序域,而错误将是错误字符串的域。 您会发现函数dgettext()
对于这种类型的设置非常有用。
dgettext()
is essentially the same as gettext()
and _()
, except it accepts the name of a domain as the first argument in which to look up the translation. This doesn’t affect the default domain that was set by textdomain()
; subsequent calls to gettext()
will still use the default.
dgettext()
本质上与gettext()
和_()
,只是它接受域的名称作为在其中查找翻译的第一个参数。 这不会影响由textdomain()
设置的默认域; 随后对gettext()
调用仍将使用默认值。
Create a new translation domain by the name of errors for the French locale (TestI18N/Locale/fr_FR/LC_MESSAGES/errors.po
). Make sure you create the file using Poedit, then open it using a text editor and add the following translations:
通过法国语言环境的错误名称( TestI18N/Locale/fr_FR/LC_MESSAGES/errors.po
)创建一个新的翻译域。 确保使用Poedit创建文件,然后使用文本编辑器将其打开并添加以下翻译:
Test Error 1
msgid "Error getting content"
msgstr "Erreur de l'obtention du contenu"
#Test Error 2
msgid "Error saving data"
msgstr "Erreur de sauvegarde des données"
Save errors.po
, re-open it in Poedit and compile it to errors.mo
. Then add the following lines at the end of test-locale.php
:
保存errors.po
,在Poedit中重新打开并将其编译为errors.mo
。 然后在test-locale.php
的末尾添加以下行:
<?php
echo "<br>";
echo _("Error getting content"), "<br>";
echo _("Error saving data");
When you run the test-locale.php
script you’ll see the English strings, even if you pass lang=fr_FR
as the parameter. This is because you added these messages to a different domain (_()
is using the messages domain to look up translations since this is what was set with textdomain()
). To inform gettext where to find the new translation strings, update the code to use dgettext()
instead:
运行test-locale.php
脚本时,即使您将lang=fr_FR
作为参数传递,您也会看到英语字符串。 这是因为您已将这些消息添加到其他域( _()
使用message域查找翻译,因为这是使用textdomain()
设置的)。 要通知gettext在哪里可以找到新的翻译字符串,请更新代码以使用dgettext()
代替:
<?php
echo dgettext("errors", "Error getting content"), "<br>";
echo dgettext("errors", "Error saving data");
Now when you run the script… you still see the English messages! Hrm… why aren’t they being replaced?
现在,当您运行脚本时,您仍然会看到英语消息! 嗯...为什么不更换它们?
Actually we’ve just made a very common mistake that people make when using gettext. If you recall in Part 1 I talked about two very important methods, bindtextdomain()
and bind_textdomain_codeset()
and mentioned that you can call them several times to bind as many domains as you want. Whenever you need to use a domain you must to explicitly bind it using bindtextdomain()
first. gettext only allows loading a single “master” domain, which is the one when specify using textdomain()
and is the one used by gettext()
and _()
. You can lookup messages in other domains with dgettext()
only if they’re bound first.
实际上,我们刚刚犯了一个很常见的错误,人们在使用gettext时会犯错误。 如果您在第1部分中回想过,我谈到了两个非常重要的方法, bindtextdomain()
和bind_textdomain_codeset()
并提到您可以多次调用它们以绑定所需的任意多个域。 每当需要使用域时,都必须先使用bindtextdomain()
显式绑定它。 gettext只允许加载一个“主”域,该域是使用textdomain()
指定时的域,也是gettext()
和_()
。 仅当绑定第一条消息时,才可以使用dgettext()
在其他域中查找消息。
Update the locale.php
include file to bind the errors domain as well:
更新locale.php
包含文件以绑定错误域:
<?php
...
bindtextdomain($domain, $folder);
bind_textdomain_codeset($domain, $encoding);
bindtextdomain("errors", "Locale");
bind_textdomain_codeset("errors", "UTF-8");
textdomain($domain);
Now when you run your script you’ll see the error messages correctly translated to French.
现在,当您运行脚本时,您将看到错误消息已正确翻译为法语。
摘要 (Summary)
In this part you learned how using the default locale’s strings as msgids in target domains can improve performance and organization, and how switching between locales based on the user’s preference can be accomplished. You also learned that while gettext allows only one default lookup domain, you can use multiple domains with dgettext()
provided you’ve bound them first.
在这一部分中,您学习了如何在目标域中将默认语言环境的字符串用作msgids来改善性能和组织,以及如何实现基于用户偏好的语言环境之间的切换。 您还了解到,尽管gettext仅允许一个默认的查找域,但是如果您首先绑定了它们,则可以将其与dgettext()
一起使用。
In the next part I’ll show you how powerful gettext is when it comes to handling one of language’s most demanding aspects – plural forms.
在下一部分中,我将向您展示gettext在处理语言最苛刻的方面之一-复数形式方面的功能。
Image via sgame / Shutterstock
图片来自sgame / Shutterstock
翻译自: https://www.sitepoint.com/localizing-php-applications-3/
本地应用程序