国际化简单 PHP 网站的最佳方式

发布于 2024-11-28 13:36:36 字数 717 浏览 1 评论 0原文

我必须开发一个非常简单的 php 网站,所以我不需要框架。 但它必须支持多语言(EN/FR/CHINESE)。 我寻找了 php 内置系统,发现了两种方法:

我没有没有框架的 i18n 经验,所以关于什么是最简单的方法的任何建议强>支持多语言?

最后,我只需要一个将翻译搜索到文件中的功能(按语言一个文件)。 情商: trans('你好');

=> en.yaml(无论是否为 yaml,这只是一个示例)

hello: "Hello world!"

=> fr.yaml

hello: "Bonjour tout le monde !"

如果可能的话我更喜欢纯 PHP 实现

I have to develop a pretty simple php website so I don't need framework.
But it's must support multi language (EN/FR/CHINESE).
I have looked for php built in system and I found two ways :

I have no experience in i18n without framework, so any advices about what's the simplest way to support multi language ?

At end I just need a function that search translation into file (one file by language).
EQ :
trans('hello');

=> en.yaml (yaml or not, it's an example)

hello: "Hello world!"

=> fr.yaml

hello: "Bonjour tout le monde !"

And if possible I prefer Pure PHP implementations

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

遇到 2024-12-05 13:36:36

虽然 ext/gettextext/intl 都与 i18(国际化)相关,但 gettext 处理翻译,而 intl > 处理国际化事务,例如数字和日期显示、排序顺序和音译。因此,您实际上需要两者才能获得完整的 i18 解决方案。根据您的需求,您可能会想出一个依赖于上述扩展或使用某些框架提供的组件的自制解决方案:

如果您只需要翻译并且站点足够简单,也许您的简单解决方案(将翻译配置文件读入 PHP 数组,使用简单的函数检索令牌)可能是最简单的。

我能想到的最简单的解决方案是:

$translation = array(
    'Hello world!' => array(
        'fr' => 'Bonjour tout le monde!',
        'de' => 'Hallo Welt!'
    )
);

if (!function_exists('gettext')) {
    function _($token, $lang = null) {
        global $translation;
        if (   empty($lang)
            || !array_key_exists($token, $translation)
            || !array_key_exists($lang, $translation[$token])
        ) {
            return $token;
        } else {
            return $translation[$token][$lang];
        }
    }
}

echo _('Hello World!');

Although ext/gettext and ext/intl are both related to i18 (internationalization), gettext deals with translation while intl deals with internationalizing things like number and date display, sorting orders and transliteration. So you'd actually need both for a complete i18-solution. Depending on your needs you may come up with an home-brew solution relying on the extensions mentioned above or your use components provided by some framework:

If you only need translation and the site is simple enough, perhaps your simple solution (reading a translation configuration file into an PHP array, using a simple function to retrieve a token) might be the easiest.

The most simple solution I can think of is:

$translation = array(
    'Hello world!' => array(
        'fr' => 'Bonjour tout le monde!',
        'de' => 'Hallo Welt!'
    )
);

if (!function_exists('gettext')) {
    function _($token, $lang = null) {
        global $translation;
        if (   empty($lang)
            || !array_key_exists($token, $translation)
            || !array_key_exists($lang, $translation[$token])
        ) {
            return $token;
        } else {
            return $translation[$token][$lang];
        }
    }
}

echo _('Hello World!');
难忘№最初的完美 2024-12-05 13:36:36

我知道这是一个老问题,但我觉得答案从头到尾都缺乏更实际的方法。这就是我使用 PHP 的 gettextPoedit,无需在 Debian 服务器上使用任何其他 PHP 库:

准备步骤 1:安装 gettext 和语言环境在服务器上

我不确定对于其他操作系统,这是如何完成的,但对于 Debian,您可以这样做:

sudo apt-get install gettext
sudo dpkg-reconfigure locales

编辑:我假设 Ubuntu 与 Debian 相同,但显然它略有不同。有关在 Ubuntu 上安装区域设置的说明,请参阅此页面

确保选择所有您想要使用的区域设置。然后您应该看到类似以下内容:

Generating locales (this might take a while)...
  en_US.UTF-8... done
  es_MX.UTF-8... done
  fr_FR.UTF-8... done
  zh_CN.UTF-8... done
Generation complete.

注意:确保为每种语言选择正确的变体字符编码(最有可能是 UTF-8) 。如果您安装 es_MX.UTF-8 并尝试使用 es_ES.UTF-8es_MX.ISO-8859-1 则不会工作。
准备步骤 2:在翻译人员的计算机上安装 Poedit

可从适用于大多数 Linux 操作系统的软件存储库中获取 Poedit。对于基于 Debian 的系统,只需执行:

sudo apt-get install poedit

对于 Windows 和 Mac,请访问:https://poedit.net/download


开始编码:

好的,现在您已经准备好开始编码了。我编写了以下 gettext() 包装函数来翻译单数和复数:

function __($text, $plural=null, $number=null) {
    if (!isset($plural)) {
        return _($text);
    }
    return ngettext($text, $plural, $number);
}

示例用法:

// Singular
echo __('Hello world');

// Plural
$exp = 3;
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);

