#!/usr/bin/python2.6
#
# Copyright 2010 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""poc: .po file compiler.

Converts a single .pot file and a number of .po files into source files
containing string tables with C syntax.

For each .po file "<locale>.po" given, poc will generate a file "<locale>.po.cc"
that contains the string table for that locale.  If no .po files are given, a
file called "master.po.cc" will be generated with a "master" list of the english
strings from the .pot file.  All files will be written to the directory given by
the out_dir argument.

Each string table will have n+1 elements (where n is the number of strings in
the .pot file).  The first n elements will be the strings in the appropriate
locale, and the final element will be a NULL pointer.

Usage: poc out_dir pot_file [po_file1 [po_file2 ...]]
"""

__author__ = 'aoates@google.com (Andrew Oates)'

import os
import sys

import polib

# Template for generated string table files.
STRING_TABLE_TEMPLATE = """// Generated by poc

// Copyright 2010 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Author: aoates@google.com (Andrew Oates)

#include "pagespeed/l10n/register_locale.h"

namespace {

const char* %(table_name)s[] = {
%(table_data)s
};

pagespeed::l10n::RegisterLocale
  %(locale)s_locale(%(locale_str)s, %(table_name)s);

} // namespace
"""


def UnicodeToCLiteral(s):
  """Converts a unicode string to a C-style escaped string (e.g. "\xe1\x84")."""
  s = s.encode('utf8')
  out = ['"']
  for c in s:
    if ord(c) > 127:
      out.append(r'\x%.2x' % ord(c))
      # To prevent the C++ compiler from interpreting subsequent characters as
      # part of the hex code, we break the string literal.
      #
      # E.g. [?feet] --> ["\x3f" "feet"] instead of ["\x3ffeet"]
      out.append('" "')
    else:
      out.append(c)
  out.append('"')
  return ''.join(out)


def WriteStringTable(strings, table_name, locale, out_dir,
                     filename, comments=None):
  """Writes a c-style string table to the given file in the given directory.

  Args:
    strings: An ordered list of strings to be written to the table.
    table_name: The name of the generated table.
    locale: The locale of the string table being generated (e.g. "fr_FR"),
        or None for the master string table.
    out_dir: The directory to write the string table to.
    filename: The name of the file to generate.
    comments: A list of strings such that len(comments) == len(strings),
        each element of which is written out as a comment next to
        the corresponding string.  If None, then no comments are written.
  """
  string_lines = []

  quoted_strings = [s.replace('"', r'\"') for s in strings]
  escaped_strings = [UnicodeToCLiteral(s) for s in quoted_strings]

  if comments:
    for (s, c) in zip(escaped_strings, comments):
      string_lines.append('  %s, // %s' % (s, c))
  else:
    for s in escaped_strings:
      string_lines.append('  %s,' % s)
  string_lines.append('  0x0')

  file_contents = STRING_TABLE_TEMPLATE % {
      'table_name': table_name,
      'table_data': '\n'.join(string_lines),
      'locale_str': ('"%s"' % locale) if locale else 'NULL',
      'locale': locale if locale else 'master',
      }

  f = open(os.path.join(out_dir, filename), 'w')
  f.write(file_contents)
  f.close()


def GenerateStringTables(out_dir, pot_file, po_files):
  """Generates string tables from .po files.

  Args:
    out_dir: The directory to put the string table files in.
    pot_file: The .pot file containing the master list of strings.
    po_files: A list of .po files to generate string tables from.
  """
  # First build map of string -> id for the master table.
  master_table = {}
  master_strings = []
  pot_file = polib.pofile(pot_file)
  for entry in pot_file:
    master_strings.append(entry.msgid)
    master_table[entry.msgid] = len(master_strings)-1

  # If no .po files were given (just the .pot file), generate the master table.
  if not po_files:
    WriteStringTable(master_strings, 'master_string_table',
                     None, out_dir, 'master.po.cc')

  # Now write out a table for each locale.
  locales = []
  for po_file in po_files:
    # Extract the name of the locale from the filename ("fr.po" -> "fr").
    locale_name = os.path.splitext(os.path.basename(po_file))[0]
    locales.append(locale_name)

    if not os.path.exists(po_file):
      print >> sys.stderr, 'error: %s does not exist!' % po_file
      sys.exit(1)

    po = polib.pofile(po_file)

    translation_table = {}
    for entry in po:
      translation_table[entry.msgid] = entry.msgstr

    locale_strings = []
    for ms in master_strings:
      if ms in translation_table:
        locale_strings.append(translation_table[ms])
      else:
        locale_strings.append('')

    if len(locale_strings) != len(master_strings):
      print >> sys.stderr, ('warning: %s doesn\'t match master .pot file' %
                            po_file)

    WriteStringTable(locale_strings, '%s_string_table' % locale_name,
                     locale_name, out_dir, '%s.po.cc' % locale_name,
                     comments=master_strings)


def main():
  if len(sys.argv) < 3:
    print >> sys.stderr, (
        'Usage: poc out_dir pot_file [po_file1 [po_file2 ...]]')
    sys.exit(1)

  GenerateStringTables(sys.argv[1], sys.argv[2], sys.argv[3:])

if __name__ == '__main__':
  main()
