自动翻译 .po 文件? [关闭]
Posted
技术标签:
【中文标题】自动翻译 .po 文件? [关闭]【英文标题】:Automatically translate .po files? [closed] 【发布时间】:2012-04-30 20:11:01 【问题描述】:曾经有一些服务使用 Google Translate API V1 自动翻译 .po
文件。
Google 已经停止了他们的 V1 API,在 V2 中,他们对 1.000.000 字收取 20 美元。
我已搜索但找不到任何提供 V2 版本翻译的工具。您会期望有人更新他们的工具并为 20.000 字收取 2 美元并赚取可观的利润。
是否有任何付费或免费工具可以自动翻译.po
文件?
【问题讨论】:
Google 不允许您根据其服务条款转售其付费翻译,因此没有人可以按照您的建议进行操作... "attranslate" 是一个现代工具,完全符合您的要求:github.com/fkirc/attranslate 【参考方案1】:有点晚了,但谷歌现在提供了这个功能
http://translate.google.com/toolkit/list?hl=en#translations/active
谷歌翻译已关闭此服务:/
【讨论】:
这太棒了!它可以节省 90% 的工作量! 这不再是免费的。费用也相当高 它仍然可以免费使用,尽管界面令人困惑。谷歌试图将您引向翻译提供商这一事实并没有帮助。您可以点击“不,谢谢”,将翻译文件标记为“完成”,然后下载自动翻译的文本。 天哪。这太棒了。 在 google 激励我们全世界的所有人“建议”并免费为他们翻译之后,......现在他们已经足够了,他们向我们收费!酷哈?【参考方案2】:查看我的免费 php 工具 Potrans,它使用 Google Translator API 翻译 PO 文件
从这里的仓库下载:https://github.com/OzzyCzech/potrans
【讨论】:
太棒了! %s 和 %d 变量有一些错误,但总而言之,这很棒。谢谢 当我打算写自己的翻译器时,我来到这里(顺便说一下预搜索引擎)并尝试了这个项目。截至 2021 年 10 月 8 日,它已于 8 个月前更新。我遇到了一个需要合并更改的缺陷,它与 FUZZY 记录有关。【参考方案3】:http://sourceforge.net/projects/po-auto-tran/?source=dlp 给你,我刚刚下载并使用它,效果很好!!!我不知道这是否回答了你的问题,但它对我来说是个无用的工作
【讨论】:
【参考方案4】:请随时编辑此帖子以进行更正或更好地处理错误。我不是 .po
文件格式的专家,但我认为这将满足我的 angular-gettext 需求。我认为我使用的唯一外部库是 Newtonsoft 的 Json.NET。我不隶属于 Fengly。它出现在谷歌上。如果您每 3 秒需要 1 次以上的翻译,我建议您使用不同的翻译 API。
输入文件:template.pot
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: Comment
msgid "You do not have permission to view this application"
msgstr ""
msgid "You have permission to view this application"
msgstr ""
输入指令:> program.exe template.pot en es_VE fr_FR
输出文件 1:en.cache.json 这是为了让您使用的任何翻译实用程序都不必一遍又一遍地点击。
"es_VE":
"You do not have permission to view this application": "Tu no la habana permiso que vista este aplicación",
"You have permission to view this application": "Tu tienes permiso que vista este aplicación"
,
"fr_FR":
"You do not have permission to view this application": "Vous le faites pas as autorisation a vue cette une demande",
"You have permission to view this application": "Tuas autorisation a vue cette une demande"
输出文件 2:es_VE.po
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: Comment
msgid "You do not have permission to view this application"
msgstr "Tu no la habana permiso que vista este aplicación"
msgid "You have permission to view this application"
msgstr "Tu tienes permiso que vista este aplicación"
输出文件 3:fr_FR.po
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: Comment
msgid "You do not have permission to view this application"
msgstr "Vous le faites pas as autorisation a vue cette une demande"
msgid "You have permission to view this application"
msgstr "Tuas autorisation a vue cette une demande"
来源
public interface ICache
void Add(string language, IEntry entry);
IEntry Get(string language, string id);
string GetSerialized();
public class JsonCache : ICache
private Dictionary<string, Dictionary<string, string>> _cache;
public JsonCache(string json)
this._cache =
json == null ?
new Dictionary<string, Dictionary<string, string>>() :
JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(json);
public void Add(string language, IEntry entry)
if (!this._cache.ContainsKey(language))
this._cache.Add(language, new Dictionary<string, string>());
var languageCache = this._cache[language];
languageCache.Add(entry.Id, entry.Value);
public IEntry Get(string language, string id)
if (!this._cache.ContainsKey(language))
return null;
var languageCache = this._cache[language];
Entry result = null;
if (languageCache.ContainsKey(id))
result = new Entry();
result.Id = id;
result.Value = languageCache[id];
return result;
public string GetSerialized()
return JsonConvert.SerializeObject(this._cache, Formatting.Indented);
public interface IReader : IDisposable
IEntry Read();
public class PoReader : IReader
private StreamReader _reader;
public PoReader(string fileName)
this._reader = new StreamReader(fileName);
public void Dispose()
if (this._reader != null)
this._reader.Dispose();
public IEntry Read()
var entry = new Entry();
while (entry.Id == null || entry.Value == null)
var line = this._reader.ReadLine();
if (line == null)
return null;
if (line.StartsWith(Constants.StartComment))
entry.Comment = line.Substring(Constants.StartComment.Length);
else if (line.StartsWith(Constants.StartId))
entry.Id = line.Substring(Constants.StartId.Length);
// Remove the double quotes.
entry.Id = entry.Id.Substring(1, entry.Id.Length - 2);
else if (line.StartsWith(Constants.StartValue))
entry.Value = line.Substring(Constants.StartValue.Length);
// Remove the double quotes.
entry.Value = entry.Value.Substring(1, entry.Value.Length - 2);
// Skip the first entry
if (entry.Id.Length == 0)
return this.Read();
return entry;
public class CachedTranslator : ITranslator
private ITranslator _translator;
private ICache _cache;
public CachedTranslator(ICache cache, ITranslator translator)
this._translator = translator;
this._cache = cache;
public IEntry Translate(string language, IEntry entry)
var result = this._cache.Get(language, entry.Id);
if (result == null)
result = this._translator.Translate(language, entry);
this._cache.Add(language, result);
else
// We don't want to use the cached comment.
var clone = new Entry();
clone.Comment = entry.Comment;
clone.Value = result.Value;
clone.Id = result.Id;
result = clone;
return result;
public class FrenglyTranslator : ITranslator
private string _password;
private string _email;
private string _inLanguage;
public FrenglyTranslator(string email, string password, string inLanguage)
this._email = email;
this._password = password;
this._inLanguage = inLanguage;
public IEntry Translate(string language, IEntry entry)
var url = string.Format("http://syslang.com?src=4&dest=0&text=1&email=2&password=3&outformat=json",
language.Substring(0, 2),
entry.Id,
this._email,
this._password,
this._inLanguage);
var result = new Entry();
result.Id = entry.Id;
result.Comment = entry.Comment;
using (var client = new HttpClient())
var clientResult = client.GetStringAsync(url).Result;
var jo = (JObject)JsonConvert.DeserializeObject(clientResult);
result.Value = jo.Property("translation").Value.Value<string>();
// Must wait 3 seconds between calls.
Thread.Sleep(3001);
return result;
public interface ITranslator
IEntry Translate(string language, IEntry entry);
public interface IWriter : IDisposable
void Write(IEntry entry);
public class PoWriter : IWriter
private StreamWriter _writer;
public PoWriter(string fileName)
this._writer = new StreamWriter(fileName);
var header = @"msgid """"
msgstr """"
""Content-Type: text/plain; charset=UTF-8\n""
""Content-Transfer-Encoding: 8bit\n""";
this._writer.WriteLine(header);
public void Write(IEntry entry)
this._writer.WriteLine();
if (entry.Comment != null && entry.Comment.Length > 0)
this._writer.WriteLine(Constants.StartComment + entry.Comment);
this._writer.WriteLine(string.Format("0\"1\"", Constants.StartId, entry.Id));
this._writer.WriteLine(string.Format("0\"1\"", Constants.StartValue, entry.Value));
public void Dispose()
if (this._writer != null)
this._writer.Dispose();
public static class Constants
public const string StartComment = "#: ";
public const string StartId = "msgid ";
public const string StartValue = "msgstr ";
public class Entry : IEntry
public string Comment get; set;
public string Value get; set;
public string Id get; set;
public interface IEntry
string Comment get;
string Value get;
string Id get;
class Program
private const string cacheFileNameSuffix = ".cache.json";
private const string frenglyEmail = "your.em@il.com";
private const string frenglyPassword = "YourPassword";
static void Main(string[] args)
//
// INITIALIZE
//
var inFileName = args[0];
var inLanguage = args[1];
var outLanguages = args.Skip(2);
// ICache
var cacheFileName = inLanguage + cacheFileNameSuffix;
var json = File.Exists(cacheFileName) ? File.ReadAllText(cacheFileName) : null;
ICache cache = new JsonCache(json);
// ITranslator
ITranslator translator = new FrenglyTranslator(frenglyEmail, frenglyPassword, inLanguage);
ITranslator cachedTranslator = new CachedTranslator(cache, translator);
// IWriters
var writers = new Dictionary<string, IWriter>();
foreach (var language in outLanguages)
writers.Add(language, new PoWriter(language + ".po"));
try
using (IReader reader = new PoReader(inFileName))
//
// RUN
//
IEntry entry = null;
while (true)
entry = reader.Read();
if (entry == null)
break;
foreach (var kv in writers)
var translated = cachedTranslator.Translate(kv.Key, entry);
kv.Value.Write(translated);
finally
// Store the cache.
File.WriteAllText(cacheFileName, cache.GetSerialized());
//
// CLEANUP
//
// Dispose of the writers.
foreach (var writer in writers.Values)
if (writer != null)
writer.Dispose();
【讨论】:
以上是关于自动翻译 .po 文件? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
Drupal 8 更新自定义模块的 language.po 文件