当前位置: 首页 > 工具软件 > Poedit > 使用案例 >

PHP基于gettexts实现多语言i18n利用PoEdit

厍晋鹏
2023-12-01

PHP基于gettexts实现多语言i18n利用PoEdit

错误问题

有时候, 添加翻译, 不生效的话. 充值FPM即可.

今天在使用的过程中遇到一个问题:
    在原有的XX.po文件中, 更新检索需要翻译的内容时报错.
    
    提示:
        poedit错误, 更新条目失败, XXX
        细节中,编目中的条目可能不正确.
        更新条目失败XXXXX.
    
    经过查看.编目 > 属性中的源路径和源关键字的配置.发现
    源路径指定位置错误, 应该指定到该项目的跟目录, (原理就是)将所有
    XXX.PHP文件和XXX.phtml文件_()包起来的全部内容.
    但是, 此时的路径指定到了 /User/liuhao/www/项目目录/conf/lang/en_US/下
    此时,当然是匹配不到任何东西的, 所以报错.
    同时也没有排除项目下的public下的静态文件, 如JS和CSS之类.
    同时,源标识符_()也没有.所以报错.
    完善着三个选项后, 问题解决.

说明

PHP 低版本需要开启gettext扩展

gettext简介:
GNU gettext是翻译项目的重要一步,它提供了一个工作框架,由一些集成的工具和文档组成,帮助程序员、翻译人员和最终用户实现程序的国际化和本地化。用 Gettext的方式实现多语言得到了广泛的支持,著名的BLOG程序wordpress的国际化就是用的GNU gettext。

大致原理:
GNU gettext使用PO或MO文件来实现国际化和本地化。PO的意思是Portable Object,是一种文本结构,可以方便的由人们阅读和修改。MO是Machine Object的简写,MO文件是PO文件的二进制形态。一般来说,一个PO或MO文件对应于一种语言,如果一个程序要支持多种语言,每一种语言都需要自己 的PO或MO文件。


注意:
	所谓的gettext()在PHP中即为_()函数.两个函数应该是相当于 implode和join的关系.
	
必须的PHP部分:
		
# 域名,可以任意取个有意义的名字,不过要跟相应的XXX.mo文件的文件名相同(不包括扩展名)。
$domain  =  'default' ;
# 设置一个服务器环境变量,环境变量仅存活在当前请求期间
putenv('LANG=' . $lang);
# 指定要用的语系,如:en_US、zh_CN、zh_TW,应当与/language/LC_MESSAGES/test.po这里的language相同
# 如果在该步骤,指定后缀名.utf8,就不需要bind_textdomain_codeset指定UTF-8了.
# 如:setlocale(LC_ALL, $lang . '.utf8');
setlocale(LC_ALL, $lang');
# 设置某个域的mo文件路径
bindtextdomain($domain, APPLICATION_PATH."/conf/lang/");
# 设置mo文件的编码为UTF-8 
# 如果在setlocale()指定后缀名.utf8就不需要这里了,如:setlocale(LC_ALL, $lang . '.utf8');
bind_textdomain_codeset($domain ,  'UTF-8' );
# 设置gettext();函数从哪个域去寻找mo文件
textdomain($domain);

PoEdit使用方式

建设XX.po文件的流程, 和提取需要翻译的文字.
注意:
	提取要翻译的内容时, 一定要排除静态文件.
	
1. 选择文件, 新建文件, 选择语言, 保存文件到指定的目录, 该目录下回生成XXX.po文件.
2. 打开该XXX.po文件
3. 选择XX中提取, (其中有一行注释: 你可以直接从源码中提取可翻译的字符串)
4. 在源路径中下面的"+"添加要提取的目录.如果有要排除的路径如: public下的静态文件,添加目录即可.
5. 源关键字中添加关键字, 如: _(), 一般PHP中就是用_()作为要翻译的内容添这个就好.
6. 然后, PoEdit就会将目录中所有,_()标识过的可读取的内容,检索出来.
7. 选中需要翻译的内容, 在下面翻译的输入框中输入对应的内容即可.
8. 完毕之后保存即可

注意:

将po文件和mo文件放入需要放入的项目目录 时
	要注意:
		/LC_MESSAGES/这一层目录名,必须这样写.暂未找到自定义之法
		XXX.po的文件名必须和之前
		bindtextdomain();bind_textdomain_codeset;textdomain();
		定义的域的名称一样.
 
/locale/language/LC_MESSAGES/test.po
/locale/language/LC_MESSAGES/test.mo 

如我们放入的是简体中文,则放入: 

 
/locale/zh_CN/LC_MESSAGES/test.po
/locale/zh_CN/LC_MESSAGES/test.mo 


如我们放入的是繁体中文,则放入: 
 
/locale/zh_TW/LC_MESSAGES/test.po
/locale/zh_TW/LC_MESSAGES/test.mo 

我的案例

注释:
	我写的一个简单的案例, 使用面向过程,没有进行封装, 和使用static来避免多次生成.
	目的在于, 更加直观, 后期如果忘了, 我觉得看这里是最好回忆的.


# 多语言
# 这里的语言标识, 使用zh-cn 和en-us之类的用"-"线, 是为了,和$_SERVER['HTTP_ACCEPT_LANGUAGE']请求头中的"-"线,
# 后面就就不用来用正则替换$_SERVER['HTTP_ACCEPT_LANGUAGE']中的"-"为"_"了,同时兼容性也更强
# 百度爬虫, 来访问设置为中文
if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'aiduspider')) {
	$_GET['lang'] = 'zh-cn';
}
# 支持的语言列表
$supportLang = array(
	'zh-cn' => 'zh_CN',     # 中文
	'en-us' => 'en_US'      # 英文
	);