这适用于所有语言,而不仅仅是复数为 n != 1n != 1 的语言。 code> - 这包括具有多种复数类型的语言。

您还可以像这样添加译者注释:

/** NOTE: The name Coconut Hotel is a brand name and shouldn't be 
 translated.
*/
echo __('Welcome to Coconut Hotel');

您可以将文本从 NOTE 更改为您想要的任何内容,但您必须在下面的 shell 脚本中更改它。 重要:译者注释必须是 紧接在 __() 函数之前行的注释的一部分,否则不会被选中当我们扫描 PHP 文件中的可翻译字符串时。

// Warning! THIS WILL NOT WORK!
/* NOTE: This translator's note will not be picked up because it is
not immediately preceding the __() function. */
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);
// Warning! THIS WILL NOT WORK!

准备好将字符串发送给翻译器后,将以下内容保存为应用程序根目录中的 shell 脚本(例如 update.sh):

#!/bin/sh
find . -iname "*.php" | xargs xgettext --add-comments=NOTE --keyword=__:1,2 --keyword=__ --from-code=UTF-8 -o i18n.pot
find . -name '*.po' | xargs -I{} msgmerge -U {} i18n.pot

要执行它,只需执行以下操作:

cd /path/to/script && sh update.sh

这将递归扫描该目录中的所有 PHP 文件并创建一个 .pot 文件(我将其命名为 i18n.pot,但您可以随意命名它)并更新任何现有的 .po > 它找到的包含新字符串的文件。

然后,我们需要创建存储所有区域设置文件的目录,每个区域设置一个目录。它们的格式必须为 ./locale/{locale}/LC_MESSAGES。例如:

cd /path/to/your/project
mkdir -p ./locale/en_US.UTF-8/LC_MESSAGES
mkdir -p ./locale/es_MX.UTF-8/LC_MESSAGES
# ...etc.

您需要决定要使用的文本域。这可以是您想要的任何内容,但脚本将在该语言的 LC_MESSAGES 文件夹中查找名为 {yourTextDomain}.mo 的文件。将以下内容放入您的 PHP 脚本中:

define('TEXT_DOMAIN', 'yourdomain');
bindtextdomain(TEXT_DOMAIN, __DIR__.'/locale');
textdomain(TEXT_DOMAIN);
bind_textdomain_codeset(TEXT_DOMAIN, 'UTF-8');

然后要实际切换到另一个语言环境,请执行以下操作:

$lang = 'es_MX.UTF-8'; // Change this to the language you want to use
if (setlocale(LC_ALL, $lang) === false) {
    throw new Exception("Server error: The $lang locale is not installed. Please update the server's localisations.");
}
putenv('LC_ALL='.$lang);        

首先,将上述脚本生成的 .pot 文件发送给翻译人员。然后他们打开 Poedit 并单击 File >来自 POT/PO 文件的新内容。保存时,需要将其保存为 {yourTextDomain}.po{yourTextDomain} 需要与 PHP 脚本中的文本域完全相同。当他们保存它时,它将自动创建 .po 文件和 .mo 文件。翻译完成后,这两个文件都需要保存在该语言的 LC_MESSAGES 目录中。

现在,当您更新 PHP 文件中的字符串时,只需重新执行 shell 脚本并将新更新的 .po 文件发送给翻译人员即可。然后,他们翻译字符串,并且 .po.mo 文件都需要重新上传。

就是这样。设置起来可能有点困难,但是一旦设置并运行,它就非常容易了。

I know this is an old question, but I feel that the answers are lacking a more hands-on approach from start to finish. This is what I did to get translation working using PHP's gettext library and Poedit without using any additional PHP libraries on a Debian server:

Preparation step 1: Install gettext and the locales on the server

I am not sure how this is done with other operating systems, but for Debian, you do:

sudo apt-get install gettext
sudo dpkg-reconfigure locales

Edit: I assumed Ubuntu would be the same as Debian, but apparently it's slightly different. See this page for instructions for installing locales on Ubuntu.

Make sure you select all of the locales that you want to use. You should then see something like:

Generating locales (this might take a while)...
  en_US.UTF-8... done
  es_MX.UTF-8... done
  fr_FR.UTF-8... done
  zh_CN.UTF-8... done
Generation complete.

Note: Make sure you select the right variants and character encodings (most likely UTF-8) for each language. If you install es_MX.UTF-8 and try to use es_ES.UTF-8 or es_MX.ISO-8859-1 it won't work.
Preparation step 2: Install Poedit on the translators' computers

Poedit is available from the software repository for most Linux operating systems. For Debian-based, just execute:

sudo apt-get install poedit

For Windows and Mac, go to: https://poedit.net/download


Start coding:

