用原始语言切换 gettext 翻译语言

Posted

技术标签:

【中文标题】用原始语言切换 gettext 翻译语言【英文标题】:Switch gettext translated language with original language 【发布时间】:2011-06-09 16:02:52 【问题描述】:

我以德语的所有文本启动我的 php 应用程序,然后使用 gettext 提取所有字符串并将它们翻译成英语。 所以,现在我有一个 .po 文件,其中所有 msgids 为德语,msgstrs 为英语。我想切换它们,以便我的源代码包含英文作为 msgids,主要原因有两个:

    更多的翻译人员会懂英语,因此只适合为他们提供带有msgids 的英文文件。我可以总是在发出文件之前和收到文件之后切换文件,但是不。 如果内容文本也是英文,这将有助于我编写英文的对象和函数名称以及 cmets。我愿意这样做,因此该项目对其他开源合作者更开放(更可能懂英语而不是德语)。

我可以手动完成这项任务,我预计我会花更多时间为其编写自动化例程(因为我对 shell 脚本非常不好)用手做。但我也希望像往常一样轻视每一分钟的体力计算机劳动(感觉像是矛盾的说法,对吗?)。

以前有人做过吗?我认为这将是一个常见问题,但找不到任何东西。非常感谢。

示例问题:

<title><?=_('Routinen')?></title>

#: /users/ruben/sites/v/routinen.php:43
msgid "Routinen"
msgstr "Routines"

我想我会缩小问题的范围。 .po文件中的切换当然没问题,就这么简单

preg_replace('/msgid "(.+)"\nmsgstr "(.+)"/', '/msgid "$2"\nmsgstr "$1"/', $str);

对我来说,问题是在解析 .po 文件时搜索我的项目文件夹文件以查找 _('$msgid') 并替换 _('msgstr') 的例程(这可能甚至不是最优雅的方式,毕竟 .po 文件包含 cmets,其中包含 msgid 出现的所有文件路径)。


在fooling around 稍微回答了akirk 之后,我又遇到了一些问题。

    因为我有_('xxx')_("xxx") 的混合调用,所以我必须小心(取消)转义。 msgids 和 msgstrs 中的双引号 " 必须不转义,但斜线不能去掉,因为可能是双引号在 PHP 中也被转义了 单引号在替换为 PHP 时必须进行转义,但随后也必须在 .po 文件中进行更改。幸运的是,单引号仅出现在英文文本中。 msgids 和 msgstrs 可以有多行,那么它们看起来像这样msgid = """line 1\n""line 2\n"msgstr = """line 1\n"@ 987654333@ 目前当然会跳过复数形式,但在我的情况下,这不是问题 poedit 想要删除似乎已成功切换的过时字符串,但我不知道为什么会在(很多)情况下发生这种情况。

今晚我将不得不停止工作。尽管如此,使用解析器而不是 RegExps 似乎并不过分。

【问题讨论】:

棘手,不仅需要切换gettext文件中的条目,还需要替换代码中的所有字符串。 @tharkun:是的,当然,这就是我需要做的,但这对我来说似乎并不那么棘手。我想我可以用 PHP 字符串来做,但不能用 shell。以最简单的方式,只需解析(或搜索).po 文件中的 msgids 和 strs,然后搜索并替换文件夹中的所有文件以查找该字符串。我在帖子中包含了开关,以缩小问题范围。 我认为你在这里是正确的。诀窍是确保您使用的正则表达式不会无意中更改实际来源。不要忘记正则表达式还需要处理对_(...) 的单引号和双引号调用。祝你好运。 @Yzmir Ramirez 我想更改实际的源文件。对我来说,trick 正在正确解析 po-file 以提供我的搜索和替换脚本文件和搜索字符串,而 那是我卡住的地方。 【参考方案1】:

我以 akirk 的答案为基础,并希望在此处保留我想出的答案,以防有人遇到同样的问题。 这不是递归的,但当然很容易改变。随意评论改进,我会观看和编辑这篇文章。

$po = file_get_contents("locale/en_GB/LC_MESSAGES/messages.po");

$translations = array(); // german => english
$rawmsgids = array(); // find later
$msgidhits = array(); // record success
$msgstrs = array(); // find later

preg_match_all('/msgid "(.+)"\nmsgstr "(.+)"/', $po, $matches, PREG_SET_ORDER);

foreach ($matches as $match) 
    $german = str_replace('\"','"',$match[1]); // unescape double quotes (could misfire if you escaped double quotes in PHP _("<a href=\"bla\">bla</a>") but in my case that was one case versus many)
    $english = str_replace('\"','"',$match[2]);


    $en_sq_e = str_replace("'","\'",$english); // escape single quotes

    $translations['_(\''. $german . '\''] = '_(\'' . $en_sq_e . '\'';
    $rawmsgids['_(\''. $german . '\''] = $match[1]; // find raw msgid with searchstr as key

    $translations['_("'. $match[1] . '"'] = '_("' . $match[2] . '"';
    $rawmsgids['_("'. $match[1] . '"'] = $match[1];

    $translations['__(\''. $german . '\''] = '__(\'' . $en_sq_e . '\'';
    $rawmsgids['__(\''. $german . '\''] = $match[1];

    $translations['__("'. $match[1] . '"'] = '__("' . $match[2] . '"';
    $rawmsgids['__("'. $match[1] . '"'] = $match[1];

    $msgstrs[$match[1]] = $match[2]; // msgid => msgstr