# 通过URL传来的GET自然语言,参数为优先级最高
if(isset($_GET['lang']))
{
	$lang = $_GET['lang'];
	
	# cookie中保存的自然语言, 其次
}else if(isset($_COOKIE['lang']))
{
	$lang = $_COOKIE['lang'];
	
	# 获取浏览器自然语言, 最后
}else{
	$lang = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 5));
}

# 如果浏览器支持不完善,或被篡改导致未命中服务器支持的语言类型, 默认为英文
$lang = strtolower($lang);
$lang = isset($supportLang[$lang]) ? $supportLang[$lang]: 'en_US';

# 设置cookie, 记录下次访问的默认语言
setcookie('lang', $lang, null,'/');

putenv("LANG={$lang}" );
# 设置本地语言,如zh_CN en_US
setlocale(LC_ALL, $lang );
# 设置域的名称
$domain  =  'default' ;
# 绑定域,自动检索的目录
bindtextdomain ( $domain ,  APPLICATION_PATH."/conf/lang/");
# 绑定默认编码类型
bind_textdomain_codeset($domain ,  'UTF-8' );
# 设置gettext()即_()函数从哪个域去找mo文件,用来替换gettext()中的内容
textdomain($domain );

简单案例

<?
	$lang  =  $_GET [ 'lang' ];
	if ( $lang  ==  'zh_CN' ){
		putenv('LANG=zh_CN' );
		setlocale(LC_ALL, 'zh_CN' );  //指定要用的语系,如:en_US、zh_CN、zh_TW应当与语言目录的名称相同
	}elseif  ( $lang  ==  'en_US' ) {
		putenv('LANG=en_US' );
		setlocale(LC_ALL, 'en_US' );  //指定要用的语系,如:en_US、zh_CN、zh_TW应当与语言目录的名称相同
	}
	
	$domain  =  'default' ;                         # 域名,可以任意取个有意义的名字,不过要跟相应的.mo文件的文件名相同(不包括扩展名)。
	bindtextdomain ( $domain ,  "conf/lang/" );     # 设置某个域的mo文件路径
	bind_textdomain_codeset($domain ,  'UTF-8' );   # 设置mo文件的编码为UTF-8
	textdomain($domain );                           # 设置gettext()函数在PHP中相当于_()从哪个域去找mo文件
	
	# 输出测试
	echo gettext('你好');   # 也可以用_('你好')
?>

典型案例

<?php
/**
 * 多语言类
 */
class Lang
{
	# 全部语种
	public static $alllang = array('en_US' => 1, 'zh_CN' => 1, 'ja_JP' => 1);

	/**
	 * 设置语言
	 * @return string
	 */
	static function langcookie()
	{
		static $lang;
		if(!$lang){
			# 当前语种
			if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'aiduspider')) {
				$_GET['lang'] = 'zh_CN';
			}
			# GET
			isset($_GET['lang'], self::$alllang[$_GET['lang']]) && $lang = $_GET['lang'];
			# COOKIE
			if (empty($lang) && isset($_COOKIE['lang'], self::$alllang[$_COOKIE['lang']])) {
				$lang = $_COOKIE['lang'];
			}
			# LANGUAGE
			if (empty($lang) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
				$l = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 4);
				if (preg_match("/zh-c/i", $l)) $lang = 'zh_CN';
				elseif (preg_match("/en/i", $l)) $lang = 'en_US';
			}
			empty($lang) && $lang = 'zh_CN';
			setcookie('lang', $_COOKIE['lang'] = $lang, time() + 86400 * 3650, '/');
		}
		return $lang;
	}

	/**
	 * 处理语言
	 */
	static function lang()
	{
		# 判断用户自然语言类型
		$lang = self::langcookie();
		# 调用自然语言
		if (isset(self::$alllang[$lang])) {
			# 设置一个环境变量,环境变量仅存活在当前请求期间
			putenv('LANG=' . $lang);
			# 指定要用的语系,如:en_US、zh_CN、zh_TW, 语言目录应当与此处相同.
			setlocale(LC_ALL, $lang . '.utf8');
			# 设置某个域的mo文件路径 
			bindtextdomain("default", APPLICATION_PATH . "/conf/lang/");
			# 设置gettext()即_()函数从哪个域去找mo文件
			textdomain("default");
		}
	}
}

Lang::lang()

XXX.po文件剖析

由此可见
		前面一段是头文件,标识创建时间,文件语言内容,版本,MINI类型等信息.
		msgid		为源内容
		msgstr	为替换的内容
		即为, 将原内容, 替换为替换内容.


文件内容:

msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2017-09-20 00:18+0800\n"
"PO-Revision-Date: 2017-09-20 01:02+0800\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.3\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: .\n"
"X-Poedit-SearchPathExcluded-0: public\n"

#: application/controllers/News.php:62
msgid "参数错误"
msgstr "en参数错误"

#: application/controllers/News.php:66 application/controllers/News.php:68
msgid "您访问的页面不存在或已删除!"
msgstr "en您访问的页面不存在或已删除!"

#: application/views/index/index.phtml:18
msgid "我们的业务"
msgstr "en我们的业务"

#: application/views/index/index.phtml:19
msgid "我们致力于为世界提供最好的区块链产品和服务"
msgstr "en我们致力于为世界提供最好的区块链产品和服务"

转载于:https://my.oschina.net/chinaliuhan/blog/3064495

 类似资料: