diff options
author | Cosimo Cecchi <cosimoc@gnome.org> | 2015-06-04 18:46:27 -0700 |
---|---|---|
committer | Cosimo Cecchi <cosimoc@gnome.org> | 2015-06-04 18:46:27 -0700 |
commit | c4d4e3f299e6da8ecd4697f221fed574a0264dd5 (patch) | |
tree | a4575c06fe6a3c673a32bb294d0535d40f1a76a2 | |
parent | fbc49cb284067838416c6022d5c7dcb64899e030 (diff) | |
parent | 3bf53ceb9486106a6014915a5e05510d9de53f85 (diff) |
Merge pull request #3249 from endlessm/sdk/3245
eos-html-extractor
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | jasmine.json | 3 | ||||
-rw-r--r-- | m4/eos-i18n-extras.m4 | 41 | ||||
-rw-r--r-- | test/Makefile.am.inc | 6 | ||||
-rw-r--r-- | test/tools/test.html | 20 | ||||
-rw-r--r-- | test/tools/testHtmlExtractor.js | 30 | ||||
-rw-r--r-- | tools/Makefile.am.inc | 2 | ||||
-rwxr-xr-x | tools/eos-html-extractor | 75 | ||||
-rw-r--r-- | tools/eos-json-extractor/eos-json-extractor.in | 5 |
9 files changed, 180 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am index 0a918ac..24a7eff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -302,8 +302,9 @@ include $(top_srcdir)/tools/Makefile.am.inc # # # INSTALLED M4 MACROS # # # m4dir = ${datadir}/aclocal -m4_DATA = \ +dist_m4_DATA = \ m4/eos-coverage.m4 \ + m4/eos-i18n-extras.m4 \ $(NULL) # # # TESTS # # # diff --git a/jasmine.json b/jasmine.json index 9c7b40c..3c3e468 100644 --- a/jasmine.json +++ b/jasmine.json @@ -4,7 +4,8 @@ "spec_files": [ "test/endless", "test/webhelper", - "test/tools/eos-application-manifest" + "test/tools/eos-application-manifest", + "test/tools/testHtmlExtractor.js" ], "exclude": [ "test/webhelper/testTranslate.js", diff --git a/m4/eos-i18n-extras.m4 b/m4/eos-i18n-extras.m4 new file mode 100644 index 0000000..629bc73 --- /dev/null +++ b/m4/eos-i18n-extras.m4 @@ -0,0 +1,41 @@ +dnl Copyright 2013-2015 Endless Mobile, Inc. +dnl +dnl Macro to define translation rules for HTML files +dnl Usage: EOS_DEFINE_I18N_RULES (no arguments) +dnl +dnl Usage in Makefile: +dnl HTML_I18N_RULES: +dnl Just include @HTML_I18N_RULES@ and then for each file.html that has +dnl internationalizable values (tags with name="translatable"), put +dnl file.html.dummy.c in your po/POTFILES.in file. Also make sure the dummy file +dnl gets made by putting it in all-am or noinst_DATA. +dnl JSON_I18N_RULES: +dnl Same as HTML_I18N_RULES, but scans file.json for keys ending with an +dnl underscore, and puts those keys' values in file.json.dummy.c. +dnl +dnl Don't forget to add --directory=$(top_builddir) to XGETTEXT_OPTIONS in +dnl po/Makevars, since xgettext by default only looks for the files listed in +dnl POTFILES.in in the source tree, not in the build tree. + +AC_DEFUN([EOS_DEFINE_I18N_RULES], +[ + m4_pattern_allow([AM_V_GEN]) dnl Otherwise the variable is not allowed + AC_REQUIRE([AC_PROG_MKDIR_P]) + HTML_I18N_RULES=' +%.html.dummy.c: %.html + $(AM_V_GEN)$(MKDIR_P) [$](@D) && \ + eos-html-extractor $< $(top_srcdir) >[$]@ +' + + AC_SUBST([HTML_I18N_RULES]) + AM_SUBST_NOTMAKE([HTML_I18N_RULES]) + + JSON_I18N_RULES=' +%.json.dummy.c: %.json + $(AM_V_GEN)$(MKDIR_P) [$](@D) && \ + eos-json-extractor $< $(top_srcdir) >[$]@ +' + + AC_SUBST([JSON_I18N_RULES]) + AM_SUBST_NOTMAKE([JSON_I18N_RULES]) +]) diff --git a/test/Makefile.am.inc b/test/Makefile.am.inc index 842e8e8..f541a5b 100644 --- a/test/Makefile.am.inc +++ b/test/Makefile.am.inc @@ -43,6 +43,7 @@ EXTRA_DIST += \ javascript_tests = \ test/tools/eos-application-manifest/testInit.js \ + test/tools/testHtmlExtractor.js \ test/webhelper/testTranslate.js \ test/webhelper/testTranslate2.js \ test/webhelper/testWebActions.js \ @@ -51,7 +52,10 @@ javascript_tests = \ test/endless/testCustomContainer.js \ test/endless/testTopbarNavButton.js \ $(NULL) -EXTRA_DIST += $(javascript_tests) +EXTRA_DIST += \ + $(javascript_tests) \ + test/tools/test.html \ + $(NULL) # Run tests when running 'make check' TESTS = \ diff --git a/test/tools/test.html b/test/tools/test.html new file mode 100644 index 0000000..4dfda70 --- /dev/null +++ b/test/tools/test.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html dir="ltr" lang="en-US" class="no-js"> + <head> + <meta charset="utf-8"> + <title><span name="translatable">Finance Builder</span></title> + <meta name="description" content="Finance Builder"> + <meta name="author" content="Endless Mobile"> + </head> + <body> + <section id="choose" class="section"> + <!-- TRANSLATORS: This is a test of UTF-8 encoded characters --> + <div class="Intro"><span name="translatable">My Bü∂get</span></div> + <div class="subtitle"><span name="translatable">Choose a template</span></div> + <div id="mi-slider" class="mi-slider"> + <nav id="finance-nav"> + </nav> + </div> + </section> + </body> +</html> diff --git a/test/tools/testHtmlExtractor.js b/test/tools/testHtmlExtractor.js new file mode 100644 index 0000000..4f46706 --- /dev/null +++ b/test/tools/testHtmlExtractor.js @@ -0,0 +1,30 @@ +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; + +const EXPECTED_OUTPUT = '#line 5 "test/tools/test.html"\n\ +_("Finance Builder");\n\ +#line 12 "test/tools/test.html"\n\ +// TRANSLATORS: This is a test of UTF-8 encoded characters\n\ +_("My Bü∂get");\n\ +#line 13 "test/tools/test.html"\n\ +_("Choose a template");\n'; + +describe('eos-html-extractor', function () { + it('works correctly at a minimum', function () { + let srcdir = GLib.getenv('TOP_SRCDIR'); + if (!srcdir) + srcdir = '.'; + let executable = GLib.build_filenamev([srcdir, + 'tools/eos-html-extractor']); + let operand = GLib.build_filenamev([srcdir, 'test/tools/test.html']); + let process = new Gio.Subprocess({ + argv: [executable, operand, srcdir], + flags: Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_MERGE, + }); + process.init(null); + let [success, stdout] = process.communicate_utf8(null, null); + expect(process.get_if_exited()).toBeTruthy(); + expect(process.get_exit_status()).toBe(0); + expect(stdout).toEqual(EXPECTED_OUTPUT); + }); +}); diff --git a/tools/Makefile.am.inc b/tools/Makefile.am.inc index 5c53d72..8f0015c 100644 --- a/tools/Makefile.am.inc +++ b/tools/Makefile.am.inc @@ -3,6 +3,7 @@ bin_SCRIPTS = \ tools/eos-application-manifest/eos-application-manifest \ tools/eos-json-extractor/eos-json-extractor \ + tools/eos-html-extractor \ $(NULL) # Use the following script to replace $datadir inside the script, as suggested @@ -29,6 +30,7 @@ CLEANFILES += \ EXTRA_DIST += \ tools/eos-application-manifest/eos-application-manifest.in \ tools/eos-json-extractor/eos-json-extractor.in \ + tools/eos-html-extractor \ $(NULL) commandsdir = $(libexecdir)/eos-application-manifest/commands diff --git a/tools/eos-html-extractor b/tools/eos-html-extractor new file mode 100755 index 0000000..72a3acd --- /dev/null +++ b/tools/eos-html-extractor @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +# Copyright 2013-2015 Endless Mobile, Inc. + +import argparse +import os.path +import re +from bs4 import BeautifulSoup +from html.parser import HTMLParser + + +# Parser that adds line numbers to the HTML strings that need translating +class TranslatableHTMLParser(HTMLParser): + def __init__(self, translatable_strings): + super().__init__() + self.all_translatable_data = [] + self._comments_with_line_numbers = [] + self._translatable_strings = set(translatable_strings) + + def handle_data(self, data): + if data not in self._translatable_strings: + return + + code_line = self.getpos()[0] + optional_comment = None + + if self._comments_with_line_numbers: + # Determine if comment should be included + most_recent_comment = self._comments_with_line_numbers[-1] + comment_string, comment_line = most_recent_comment + + # Comment takes up at least one line by default (hence the +1) + comment_length = len(re.findall(r'\n', comment_string)) + 1 + + # If the comment immediately preceded this string, include it + if comment_line + comment_length == code_line: + optional_comment = ' '.join(comment_string.split()) + + self.all_translatable_data.append((data.strip(), code_line, optional_comment)) + + def handle_comment(self, comment): + self._comments_with_line_numbers.append((comment, self.getpos()[0])) + +parser = argparse.ArgumentParser(description='Extract translatable strings ' + + 'from HTML files. This is xgettext for HTML.') +parser.add_argument('input_file', type=str, + help='Input file to scan') +parser.add_argument('top_srcdir', type=str, nargs='?', default='.', + help='Top-level source directory (for printing correct #line directives)') +args = parser.parse_args() + +# Path from current directory to top-level app directory +html_file = args.input_file +top_dir = args.top_srcdir +final_path = os.path.relpath(html_file, top_dir) + +# Create the BeautifulSoup HTML-parsing object +with open(html_file) as f: + page = f.read() +soup = BeautifulSoup(page) + +# Extract all translatable strings from that HTML +translatable_divs = soup.find_all(attrs={'name': 'translatable'}) +translatable_strings = map(lambda div: div.contents[0], translatable_divs) + +# Find the line numbers for those strings +parser = TranslatableHTMLParser(translatable_strings) +parser.feed(page) + +# Write out all info about the translatable strings found in this file +for string, line_num, optional_comment in parser.all_translatable_data: + print('#line {line} "{path}"'.format(line=line_num, path=final_path)) + if optional_comment: + print('// ' + optional_comment) + print('_("{string}");'.format(string=string)) diff --git a/tools/eos-json-extractor/eos-json-extractor.in b/tools/eos-json-extractor/eos-json-extractor.in index fd61d4b..646fb9f 100644 --- a/tools/eos-json-extractor/eos-json-extractor.in +++ b/tools/eos-json-extractor/eos-json-extractor.in @@ -18,7 +18,8 @@ const programVersion = "@PACKAGE_VERSION@"; * Print command-line help message. */ function usage() { - print('Extracts translatable strings fron JSON configuration file.\n'); + print('Extracts translatable strings from JSON configuration file.\n' + + 'This is xgettext for JSON.\n'); print('Usage: %s [Options | <INPUT-FILE> <TOP-SRCDIR>]\n'.format( System.programInvocationName)); print('Options:'); @@ -33,7 +34,7 @@ function usage() { * Print command-line version output. */ function version() { - print('%s %s - Discover unit tests in a source tree'.format( + print('%s %s'.format( System.programInvocationName, programVersion)); System.exit(0); } |