summaryrefslogtreecommitdiffstats
path: root/remoting/tools
diff options
context:
space:
mode:
authordcaiafa <dcaiafa@chromium.org>2014-12-01 15:28:57 -0800
committerCommit bot <commit-bot@chromium.org>2014-12-01 23:29:50 +0000
commit9e715f2331c7932ae0b44cc13dc3befc133016b5 (patch)
tree780bc8a1d25d5afdd86632cd51b77b69534d9731 /remoting/tools
parentaab8bd999e5bf1d0a8c4116c73ecdbbf31212acb (diff)
downloadchromium_src-9e715f2331c7932ae0b44cc13dc3befc133016b5.zip
chromium_src-9e715f2331c7932ae0b44cc13dc3befc133016b5.tar.gz
chromium_src-9e715f2331c7932ae0b44cc13dc3befc133016b5.tar.bz2
Chromoting iOS Client l10n support
- Add a script to generate iOS localization files from grit-generated data-packs. - Update remoting_strings.grd to make Android strings available to iOS as well. Review URL: https://codereview.chromium.org/746313002 Cr-Commit-Position: refs/heads/master@{#306290}
Diffstat (limited to 'remoting/tools')
-rwxr-xr-xremoting/tools/build/remoting_ios_localize.py311
1 files changed, 311 insertions, 0 deletions
diff --git a/remoting/tools/build/remoting_ios_localize.py b/remoting/tools/build/remoting_ios_localize.py
new file mode 100755
index 0000000..04beb18
--- /dev/null
+++ b/remoting/tools/build/remoting_ios_localize.py
@@ -0,0 +1,311 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tool to produce localized strings for the remoting iOS client.
+
+This script uses a subset of grit-generated string data-packs to produce
+localized string files appropriate for iOS.
+
+For each locale, it generates the following:
+
+<locale>.lproj/
+ Localizable.strings
+ InfoPlist.strings
+
+The strings in Localizable.strings are specified in a file containing a list of
+IDS. E.g.:
+
+Given: Localizable_ids.txt:
+IDS_PRODUCT_NAME
+IDS_SIGN_IN_BUTTON
+IDS_CANCEL
+
+Produces: Localizable.strings:
+"IDS_PRODUCT_NAME" = "Remote Desktop";
+"IDS_SIGN_IN_BUTTON" = "Sign In";
+"IDS_CANCEL" = "Cancel";
+
+The InfoPlist.strings is formatted using a Jinja2 template where the "ids"
+variable is a dictionary of id -> string. E.g.:
+
+Given: InfoPlist.strings.jinja2:
+"CFBundleName" = "{{ ids.IDS_PRODUCT_NAME }}"
+"CFCopyrightNotice" = "{{ ids.IDS_COPYRIGHT }}"
+
+Produces: InfoPlist.strings:
+"CFBundleName" = "Remote Desktop";
+"CFCopyrightNotice" = "Copyright 2014 The Chromium Authors.";
+
+Parameters:
+ --print-inputs
+ Prints the expected input file list, then exit. This can be used in gyp
+ input rules.
+
+ --print-outputs
+ Prints the expected output file list, then exit. This can be used in gyp
+ output rules.
+
+ --from-dir FROM_DIR
+ Specify the directory containing the data pack files generated by grit.
+ Each data pack should be named <locale>.pak.
+
+ --to-dir TO_DIR
+ Specify the directory to write the <locale>.lproj directories containing
+ the string files.
+
+ --localizable-list LOCALIZABLE_ID_LIST
+ Specify the file containing the list of the IDs of the strings that each
+ Localizable.strings file should contain.
+
+ --infoplist-template INFOPLIST_TEMPLATE
+ Specify the Jinja2 template to be used to create each InfoPlist.strings
+ file.
+
+ --resources-header RESOURCES_HEADER
+ Specifies the grit-generated header file that maps ID names to ID values.
+ It's required to map the IDs in LOCALIZABLE_ID_LIST and INFOPLIST_TEMPLATE
+ to strings in the data packs.
+"""
+
+
+import codecs
+import optparse
+import os
+import re
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..',
+ 'tools', 'grit'))
+from grit.format import data_pack
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..',
+ 'third_party'))
+import jinja2
+
+
+LOCALIZABLE_STRINGS = 'Localizable.strings'
+INFOPLIST_STRINGS = 'InfoPlist.strings'
+
+
+class LocalizeException(Exception):
+ pass
+
+
+class LocalizedStringJinja2Adapter:
+ """Class that maps ID names to localized strings in Jinja2."""
+ def __init__(self, id_map, pack):
+ self.id_map = id_map
+ self.pack = pack
+
+ def __getattr__(self, name):
+ id_value = self.id_map.get(name)
+ if not id_value:
+ raise LocalizeException('Could not find id %s in resource header' % name)
+ data = self.pack.resources.get(id_value)
+ if not data:
+ raise LocalizeException(
+ 'Could not find string with id %s (%d) in data pack' %
+ (name, id_value))
+ return decode_and_escape(data)
+
+
+def get_inputs(from_dir, locales):
+ """Returns the list of files that would be required to run the tool."""
+ inputs = []
+ for locale in locales:
+ inputs.append(os.path.join(from_dir, '%s.pak' % locale))
+ return format_quoted_list(inputs)
+
+
+def get_outputs(to_dir, locales):
+ """Returns the list of files that would be produced by the tool."""
+ outputs = []
+ for locale in locales:
+ lproj_dir = format_lproj_dir(to_dir, locale)
+ outputs.append(os.path.join(lproj_dir, LOCALIZABLE_STRINGS))
+ outputs.append(os.path.join(lproj_dir, INFOPLIST_STRINGS))
+ return format_quoted_list(outputs)
+
+
+def format_quoted_list(items):
+ """Formats a list as a string, with items space-separated and quoted."""
+ return " ".join(['"%s"' % x for x in items])
+
+
+def format_lproj_dir(to_dir, locale):
+ """Formats the name of the lproj directory for a given locale."""
+ locale = locale.replace('-', '_')
+ return os.path.join(to_dir, '%s.lproj' % locale)
+
+
+def read_resources_header(resources_header_path):
+ """Reads and parses a grit-generated resource header file.
+
+ This function will parse lines like the following:
+
+ #define IDS_PRODUCT_NAME 28531
+ #define IDS_CANCEL 28542
+
+ And return a dictionary like the following:
+
+ { 'IDS_PRODUCT_NAME': 28531, 'IDS_CANCEL': 28542 }
+ """
+ regex = re.compile(r'^#define\s+(\w+)\s+(\d+)$')
+ id_map = {}
+ try:
+ with open(resources_header_path, 'r') as f:
+ for line in f:
+ match = regex.match(line)
+ if match:
+ id_str = match.group(1)
+ id_value = int(match.group(2))
+ id_map[id_str] = id_value
+ except:
+ sys.stderr.write('Error while reading header file %s\n'
+ % resources_header_path)
+ raise
+
+ return id_map
+
+
+def read_id_list(id_list_path):
+ """Read a text file with ID names.
+
+ Names are stripped of leading and trailing spaces. Empty lines are ignored.
+ """
+ with open(id_list_path, 'r') as f:
+ stripped_lines = [x.strip() for x in f]
+ non_empty_lines = [x for x in stripped_lines if x]
+ return non_empty_lines
+
+
+def read_jinja2_template(template_path):
+ """Reads a Jinja2 template."""
+ (template_dir, template_name) = os.path.split(template_path)
+ env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir))
+ template = env.get_template(template_name)
+ return template
+
+
+def decode_and_escape(data):
+ """Decodes utf-8 data, and escapes it appropriately to use in *.strings."""
+ u_string = codecs.decode(data, 'utf-8')
+ u_string = u_string.replace('\\', '\\\\')
+ u_string = u_string.replace('"', '\\"')
+ return u_string
+
+
+def generate(from_dir, to_dir, localizable_list_path, infoplist_template_path,
+ resources_header_path, locales):
+ """Generates the <locale>.lproj directories and files."""
+
+ id_map = read_resources_header(resources_header_path)
+ localizable_ids = read_id_list(localizable_list_path)
+ infoplist_template = read_jinja2_template(infoplist_template_path)
+
+ # Generate string files for each locale
+ for locale in locales:
+ pack = data_pack.ReadDataPack(
+ os.path.join(os.path.join(from_dir, '%s.pak' % locale)))
+
+ lproj_dir = format_lproj_dir(to_dir, locale)
+ if not os.path.exists(lproj_dir):
+ os.makedirs(lproj_dir)
+
+ # Generate Localizable.strings
+ localizable_strings_path = os.path.join(lproj_dir, LOCALIZABLE_STRINGS)
+ try:
+ with codecs.open(localizable_strings_path, 'w', 'utf-16') as f:
+ for id_str in localizable_ids:
+ id_value = id_map.get(id_str)
+ if not id_value:
+ raise LocalizeException('Could not find "%s" in %s' %
+ (id_str, resources_header_path))
+
+ localized_data = pack.resources.get(id_value)
+ if not localized_data:
+ raise LocalizeException(
+ 'Could not find localized string in %s for %s (%d)' %
+ (localizable_strings_path, id_str, id_value))
+
+ f.write(u'"%s" = "%s";\n' %
+ (id_str, decode_and_escape(localized_data)))
+ except:
+ sys.stderr.write('Error while creating %s\n' % localizable_strings_path)
+ raise
+
+ # Generate InfoPlist.strings
+ infoplist_strings_path = os.path.join(lproj_dir, INFOPLIST_STRINGS)
+ try:
+ with codecs.open(infoplist_strings_path, 'w', 'utf-16') as f:
+ infoplist = infoplist_template.render(
+ ids = LocalizedStringJinja2Adapter(id_map, pack))
+ f.write(infoplist)
+ except:
+ sys.stderr.write('Error while creating %s\n' % infoplist_strings_path)
+ raise
+
+
+def DoMain(args):
+ """Entrypoint used by gyp's pymod_do_main."""
+ parser = optparse.OptionParser("usage: %prog [options] locales")
+ parser.add_option("--print-inputs", action="store_true", dest="print_input",
+ default=False,
+ help="Print the expected input file list, then exit.")
+ parser.add_option("--print-outputs", action="store_true", dest="print_output",
+ default=False,
+ help="Print the expected output file list, then exit.")
+ parser.add_option("--from-dir", action="store", dest="from_dir",
+ help="Source data pack directory.")
+ parser.add_option("--to-dir", action="store", dest="to_dir",
+ help="Destination data pack directory.")
+ parser.add_option("--localizable-list", action="store",
+ dest="localizable_list",
+ help="File with list of IDS to build Localizable.strings")
+ parser.add_option("--infoplist-template", action="store",
+ dest="infoplist_template",
+ help="File with list of IDS to build InfoPlist.strings")
+ parser.add_option("--resources-header", action="store",
+ dest="resources_header",
+ help="Auto-generated header with resource ids.")
+ options, locales = parser.parse_args(args)
+
+ if not locales:
+ parser.error('At least one locale is required.')
+
+ if options.print_input and options.print_output:
+ parser.error('Only one of --print-inputs or --print-outputs is allowed')
+
+ if options.print_input:
+ if not options.from_dir:
+ parser.error('--from-dir is required.')
+ return get_inputs(options.from_dir, locales)
+
+ if options.print_output:
+ if not options.to_dir:
+ parser.error('--to-dir is required.')
+ return get_outputs(options.to_dir, locales)
+
+ if not (options.from_dir and options.to_dir and options.localizable_list and
+ options.infoplist_template and options.resources_header):
+ parser.error('--from-dir, --to-dir, --localizable-list, ' +
+ '--infoplist-template and --resources-header are required.')
+
+ try:
+ generate(options.from_dir, options.to_dir, options.localizable_list,
+ options.infoplist_template, options.resources_header, locales)
+ except LocalizeException as e:
+ sys.stderr.write('Error: %s\n' % str(e))
+ sys.exit(1)
+
+ return ""
+
+
+def main(args):
+ print DoMain(args[1:])
+
+
+if __name__ == '__main__':
+ main(sys.argv)