Ok, now you're ready to get started coding. I wrote the following gettext() wrapper function to translate both singular and plurals:

function __($text, $plural=null, $number=null) {
    if (!isset($plural)) {
        return _($text);
    }
    return ngettext($text, $plural, $number);
}

Example usage:

// Singular
echo __('Hello world');

// Plural
$exp = 3;
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);

This will work for all languages, not only languages where plural is anything where n != 1 - this includes languages with multiple plural types.

You can also add translator notes like this:

/** NOTE: The name Coconut Hotel is a brand name and shouldn't be 
 translated.
*/
echo __('Welcome to Coconut Hotel');

You can change the text from NOTE to whatever you want, but you will have to alter it in the shell script below. Important: The translators note must be part of a comment on the line immediately preceding the __() function or it won't be picked up when we scan the PHP files for translatable strings.

// Warning! THIS WILL NOT WORK!
/* NOTE: This translator's note will not be picked up because it is
not immediately preceding the __() function. */
printf(
    __(
        'Your account will expire in %d day',
        'Your account will expire in %d days',
        $exp
    ),
    $exp
);
// Warning! THIS WILL NOT WORK!

After you are ready to send the strings off to the translators, save the following as a shell script (e.g. update.sh) in your application's root directory:

#!/bin/sh
find . -iname "*.php" | xargs xgettext --add-comments=NOTE --keyword=__:1,2 --keyword=__ --from-code=UTF-8 -o i18n.pot
find . -name '*.po' | xargs -I{} msgmerge -U {} i18n.pot

To execute it, just do:

cd /path/to/script && sh update.sh

This will recursively scan for all PHP files in that directory and create a .pot file (I called it i18n.pot, but feel free to name it whatever you like) and update any existing .po files it finds with the new strings.

We then need to create the directories that all the locale files will be stored, one for each locale. They need to be of the format ./locale/{locale}/LC_MESSAGES. For example:

cd /path/to/your/project
mkdir -p ./locale/en_US.UTF-8/LC_MESSAGES
mkdir -p ./locale/es_MX.UTF-8/LC_MESSAGES
# ...etc.

You need to decide on a text domain to use. This can be anything you want, but the script will look for a file called {yourTextDomain}.mo within the LC_MESSAGES folder for that language. Put the following in your PHP script:

define('TEXT_DOMAIN', 'yourdomain');
bindtextdomain(TEXT_DOMAIN, __DIR__.'/locale');
textdomain(TEXT_DOMAIN);
bind_textdomain_codeset(TEXT_DOMAIN, 'UTF-8');

Then to actually switch to another locale, do:

$lang = 'es_MX.UTF-8'; // Change this to the language you want to use
if (setlocale(LC_ALL, $lang) === false) {
    throw new Exception("Server error: The $lang locale is not installed. Please update the server's localisations.");
}
putenv('LC_ALL='.$lang);        

Initially, you send the .pot file generated by the script above to the translators. They then open Poedit and click on File > New from POT/PO file. When they save it, they need to save it as {yourTextDomain}.po. The {yourTextDomain} needs to be exactly the same as the text domain you have in your PHP script. When they save it, it will automatically create both the .po file and the .mo file. Both of these need to be saved in that language's LC_MESSAGES directory when they are done translating.

Now when you update the strings in your PHP file, just re-execute the shell script and send the newly updated .po files to the translators. They then translate the strings and both the .po and .mo files need to be re-uploaded.

That's it. It may seem slightly difficult to get set up, but once you have it up and running, it's really easy.

朕就是辣么酷 2024-12-05 13:36:36

Gettext 似乎就是你所需要的。
有一个按语言分类的文件(原始文件除外),并且非常易于使用:

echo _('Bonjour, ça va ?');

将打印 Hello , how are you ?用英语。

一些带有 gettext 的工具可以扫描您的php 文件并搜索可翻译字符串(实际上 _() 或 gettext() 中的所有字符串)。因此,您不必担心不同的语言文件。您只需使用原始语言对您的网站进行编码,语言文件稍后将自动创建。

尽管如此,gettext 更多的是一种翻译工具,而 intl 实际上是一种 i18n 工具(例如数字格式化)

Gettext seems to be what you need.
There is a file by langage (except for the original one) and it's very easy to use :

echo _('Bonjour, ça va ?');

will print Hello , how are you ? in english.

There is some tools with gettext that could scan your php file and search for translatable string (in fact all string in _() or gettext()). Thanks to that you don't have to worry about the different langage file. You just code your website in the original langage and the langage file will automatically created later.

Nevertheless gettext is more a translation tools whereas intl is really an i18n one (number formating for example)

只怪假的太真实 2024-12-05 13:36:36

尽管您不需要框架,但您可以使用框架。 Zend Framework 中的国际化功能非常好,您可以只使用其中的一部分,而不用使用所有部分(包括 MVC)

Althought you don't need a framework you can use a framework. The internationalization features in Zend Framework is pretty good and you can just use that part of it instead of using all the parts (including MVC)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文