33

There used to be a few services that used Google Translate API V1 to automatically translate .po files.

Google has stopped their V1 API and with V2 they charge $20 for 1.000.000 words.

I have searched but cannot find any tool that offers translation with the V2 version. You would expect someone to update their tool and charge $2 for 20.000 words and make a good profit.

Are there any paid or free tools that will automatically translate .po files?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Lisa
  • 628
  • 2
  • 6
  • 14
  • 1
    Google doesn't allow you to re-sell its paid translations, according to its terms of service, so nobody would be able to do what you suggest... – daveagp Aug 31 '12 at 00:21
  • "attranslate" is a modern tool that does exactly what you are asking for: https://github.com/fkirc/attranslate – Mike76 Nov 09 '20 at 19:02
  • A new tool has been released and it's great for automagically translate .po files https://www.ajexperience.com/po-translator/ – SuperAtic Apr 03 '23 at 23:39

4 Answers4

47

little bit late but google now provides this functionality

http://translate.google.com/toolkit/list?hl=en#translations/active

google translate has shut down this service :/

Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
2

See my free PHP tools Potrans. Potrans is command line tool for automatic translation of Gettext PO file with Google Translator or DeepL Translator.

Let's check source code here: https://github.com/OzzyCzech/potrans

OzzyCzech
  • 9,713
  • 3
  • 50
  • 34
  • That's magnificent ! Had a few bugs with %s and %d variables, but all in all, it's great. Thanks – JohnWolf Feb 14 '15 at 09:48
  • When I planned to write my own translator, I came (presearch search engine, by the way) here and tried this project. As of 08.10.2021, it was updated 8 months ago. I faced a flaw that will require merging changes, it is related to FUZZY records. – sunsetjunks Oct 08 '21 at 09:42
1

Feel free to edit this post to make corrections or better error handling. I'm not an expert on the .po file format, but I think this will work with my angular-gettext needs. I think the only external library I used was Newtonsoft's Json.NET. I am not affiliated with Frengly. It came up on google. I'd recommend using a different translation API if you need more than 1 translation per 3 seconds.


Input File: 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 ""

Input Command: > program.exe template.pot en es_VE fr_FR


Output File 1: en.cache.json This is created so that whatever translation utility you're using doesn't have to be hit over and over again.

{
  "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"
  }
}

Output File 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"

Output File 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"

Source

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();
                }
            }
        }
    }
}
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
-3

http://sourceforge.net/projects/po-auto-tran/?source=dlp here you go i just downloaded and used it it works GREAT!!! i dont know if this answers your question but it dud the job for me

Dani
  • 79
  • 2
  • 12