foreach (glob("*.php") as $file) 
    $code = file_get_contents($file);

    $filehits = 0; // how many replacements per file

    foreach($translations AS $msgid => $msgstr) 
        $hits = 0;
        $code = str_replace($msgid,$msgstr,$code,$hits);
        $filehits += $hits;

        if($hits!=0) $msgidhits[$rawmsgids[$msgid]] = 1; // this serves to record if the msgid was found in at least one incarnation
        elseif(!isset($msgidhits[$rawmsgids[$msgid]])) $msgidhits[$rawmsgids[$msgid]] = 0;
    
    // file_put_contents($file, $code); // be careful to test this first before doing the actual replace (and do use a version control system!) 
    echo "$file : $filehits <br>"; 
    echo $code;

/* debug */ 
$found = array_keys($msgidhits, 1, true);
foreach($found AS $mid) echo $mid . " => " . $msgstrs[$mid] . "\n\n";

echo "Not Found: <br>";
$notfound = array_keys($msgidhits, 0, true);
foreach($notfound AS $mid) echo $mid . " => " . $msgstrs[$mid] . "\n\n";

/*
following steps are still needed:
    * convert plurals (ngettext)
    * convert multi-line msgids and msgstrs (format mentioned in question)
    * resolve uniqueness conflict (msgids are unique, msgstrs are not), so you may have duplicate msgids (poedit finds these)
*/

【讨论】:

【参考方案2】:

请参阅http://code.activestate.com/recipes/475109-regular-expression-for-python-string-literals/,了解一个很好的基于 python 的正则表达式,用于查找字符串文字,并考虑转义。尽管它是 python,但这对于多行字符串和其他极端情况可能非常有用。

请参阅http://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/poswap.html 了解 .po 文件的现成、开箱即用的基础语言交换器。

例如,以下命令行会将基于德语的西班牙语翻译转换为基于英语的西班牙语翻译。在开始转换之前,您只需确保您的新基础语言(英语)已 100% 翻译:

poswap -i de-en.po -t de-es.po -o en-es.po

最后将英语 po 文件交换为德语 po 文件,使用 swappo: http://manpages.ubuntu.com/manpages/hardy/man1/swappo.1.html

交换文件后,可能需要对生成的文件进行一些手动优化。例如,标题可能会损坏,并且可能会出现一些重复的文本。

【讨论】:

我发布了一个 Python 脚本来交换 PO 文件中的源/目标语言。这可能对这种情况有用:mola.io/2013/09/17/swapping-languages-in-gettext-po-file【参考方案3】:

因此,如果我理解正确,您希望将所有德语 gettext 调用替换为英语调用。要替换目录中的内容,可以这样做。

$po = file_get_contents("translation.pot");
$translations = array(); // german => english
preg_match_all('/msgid "(.+)"\nmsgstr "(.+)"/', $po, $matches, PREG_SET_ORDER);
foreach ($matches as $match) 
    $translations['_("'. $match[1] . '")'] = '_("' . $match[2] . '")';
    $translations['_(\''. $match[1] . '\')'] = '_(\'' . $match[2] . '\')';

foreach (glob("*.php") as $file) 
    $code = file_get_contents($file);
    $code = str_replace(array_keys($translations), array_values($translations), $code);
    //file_put_contents($file, $code);
    echo $code; // be careful to test this first before doing the actual replace (and do use a version control system!)

【讨论】:

是的,但即使我当然可以将 po 文件作为字符串提供,我也需要搜索和替换 php 文件目录,而不是字符串。最后,我还想知道哪些 msgids 无法找到(这将是对复数形式和占位符的函数调用:太少了以至于我可以手动完成)。我希望 gettext 解析器本身可以以某种方式使用,毕竟它已经做了非常相似的事情(解析 php 文件并在指定的函数调用中找到 msgids)。 我不知道 gettext 发行版中有一个工具,您必须手动完成(这不是那么乏味)。我已更改代码以反映这一点。 我已经对你的脚本做了一点pastebin.com/J7ipM1fy 以便更容易地查看找到了哪些字符串。处理引号和多行字符串并非易事,我将更新我的问题以反映这一点。

以上是关于用原始语言切换 gettext 翻译语言的主要内容,如果未能解决你的问题,请参考以下文章

Cakephp 3 先保存翻译。之后的原始实体

UWP 切换语言

如何在原始 DOM 和内容脚本更改的 DOM 之间切换?

gettext 的替换

前端html页面如何实现多语言切换功能

QT实多国语言切换