def test_header_entry(self): buf = StringIO(r'''\ # SOME DESCRIPTIVE TITLE. # Copyright (C) 2007 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, 2007. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 3.15\n" "Report-Msgid-Bugs-To: Fliegender Zirkus <*****@*****.**>\n" "POT-Creation-Date: 2007-09-27 11:19+0700\n" "PO-Revision-Date: 2007-09-27 21:42-0700\n" "Last-Translator: John <*****@*****.**>\n" "Language-Team: German Lang <*****@*****.**>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-2\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev-r313\n" ''') catalog = pofile.read_po(buf) self.assertEqual(1, len(list(catalog))) self.assertEqual(u'3.15', catalog.version) self.assertEqual(u'Fliegender Zirkus <*****@*****.**>', catalog.msgid_bugs_address) self.assertEqual(datetime(2007, 9, 27, 11, 19, tzinfo=FixedOffsetTimezone(7 * 60)), catalog.creation_date) self.assertEqual(u'John <*****@*****.**>', catalog.last_translator) self.assertEqual(u'German Lang <*****@*****.**>', catalog.language_team) self.assertEqual(u'iso-8859-2', catalog.charset) self.assertEqual(True, list(catalog)[0].fuzzy)
def test_with_context(self): buf = BytesIO(b'''# Some string in the menu #: main.py:1 msgctxt "Menu" msgid "foo" msgstr "Voh" # Another string in the menu #: main.py:2 msgctxt "Menu" msgid "bar" msgstr "Bahr" ''') catalog = pofile.read_po(buf, ignore_obsolete=True) self.assertEqual(2, len(catalog)) message = catalog.get('foo', context='Menu') self.assertEqual('Menu', message.context) message = catalog.get('bar', context='Menu') self.assertEqual('Menu', message.context) # And verify it pass through write_po out_buf = BytesIO() pofile.write_po(out_buf, catalog, omit_header=True) assert out_buf.getvalue().strip() == buf.getvalue().strip(), \ out_buf.getvalue()
def run(self): po_files = [] mo_files = [] if not self.input_file: if self.locale: po_files.append( (self.locale, os.path.join(self.directory, self.locale, "LC_MESSAGES", self.domain + ".po")) ) mo_files.append(os.path.join(self.directory, self.locale, "LC_MESSAGES", self.domain + ".mo")) else: for locale in os.listdir(self.directory): po_file = os.path.join(self.directory, locale, "LC_MESSAGES", self.domain + ".po") if os.path.exists(po_file): po_files.append((locale, po_file)) mo_files.append(os.path.join(self.directory, locale, "LC_MESSAGES", self.domain + ".mo")) else: po_files.append((self.locale, self.input_file)) if self.output_file: mo_files.append(self.output_file) else: mo_files.append(os.path.join(self.directory, self.locale, "LC_MESSAGES", self.domain + ".mo")) if not po_files: raise DistutilsOptionError("no message catalogs found") for idx, (locale, po_file) in enumerate(po_files): mo_file = mo_files[idx] infile = open(po_file, "rb") try: catalog = read_po(infile, locale) finally: infile.close() if self.statistics: translated = 0 for message in list(catalog)[1:]: if message.string: translated += 1 percentage = 0 if len(catalog): percentage = translated * 100 // len(catalog) log.info("%d of %d messages (%d%%) translated in %r", translated, len(catalog), percentage, po_file) if catalog.fuzzy and not self.use_fuzzy: log.warn("catalog %r is marked as fuzzy, skipping", po_file) continue for message, errors in catalog.check(): for error in errors: log.error("error: %s:%d: %s", po_file, message.lineno, error) log.info("compiling catalog %r to %r", po_file, mo_file) outfile = open(mo_file, "wb") try: write_mo(outfile, catalog, use_fuzzy=self.use_fuzzy) finally: outfile.close()
def test_single_plural_form(self): buf = StringIO(r'''msgid "foo" msgid_plural "foos" msgstr[0] "Voh"''') catalog = pofile.read_po(buf, locale='ja_JP') self.assertEqual(1, len(catalog)) self.assertEqual(1, catalog.num_plurals) message = catalog['foo'] self.assertEqual(1, len(message.string))
def test_plural_with_square_brackets(self): buf = StringIO(r'''msgid "foo" msgid_plural "foos" msgstr[0] "Voh [text]" msgstr[1] "Vohs [text]"''') catalog = pofile.read_po(buf, locale='nb_NO') self.assertEqual(1, len(catalog)) self.assertEqual(2, catalog.num_plurals) message = catalog['foo'] self.assertEqual(2, len(message.string))
def test_1_num_plurals_checkers(self): for _locale in [p for p in PLURALS if PLURALS[p][0] == 1]: try: locale = Locale.parse(_locale) except UnknownLocaleError: # Just an alias? Not what we're testing here, let's continue continue po_file = (u"""\ # %(english_name)s translations for TestProject. # Copyright (C) 2007 FooBar, Inc. # This file is distributed under the same license as the TestProject # project. # FIRST AUTHOR <EMAIL@ADDRESS>, 2007. # msgid "" msgstr "" "Project-Id-Version: TestProject 0.1\\n" "Report-Msgid-Bugs-To: [email protected]\\n" "POT-Creation-Date: 2007-04-01 15:30+0200\\n" "PO-Revision-Date: %(date)s\\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" "Language-Team: %(locale)s <*****@*****.**>\n" "Plural-Forms: nplurals=%(num_plurals)s; plural=%(plural_expr)s\\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=utf-8\\n" "Content-Transfer-Encoding: 8bit\\n" "Generated-By: Babel %(version)s\\n" #. This will be a translator comment, #. that will include several lines #: project/file1.py:8 msgid "bar" msgstr "" #: project/file2.py:9 msgid "foobar" msgid_plural "foobars" msgstr[0] "" """ % dict(locale = _locale, english_name = locale.english_name, version = VERSION, year = time.strftime('%Y'), date = format_datetime(datetime.now(LOCALTZ), 'yyyy-MM-dd HH:mmZ', tzinfo=LOCALTZ, locale=_locale), num_plurals = PLURALS[_locale][0], plural_expr = PLURALS[_locale][0])).encode('utf-8') # This test will fail for revisions <= 406 because so far # catalog.num_plurals was neglected catalog = read_po(BytesIO(po_file), _locale) message = catalog['foobar'] checkers.num_plurals(catalog, message)
def test_input_dirs_is_treated_as_list(self): self.cmd.input_dirs = self.datadir self.cmd.output_file = self._pot_file() self.cmd.finalize_options() self.cmd.run() with open(self._pot_file(), 'U') as f: catalog = read_po(f) msg = catalog.get('bar') self.assertEqual(1, len(msg.locations)) self.assertTrue('file1.py' in msg.locations[0][0])
def test_more_than_two_plural_forms(self): buf = StringIO(r'''msgid "foo" msgid_plural "foos" msgstr[0] "Voh" msgstr[1] "Vohs" msgstr[2] "Vohss"''') catalog = pofile.read_po(buf, locale='lv_LV') self.assertEqual(1, len(catalog)) self.assertEqual(3, catalog.num_plurals) message = catalog['foo'] self.assertEqual(3, len(message.string)) self.assertEqual(u'Vohss', message.string[2])
def test_not_fuzzy_header(self): buf = StringIO(r'''\ # Translations template for AReallyReallyLongNameForAProject. # Copyright (C) 2007 ORGANIZATION # This file is distributed under the same license as the # AReallyReallyLongNameForAProject project. # FIRST AUTHOR <EMAIL@ADDRESS>, 2007. # ''') catalog = pofile.read_po(buf) self.assertEqual(1, len(list(catalog))) self.assertEqual(False, list(catalog)[0].fuzzy)
def test_silent_location_fallback(self): buf = BytesIO(b'''\ #: broken_file.py msgid "missing line number" msgstr "" #: broken_file.py:broken_line_number msgid "broken line number" msgstr ""''') catalog = pofile.read_po(buf) self.assertEqual(catalog['missing line number'].locations, []) self.assertEqual(catalog['broken line number'].locations, [])
def test_read_multiline(self): buf = StringIO(r'''msgid "" "Here's some text that\n" "includesareallylongwordthatmightbutshouldnt" " throw us into an infinite " "loop\n" msgstr ""''') catalog = pofile.read_po(buf) self.assertEqual(1, len(catalog)) message = list(catalog)[1] self.assertEqual("Here's some text that\nincludesareallylongwordthat" "mightbutshouldnt throw us into an infinite loop\n", message.id)
def test_obsolete_message_ignored(self): buf = StringIO(r'''# This is an obsolete message #~ msgid "foo" #~ msgstr "Voh" # This message is not obsolete #: main.py:1 msgid "bar" msgstr "Bahr" ''') catalog = pofile.read_po(buf, ignore_obsolete=True) self.assertEqual(1, len(catalog)) self.assertEqual(0, len(catalog.obsolete))
def test_obsolete_message(self): buf = StringIO(r'''# This is an obsolete message #~ msgid "foo" #~ msgstr "Voh" # This message is not obsolete #: main.py:1 msgid "bar" msgstr "Bahr" ''') catalog = pofile.read_po(buf) self.assertEqual(1, len(catalog)) self.assertEqual(1, len(catalog.obsolete)) message = catalog.obsolete[u'foo'] self.assertEqual(u'foo', message.id) self.assertEqual(u'Voh', message.string) self.assertEqual(['This is an obsolete message'], message.user_comments)
def test_applies_specified_encoding_during_read(self): buf = BytesIO(u''' msgid "" msgstr "" "Project-Id-Version: 3.15\\n" "Report-Msgid-Bugs-To: Fliegender Zirkus <*****@*****.**>\\n" "POT-Creation-Date: 2007-09-27 11:19+0700\\n" "PO-Revision-Date: 2007-09-27 21:42-0700\\n" "Last-Translator: John <*****@*****.**>\\n" "Language-Team: German Lang <*****@*****.**>\\n" "Plural-Forms: nplurals=2; plural=(n != 1)\\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=iso-8859-1\\n" "Content-Transfer-Encoding: 8bit\\n" "Generated-By: Babel 1.0dev-r313\\n" msgid "foo" msgstr "bär"'''.encode('iso-8859-1')) catalog = pofile.read_po(buf, locale='de_DE') self.assertEqual(u'bär', catalog.get('foo').string)
def run(self): log.info("creating catalog %r based on %r", self.output_file, self.input_file) infile = open(self.input_file, "rb") try: # Although reading from the catalog template, read_po must be fed # the locale in order to correctly calculate plurals catalog = read_po(infile, locale=self.locale) finally: infile.close() catalog.locale = self._locale catalog.revision_date = datetime.now(LOCALTZ) catalog.fuzzy = False outfile = open(self.output_file, "wb") try: write_po(outfile, catalog, width=self.width) finally: outfile.close()
def update(self, argv): """Subcommand for updating existing message catalogs from a template. :param argv: the command arguments :since: version 0.9 """ parser = OptionParser(usage=self.usage % ("update", ""), description=self.commands["update"]) parser.add_option("--domain", "-D", dest="domain", help="domain of PO file (default '%default')") parser.add_option("--input-file", "-i", dest="input_file", metavar="FILE", help="name of the input file") parser.add_option("--output-dir", "-d", dest="output_dir", metavar="DIR", help="path to output directory") parser.add_option( "--output-file", "-o", dest="output_file", metavar="FILE", help="name of the output file (default " "'<output_dir>/<locale>/LC_MESSAGES/" "<domain>.po')", ) parser.add_option("--locale", "-l", dest="locale", metavar="LOCALE", help="locale of the translations catalog") parser.add_option("-w", "--width", dest="width", type="int", help="set output line width (default 76)") parser.add_option( "--no-wrap", dest="no_wrap", action="store_true", help="do not break long message lines, longer than " "the output line width, into several lines", ) parser.add_option( "--ignore-obsolete", dest="ignore_obsolete", action="store_true", help="do not include obsolete messages in the output " "(default %default)", ) parser.add_option( "--no-fuzzy-matching", "-N", dest="no_fuzzy_matching", action="store_true", help="do not use fuzzy matching (default %default)", ) parser.add_option( "--previous", dest="previous", action="store_true", help="keep previous msgids of translated messages " "(default %default)", ) parser.set_defaults(domain="messages", ignore_obsolete=False, no_fuzzy_matching=False, previous=False) options, args = parser.parse_args(argv) if not options.input_file: parser.error("you must specify the input file") if not options.output_file and not options.output_dir: parser.error("you must specify the output file or directory") if options.output_file and not options.locale: parser.error("you must specify the locale") if options.no_fuzzy_matching and options.previous: options.previous = False po_files = [] if not options.output_file: if options.locale: po_files.append( ( options.locale, os.path.join(options.output_dir, options.locale, "LC_MESSAGES", options.domain + ".po"), ) ) else: for locale in os.listdir(options.output_dir): po_file = os.path.join(options.output_dir, locale, "LC_MESSAGES", options.domain + ".po") if os.path.exists(po_file): po_files.append((locale, po_file)) else: po_files.append((options.locale, options.output_file)) domain = options.domain if not domain: domain = os.path.splitext(os.path.basename(options.input_file))[0] infile = open(options.input_file, "U") try: template = read_po(infile) finally: infile.close() if not po_files: parser.error("no message catalogs found") if options.width and options.no_wrap: parser.error("'--no-wrap' and '--width' are mutually exclusive.") elif not options.width and not options.no_wrap: options.width = 76 for locale, filename in po_files: self.log.info("updating catalog %r based on %r", filename, options.input_file) infile = open(filename, "rb") try: catalog = read_po(infile, locale=locale, domain=domain) finally: infile.close() catalog.update(template, options.no_fuzzy_matching) tmpname = os.path.join(os.path.dirname(filename), tempfile.gettempprefix() + os.path.basename(filename)) tmpfile = open(tmpname, "wb") try: try: write_po( tmpfile, catalog, ignore_obsolete=options.ignore_obsolete, include_previous=options.previous, width=options.width, ) finally: tmpfile.close() except: os.remove(tmpname) raise try: os.rename(tmpname, filename) except OSError: # We're probably on Windows, which doesn't support atomic # renames, at least not through Python # If the error is in fact due to a permissions problem, that # same error is going to be raised from one of the following # operations os.remove(filename) shutil.copy(tmpname, filename) os.remove(tmpname)
def run(self): po_files = [] if not self.output_file: if self.locale: po_files.append( (self.locale, os.path.join(self.output_dir, self.locale, "LC_MESSAGES", self.domain + ".po")) ) else: for locale in os.listdir(self.output_dir): po_file = os.path.join(self.output_dir, locale, "LC_MESSAGES", self.domain + ".po") if os.path.exists(po_file): po_files.append((locale, po_file)) else: po_files.append((self.locale, self.output_file)) domain = self.domain if not domain: domain = os.path.splitext(os.path.basename(self.input_file))[0] infile = open(self.input_file, "rb") try: template = read_po(infile) finally: infile.close() if not po_files: raise DistutilsOptionError("no message catalogs found") for locale, filename in po_files: log.info("updating catalog %r based on %r", filename, self.input_file) infile = open(filename, "rb") try: catalog = read_po(infile, locale=locale, domain=domain) finally: infile.close() catalog.update(template, self.no_fuzzy_matching) tmpname = os.path.join(os.path.dirname(filename), tempfile.gettempprefix() + os.path.basename(filename)) tmpfile = open(tmpname, "wb") try: try: write_po( tmpfile, catalog, ignore_obsolete=self.ignore_obsolete, include_previous=self.previous, width=self.width, ) finally: tmpfile.close() except: os.remove(tmpname) raise try: os.rename(tmpname, filename) except OSError: # We're probably on Windows, which doesn't support atomic # renames, at least not through Python # If the error is in fact due to a permissions problem, that # same error is going to be raised from one of the following # operations os.remove(filename) shutil.copy(tmpname, filename) os.remove(tmpname)
def compile(self, argv): """Subcommand for compiling a message catalog to a MO file. :param argv: the command arguments :since: version 0.9 """ parser = OptionParser(usage=self.usage % ("compile", ""), description=self.commands["compile"]) parser.add_option("--domain", "-D", dest="domain", help="domain of MO and PO files (default '%default')") parser.add_option("--directory", "-d", dest="directory", metavar="DIR", help="base directory of catalog files") parser.add_option("--locale", "-l", dest="locale", metavar="LOCALE", help="locale of the catalog") parser.add_option("--input-file", "-i", dest="input_file", metavar="FILE", help="name of the input file") parser.add_option( "--output-file", "-o", dest="output_file", metavar="FILE", help="name of the output file (default " "'<output_dir>/<locale>/LC_MESSAGES/" "<domain>.mo')", ) parser.add_option( "--use-fuzzy", "-f", dest="use_fuzzy", action="store_true", help="also include fuzzy translations (default " "%default)", ) parser.add_option( "--statistics", dest="statistics", action="store_true", help="print statistics about translations" ) parser.set_defaults(domain="messages", use_fuzzy=False, compile_all=False, statistics=False) options, args = parser.parse_args(argv) po_files = [] mo_files = [] if not options.input_file: if not options.directory: parser.error("you must specify either the input file or the " "base directory") if options.locale: po_files.append( ( options.locale, os.path.join(options.directory, options.locale, "LC_MESSAGES", options.domain + ".po"), ) ) mo_files.append(os.path.join(options.directory, options.locale, "LC_MESSAGES", options.domain + ".mo")) else: for locale in os.listdir(options.directory): po_file = os.path.join(options.directory, locale, "LC_MESSAGES", options.domain + ".po") if os.path.exists(po_file): po_files.append((locale, po_file)) mo_files.append(os.path.join(options.directory, locale, "LC_MESSAGES", options.domain + ".mo")) else: po_files.append((options.locale, options.input_file)) if options.output_file: mo_files.append(options.output_file) else: if not options.directory: parser.error("you must specify either the output file or " "the base directory") mo_files.append(os.path.join(options.directory, options.locale, "LC_MESSAGES", options.domain + ".mo")) if not po_files: parser.error("no message catalogs found") for idx, (locale, po_file) in enumerate(po_files): mo_file = mo_files[idx] infile = open(po_file, "rb") try: catalog = read_po(infile, locale) finally: infile.close() if options.statistics: translated = 0 for message in list(catalog)[1:]: if message.string: translated += 1 percentage = 0 if len(catalog): percentage = translated * 100 // len(catalog) self.log.info( "%d of %d messages (%d%%) translated in %r", translated, len(catalog), percentage, po_file ) if catalog.fuzzy and not options.use_fuzzy: self.log.warning("catalog %r is marked as fuzzy, skipping", po_file) continue for message, errors in catalog.check(): for error in errors: self.log.error("error: %s:%d: %s", po_file, message.lineno, error) self.log.info("compiling catalog %r to %r", po_file, mo_file) outfile = open(mo_file, "wb") try: write_mo(outfile, catalog, use_fuzzy=options.use_fuzzy) finally: outfile.close()
def test_preserve_domain(self): buf = StringIO(r'''msgid "foo" msgstr "Voh"''') catalog = pofile.read_po(buf, domain='mydomain') self.assertEqual('mydomain', catalog.domain)
def test_preserve_locale(self): buf = StringIO(r'''msgid "foo" msgstr "Voh"''') catalog = pofile.read_po(buf, locale='en_US') self.assertEqual(Locale('en', 'US'), catalog.locale)
def init(self, argv): """Subcommand for creating new message catalogs from a template. :param argv: the command arguments """ parser = OptionParser(usage=self.usage % ("init", ""), description=self.commands["init"]) parser.add_option("--domain", "-D", dest="domain", help="domain of PO file (default '%default')") parser.add_option("--input-file", "-i", dest="input_file", metavar="FILE", help="name of the input file") parser.add_option("--output-dir", "-d", dest="output_dir", metavar="DIR", help="path to output directory") parser.add_option( "--output-file", "-o", dest="output_file", metavar="FILE", help="name of the output file (default " "'<output_dir>/<locale>/LC_MESSAGES/" "<domain>.po')", ) parser.add_option( "--locale", "-l", dest="locale", metavar="LOCALE", help="locale for the new localized catalog" ) parser.add_option("-w", "--width", dest="width", type="int", help="set output line width (default 76)") parser.add_option( "--no-wrap", dest="no_wrap", action="store_true", help="do not break long message lines, longer than " "the output line width, into several lines", ) parser.set_defaults(domain="messages") options, args = parser.parse_args(argv) if not options.locale: parser.error("you must provide a locale for the new catalog") try: locale = Locale.parse(options.locale) except UnknownLocaleError as e: parser.error(e) if not options.input_file: parser.error("you must specify the input file") if not options.output_file and not options.output_dir: parser.error("you must specify the output file or directory") if not options.output_file: options.output_file = os.path.join( options.output_dir, options.locale, "LC_MESSAGES", options.domain + ".po" ) if not os.path.exists(os.path.dirname(options.output_file)): os.makedirs(os.path.dirname(options.output_file)) if options.width and options.no_wrap: parser.error("'--no-wrap' and '--width' are mutually exclusive.") elif not options.width and not options.no_wrap: options.width = 76 infile = open(options.input_file, "r") try: # Although reading from the catalog template, read_po must be fed # the locale in order to correctly calculate plurals catalog = read_po(infile, locale=options.locale) finally: infile.close() catalog.locale = locale catalog.revision_date = datetime.now(LOCALTZ) self.log.info("creating catalog %r based on %r", options.output_file, options.input_file) outfile = open(options.output_file, "wb") try: write_po(outfile, catalog, width=options.width) finally: outfile.close()