title: Translating %% with gettext and jinja2 and pyramid
body: <p>Doing i18n work with Python using Jinja2 and Pyramid.  Seems to have a problem knowing how it should translate %%.  <strike>I'm beginning to suspect the bug is in Jinja2.</strike></p>
  
  <hr>
  
  <p>So I've done some more investigation and it appears the problem is more with gettext than with jinja2 as illustrated with the repl</p>
  
  <pre><code>&gt;&gt;&gt;gettext.gettext("98%% off %s sale") % ('holiday')
  '98% off holiday sale'
  &gt;&gt;&gt;gettext.gettext("98%% off sale")
  '98%% off sale'
  
  &gt;&gt;&gt;gettext.gettext("98% off %s sale") % ('holiday')
  Traceback (most recent call last):
    Python Shell, prompt 13, line 1
  TypeError: %o format: a number is required, not str
  </code></pre>
  
  <p>It seems to be a chicken/egg problem.  </p>
  
  <ul>
  <li>If gettext translates %% -> % then the formatter blows up on it during parameter substitution.</li>
  <li>If gettext doesn't translate %% -> % then when the formatter is not called (no params to insert) then the %% leaks out.</li>
  </ul>
  
  <p>All this means the translators (most of whom are not computer programmers) have to be very careful in how they do the translation and everyone needs to be very careful with translations that include %.</p>
  
  <p>Seems like we are doing this wrong (somehow) and there should be a more straightforward and uniform format for doing this.  Right now we are coping by simply injecting a % as a format parameter.</p>
  
  <p>Is there a better way to do this, or is this as good as it gets?</p>
  
  <hr>
  
  <p>There is a .po file at the bottom</p>
  
  <p>Unit test pretty much says it all, why is the last assertion failing?  Is this a bug with Jinja2, or do I need to be dealing with this differently.</p>
  
  <pre><code>class Jinja2Tests(TestCase):
  
      def test_percent_percent(self):
          """ i18n(gettext) expresses  98% as 98%% only in some versions of jinja2 that has not
              worked as expected.  This is to make sure that it is working. """
          env = Environment(extensions=['jinja2.ext.i18n'])
          lang = gettext.translation('messages', path.abspath(path.join(path.dirname(__file__), 'data')))
          env.install_gettext_translations(lang)
  
          template = env.from_string(source="{{ _('98%% off %(name)s sale') | format(name='holiday') }}")
          result = template.render()
          self.assertEqual('98% off holiday sale(translated)', result)
  
          template = env.from_string(source="{{ _('98%% off sale') }}")
          result = template.render()
  
          # THE LINE BELOW FAILS WITH:
          # AssertionError: '98% off sale(translated)' != u'98%% off sale(translated)'
          self.assertEqual('98% off sale(translated)', result)
  </code></pre>
  
  <p>And the MO file you have to compile to a PO file to run the above code.</p>
  
  <pre><code># This file is distributed under the same license as the Uniregistrar project.
  # FIRST AUTHOR &lt;EMAIL@ADDRESS&gt;, 2016.
  #
  msgid ""
  msgstr ""
  "Project-Id-Version: Uniregistrar 1.0\n"
  "Report-Msgid-Bugs-To: mark@uniregistry.com\n"
  "POT-Creation-Date: 2016-12-22 15:22-0500\n"
  "PO-Revision-Date: 2016-11-14 16:42-0500\n"
  "Last-Translator: FULL NAME &lt;EMAIL@ADDRESS&gt;\n"
  "Language: en\n"
  "Language-Team: en &lt;LL@li.org&gt;\n"
  "Plural-Forms: nplurals=2; plural=(n != 1)\n"
  "MIME-Version: 1.0\n"
  "Content-Type: text/plain; charset=utf-8\n"
  "Content-Transfer-Encoding: 8bit\n"
  "Generated-By: Babel 2.3.3\n"
  
  #: uniregistrar/constants.py:90
  msgid "98%% off sale"
  msgstr "98%% off sale(translated)"
  
  #: uniregistrar/constants.py:90
  msgid "98%% off %(name)s sale"
  msgstr "98%% off %(name)s sale(translated)"
  </code></pre>