We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Create `i18n-messages validate` script to check for presence of fuzzy flags
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

### Describe the problem that you'd like solved
<!-- A clear and concise description of what you want to happen. -->
When `i18n-messages update` is executed, some translations in `.po` files will have a `fuzzy` annotation.  This means that the translation may not be completely accurate, and should be manually approved by a translator.  The last step of the `i18n-messages update` flow compiles all `.po` files into `.mo` files, regardless of the presence of fuzzy translations.

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->
Add validation step to i18n flow, in between the update and compile steps.  Validation step will read each `.po` file, searching for lines that begin with `#, fuzzy`.  If any are found in a `.po` file, that file should not be compiled.

<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->

### Additional context
<!-- Add any other context or screenshots about the feature request here. -->
This is a sub-task of #5134.
### Stakeholders
<!-- @ tag stakeholders of this bug -->
@cdrini



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openlibrary/i18n/__init__.py`
Content:
```
1 from __future__ import print_function
2 
3 import sys
4 from typing import List
5 
6 import web
7 import os
8 import shutil
9 
10 import babel
11 from babel._compat import BytesIO
12 from babel.support import Translations
13 from babel.messages import Catalog
14 from babel.messages.pofile import read_po, write_po
15 from babel.messages.mofile import write_mo
16 from babel.messages.extract import extract_from_file, extract_from_dir, extract_python
17 
18 root = os.path.dirname(__file__)
19 
20 def _compile_translation(po, mo):
21     try:
22         catalog = read_po(open(po, 'rb'))
23 
24         f = open(mo, 'wb')
25         write_mo(f, catalog)
26         f.close()
27         print('compiled', po, file=web.debug)
28     except Exception as e:
29         print('failed to compile', po, file=web.debug)
30         raise e
31 
32 
33 def get_locales():
34     return [
35         d
36         for d in os.listdir(root)
37         if (os.path.isdir(os.path.join(root, d)) and
38             os.path.exists(os.path.join(root, d, 'messages.po')))
39     ]
40 
41 def extract_templetor(fileobj, keywords, comment_tags, options):
42     """Extract i18n messages from web.py templates."""
43     try:
44         instring = fileobj.read().decode('utf-8')
45         # Replace/remove inline js '\$' which interferes with the Babel python parser:
46         cleaned_string = instring.replace('\$', '')
47         code = web.template.Template.generate_code(cleaned_string, fileobj.name)
48         f = BytesIO(code.encode('utf-8')) # Babel wants bytes, not strings
49     except Exception as e:
50         print('Failed to extract ' + fileobj.name + ':', repr(e), file=web.debug)
51         return []
52     return extract_python(f, keywords, comment_tags, options)
53 
54 
55 def extract_messages(dirs: List[str]):
56     catalog = Catalog(
57         project='Open Library',
58         copyright_holder='Internet Archive'
59     )
60     METHODS = [
61         ("**.py", "python"),
62         ("**.html", "openlibrary.i18n:extract_templetor")
63     ]
64     COMMENT_TAGS = ["NOTE:"]
65 
66     for d in dirs:
67         extracted = extract_from_dir(d, METHODS, comment_tags=COMMENT_TAGS,
68                                      strip_comment_tags=True)
69 
70         counts = {}
71         for filename, lineno, message, comments, context in extracted:
72             counts[filename] = counts.get(filename, 0) + 1
73             catalog.add(message, None, [(filename, lineno)], auto_comments=comments)
74 
75         for filename, count in counts.items():
76             path = filename if d == filename else os.path.join(d, filename)
77             print(f"{count}\t{path}", file=sys.stderr)
78 
79     path = os.path.join(root, 'messages.pot')
80     f = open(path, 'wb')
81     write_po(f, catalog)
82     f.close()
83 
84     print('wrote template to', path)
85 
86 def compile_translations():
87     for locale in get_locales():
88         po_path = os.path.join(root, locale, 'messages.po')
89         mo_path = os.path.join(root, locale, 'messages.mo')
90 
91         if os.path.exists(po_path):
92             _compile_translation(po_path, mo_path)
93 
94 def update_translations():
95     pot_path = os.path.join(root, 'messages.pot')
96     template = read_po(open(pot_path, 'rb'))
97 
98     for locale in get_locales():
99         po_path = os.path.join(root, locale, 'messages.po')
100         mo_path = os.path.join(root, locale, 'messages.mo')
101 
102         if os.path.exists(po_path):
103             catalog = read_po(open(po_path, 'rb'))
104             catalog.update(template)
105 
106             f = open(po_path, 'wb')
107             write_po(f, catalog)
108             f.close()
109             print('updated', po_path)
110 
111     compile_translations()
112 
113 
114 def generate_po(args):
115     if args:
116         po_dir = os.path.join(root, args[0])
117         pot_src = os.path.join(root, 'messages.pot')
118         po_dest = os.path.join(po_dir, 'messages.po')
119 
120         if os.path.exists(po_dir):
121             if os.path.exists(po_dest):
122                 print(f"Portable object file already exists at {po_dest}")
123             else:
124                 shutil.copy(pot_src, po_dest)
125                 os.chmod(po_dest, 0o666)
126                 print(f"File created at {po_dest}")
127         else:
128             os.mkdir(po_dir)
129             os.chmod(po_dir, 0o777)
130             shutil.copy(pot_src, po_dest)
131             os.chmod(po_dest, 0o666)
132             print(f"File created at {po_dest}")
133     else:
134         print("Add failed. Missing required locale code.")
135 
136 
137 @web.memoize
138 def load_translations(lang):
139     po = os.path.join(root, lang, 'messages.po')
140     mo_path = os.path.join(root, lang, 'messages.mo')
141 
142     if os.path.exists(mo_path):
143         return Translations(open(mo_path, 'rb'))
144 
145 @web.memoize
146 def load_locale(lang):
147     try:
148         return babel.Locale(lang)
149     except babel.UnknownLocaleError:
150         pass
151 
152 class GetText:
153     def __call__(self, string, *args, **kwargs):
154         """Translate a given string to the language of the current locale."""
155         # Get the website locale from the global ctx.lang variable, set in i18n_loadhook
156         translations = load_translations(web.ctx.lang)
157         value = (translations and translations.ugettext(string)) or string
158 
159         if args:
160             value = value % args
161         elif kwargs:
162             value = value % kwargs
163 
164         return value
165 
166     def __getattr__(self, key):
167         from infogami.utils.i18n import strings
168         # for backward-compatability
169         return strings.get('', key)
170 
171 class LazyGetText:
172     def __call__(self, string, *args, **kwargs):
173         """Translate a given string lazily."""
174         return LazyObject(lambda: GetText()(string, *args, **kwargs))
175 
176 class LazyObject:
177     def __init__(self, creator):
178         self._creator = creator
179 
180     def __str__(self):
181         return web.safestr(self._creator())
182 
183     def __repr__(self):
184         return repr(self._creator())
185 
186     def __add__(self, other):
187         return self._creator() + other
188 
189     def __radd__(self, other):
190         return other + self._creator()
191 
192 
193 def ungettext(s1, s2, _n, *a, **kw):
194     # Get the website locale from the global ctx.lang variable, set in i18n_loadhook
195     translations = load_translations(web.ctx.lang)
196     value = translations and translations.ungettext(s1, s2, _n)
197     if not value:
198         # fallback when translation is not provided
199         if _n == 1:
200             value = s1
201         else:
202             value = s2
203 
204     if a:
205         return value % a
206     elif kw:
207         return value % kw
208     else:
209         return value
210 
211 def gettext_territory(code):
212     """Returns the territory name in the current locale."""
213     # Get the website locale from the global ctx.lang variable, set in i18n_loadhook
214     locale = load_locale(web.ctx.lang)
215     return locale.territories.get(code, code)
216 
217 gettext = GetText()
218 ugettext = gettext
219 lgettext = LazyGetText()
220 _ = gettext
221
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/openlibrary/i18n/__init__.py b/openlibrary/i18n/__init__.py
--- a/openlibrary/i18n/__init__.py
+++ b/openlibrary/i18n/__init__.py
@@ -30,6 +30,50 @@
         raise e
 
 
+def _validate_catalog(catalog, locale):
+    validation_errors = []
+    for message in catalog:
+        if message.fuzzy:
+            if message.lineno:
+                validation_errors.append(
+                    f'openlibrary/i18n/{locale}/messages.po:{message.lineno}:'
+                    f' "{message.string}" is fuzzy.'
+                )
+            else:
+                validation_errors.append(
+                    '  File is fuzzy.  Remove line containing "#, fuzzy" found near '
+                    'the beginning of the file.'
+                )
+
+    if validation_errors:
+        print("Validation failed...")
+        print("Please correct the following errors before proceeding:")
+        for e in validation_errors:
+            print(e)
+
+    return len(validation_errors) == 0
+
+
+def validate_translations(args):
+    if args:
+        locale = args[0]
+        po_path = os.path.join(root, locale, 'messages.po')
+
+        if os.path.exists(po_path):
+            catalog = read_po(open(po_path, 'rb'))
+            is_valid = _validate_catalog(catalog, locale)
+
+            if is_valid:
+                print(f'Translations for locale "{locale}" are valid!')
+            return is_valid
+        else:
+            print(f'Portable object file for locale "{locale}" does not exist.')
+            return False
+    else:
+        print('Must include locale code when executing validate.')
+        return False
+
+
 def get_locales():
     return [
         d

{"golden_diff": "diff --git a/openlibrary/i18n/__init__.py b/openlibrary/i18n/__init__.py\n--- a/openlibrary/i18n/__init__.py\n+++ b/openlibrary/i18n/__init__.py\n@@ -30,6 +30,50 @@\n         raise e\n \n \n+def _validate_catalog(catalog, locale):\n+    validation_errors = []\n+    for message in catalog:\n+        if message.fuzzy:\n+            if message.lineno:\n+                validation_errors.append(\n+                    f'openlibrary/i18n/{locale}/messages.po:{message.lineno}:'\n+                    f' \"{message.string}\" is fuzzy.'\n+                )\n+            else:\n+                validation_errors.append(\n+                    '  File is fuzzy.  Remove line containing \"#, fuzzy\" found near '\n+                    'the beginning of the file.'\n+                )\n+\n+    if validation_errors:\n+        print(\"Validation failed...\")\n+        print(\"Please correct the following errors before proceeding:\")\n+        for e in validation_errors:\n+            print(e)\n+\n+    return len(validation_errors) == 0\n+\n+\n+def validate_translations(args):\n+    if args:\n+        locale = args[0]\n+        po_path = os.path.join(root, locale, 'messages.po')\n+\n+        if os.path.exists(po_path):\n+            catalog = read_po(open(po_path, 'rb'))\n+            is_valid = _validate_catalog(catalog, locale)\n+\n+            if is_valid:\n+                print(f'Translations for locale \"{locale}\" are valid!')\n+            return is_valid\n+        else:\n+            print(f'Portable object file for locale \"{locale}\" does not exist.')\n+            return False\n+    else:\n+        print('Must include locale code when executing validate.')\n+        return False\n+\n+\n def get_locales():\n     return [\n         d\n", "issue": "Create `i18n-messages validate` script to check for presence of fuzzy flags\n<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->\r\n\r\n### Describe the problem that you'd like solved\r\n<!-- A clear and concise description of what you want to happen. -->\r\nWhen `i18n-messages update` is executed, some translations in `.po` files will have a `fuzzy` annotation.  This means that the translation may not be completely accurate, and should be manually approved by a translator.  The last step of the `i18n-messages update` flow compiles all `.po` files into `.mo` files, regardless of the presence of fuzzy translations.\r\n\r\n### Proposal & Constraints\r\n<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->\r\nAdd validation step to i18n flow, in between the update and compile steps.  Validation step will read each `.po` file, searching for lines that begin with `#, fuzzy`.  If any are found in a `.po` file, that file should not be compiled.\r\n\r\n<!-- Which suggestions or requirements should be considered for how feature needs to appear or be implemented? -->\r\n\r\n### Additional context\r\n<!-- Add any other context or screenshots about the feature request here. -->\r\nThis is a sub-task of #5134.\r\n### Stakeholders\r\n<!-- @ tag stakeholders of this bug -->\r\n@cdrini\r\n\r\n\n", "before_files": [{"content": "from __future__ import print_function\n\nimport sys\nfrom typing import List\n\nimport web\nimport os\nimport shutil\n\nimport babel\nfrom babel._compat import BytesIO\nfrom babel.support import Translations\nfrom babel.messages import Catalog\nfrom babel.messages.pofile import read_po, write_po\nfrom babel.messages.mofile import write_mo\nfrom babel.messages.extract import extract_from_file, extract_from_dir, extract_python\n\nroot = os.path.dirname(__file__)\n\ndef _compile_translation(po, mo):\n    try:\n        catalog = read_po(open(po, 'rb'))\n\n        f = open(mo, 'wb')\n        write_mo(f, catalog)\n        f.close()\n        print('compiled', po, file=web.debug)\n    except Exception as e:\n        print('failed to compile', po, file=web.debug)\n        raise e\n\n\ndef get_locales():\n    return [\n        d\n        for d in os.listdir(root)\n        if (os.path.isdir(os.path.join(root, d)) and\n            os.path.exists(os.path.join(root, d, 'messages.po')))\n    ]\n\ndef extract_templetor(fileobj, keywords, comment_tags, options):\n    \"\"\"Extract i18n messages from web.py templates.\"\"\"\n    try:\n        instring = fileobj.read().decode('utf-8')\n        # Replace/remove inline js '\\$' which interferes with the Babel python parser:\n        cleaned_string = instring.replace('\\$', '')\n        code = web.template.Template.generate_code(cleaned_string, fileobj.name)\n        f = BytesIO(code.encode('utf-8')) # Babel wants bytes, not strings\n    except Exception as e:\n        print('Failed to extract ' + fileobj.name + ':', repr(e), file=web.debug)\n        return []\n    return extract_python(f, keywords, comment_tags, options)\n\n\ndef extract_messages(dirs: List[str]):\n    catalog = Catalog(\n        project='Open Library',\n        copyright_holder='Internet Archive'\n    )\n    METHODS = [\n        (\"**.py\", \"python\"),\n        (\"**.html\", \"openlibrary.i18n:extract_templetor\")\n    ]\n    COMMENT_TAGS = [\"NOTE:\"]\n\n    for d in dirs:\n        extracted = extract_from_dir(d, METHODS, comment_tags=COMMENT_TAGS,\n                                     strip_comment_tags=True)\n\n        counts = {}\n        for filename, lineno, message, comments, context in extracted:\n            counts[filename] = counts.get(filename, 0) + 1\n            catalog.add(message, None, [(filename, lineno)], auto_comments=comments)\n\n        for filename, count in counts.items():\n            path = filename if d == filename else os.path.join(d, filename)\n            print(f\"{count}\\t{path}\", file=sys.stderr)\n\n    path = os.path.join(root, 'messages.pot')\n    f = open(path, 'wb')\n    write_po(f, catalog)\n    f.close()\n\n    print('wrote template to', path)\n\ndef compile_translations():\n    for locale in get_locales():\n        po_path = os.path.join(root, locale, 'messages.po')\n        mo_path = os.path.join(root, locale, 'messages.mo')\n\n        if os.path.exists(po_path):\n            _compile_translation(po_path, mo_path)\n\ndef update_translations():\n    pot_path = os.path.join(root, 'messages.pot')\n    template = read_po(open(pot_path, 'rb'))\n\n    for locale in get_locales():\n        po_path = os.path.join(root, locale, 'messages.po')\n        mo_path = os.path.join(root, locale, 'messages.mo')\n\n        if os.path.exists(po_path):\n            catalog = read_po(open(po_path, 'rb'))\n            catalog.update(template)\n\n            f = open(po_path, 'wb')\n            write_po(f, catalog)\n            f.close()\n            print('updated', po_path)\n\n    compile_translations()\n\n\ndef generate_po(args):\n    if args:\n        po_dir = os.path.join(root, args[0])\n        pot_src = os.path.join(root, 'messages.pot')\n        po_dest = os.path.join(po_dir, 'messages.po')\n\n        if os.path.exists(po_dir):\n            if os.path.exists(po_dest):\n                print(f\"Portable object file already exists at {po_dest}\")\n            else:\n                shutil.copy(pot_src, po_dest)\n                os.chmod(po_dest, 0o666)\n                print(f\"File created at {po_dest}\")\n        else:\n            os.mkdir(po_dir)\n            os.chmod(po_dir, 0o777)\n            shutil.copy(pot_src, po_dest)\n            os.chmod(po_dest, 0o666)\n            print(f\"File created at {po_dest}\")\n    else:\n        print(\"Add failed. Missing required locale code.\")\n\n\n@web.memoize\ndef load_translations(lang):\n    po = os.path.join(root, lang, 'messages.po')\n    mo_path = os.path.join(root, lang, 'messages.mo')\n\n    if os.path.exists(mo_path):\n        return Translations(open(mo_path, 'rb'))\n\n@web.memoize\ndef load_locale(lang):\n    try:\n        return babel.Locale(lang)\n    except babel.UnknownLocaleError:\n        pass\n\nclass GetText:\n    def __call__(self, string, *args, **kwargs):\n        \"\"\"Translate a given string to the language of the current locale.\"\"\"\n        # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n        translations = load_translations(web.ctx.lang)\n        value = (translations and translations.ugettext(string)) or string\n\n        if args:\n            value = value % args\n        elif kwargs:\n            value = value % kwargs\n\n        return value\n\n    def __getattr__(self, key):\n        from infogami.utils.i18n import strings\n        # for backward-compatability\n        return strings.get('', key)\n\nclass LazyGetText:\n    def __call__(self, string, *args, **kwargs):\n        \"\"\"Translate a given string lazily.\"\"\"\n        return LazyObject(lambda: GetText()(string, *args, **kwargs))\n\nclass LazyObject:\n    def __init__(self, creator):\n        self._creator = creator\n\n    def __str__(self):\n        return web.safestr(self._creator())\n\n    def __repr__(self):\n        return repr(self._creator())\n\n    def __add__(self, other):\n        return self._creator() + other\n\n    def __radd__(self, other):\n        return other + self._creator()\n\n\ndef ungettext(s1, s2, _n, *a, **kw):\n    # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n    translations = load_translations(web.ctx.lang)\n    value = translations and translations.ungettext(s1, s2, _n)\n    if not value:\n        # fallback when translation is not provided\n        if _n == 1:\n            value = s1\n        else:\n            value = s2\n\n    if a:\n        return value % a\n    elif kw:\n        return value % kw\n    else:\n        return value\n\ndef gettext_territory(code):\n    \"\"\"Returns the territory name in the current locale.\"\"\"\n    # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n    locale = load_locale(web.ctx.lang)\n    return locale.territories.get(code, code)\n\ngettext = GetText()\nugettext = gettext\nlgettext = LazyGetText()\n_ = gettext\n", "path": "openlibrary/i18n/__init__.py"}], "after_files": [{"content": "from __future__ import print_function\n\nimport sys\nfrom typing import List\n\nimport web\nimport os\nimport shutil\n\nimport babel\nfrom babel._compat import BytesIO\nfrom babel.support import Translations\nfrom babel.messages import Catalog\nfrom babel.messages.pofile import read_po, write_po\nfrom babel.messages.mofile import write_mo\nfrom babel.messages.extract import extract_from_file, extract_from_dir, extract_python\n\nroot = os.path.dirname(__file__)\n\ndef _compile_translation(po, mo):\n    try:\n        catalog = read_po(open(po, 'rb'))\n\n        f = open(mo, 'wb')\n        write_mo(f, catalog)\n        f.close()\n        print('compiled', po, file=web.debug)\n    except Exception as e:\n        print('failed to compile', po, file=web.debug)\n        raise e\n\n\ndef _validate_catalog(catalog, locale):\n    validation_errors = []\n    for message in catalog:\n        if message.fuzzy:\n            if message.lineno:\n                validation_errors.append(\n                    f'openlibrary/i18n/{locale}/messages.po:{message.lineno}:'\n                    f' \"{message.string}\" is fuzzy.'\n                )\n            else:\n                validation_errors.append(\n                    '  File is fuzzy.  Remove line containing \"#, fuzzy\" found near '\n                    'the beginning of the file.'\n                )\n\n    if validation_errors:\n        print(\"Validation failed...\")\n        print(\"Please correct the following errors before proceeding:\")\n        for e in validation_errors:\n            print(e)\n\n    return len(validation_errors) == 0\n\n\ndef validate_translations(args):\n    if args:\n        locale = args[0]\n        po_path = os.path.join(root, locale, 'messages.po')\n\n        if os.path.exists(po_path):\n            catalog = read_po(open(po_path, 'rb'))\n            is_valid = _validate_catalog(catalog, locale)\n\n            if is_valid:\n                print(f'Translations for locale \"{locale}\" are valid!')\n            return is_valid\n        else:\n            print(f'Portable object file for locale \"{locale}\" does not exist.')\n            return False\n    else:\n        print('Must include locale code when executing validate.')\n        return False\n\n\ndef get_locales():\n    return [\n        d\n        for d in os.listdir(root)\n        if (os.path.isdir(os.path.join(root, d)) and\n            os.path.exists(os.path.join(root, d, 'messages.po')))\n    ]\n\ndef extract_templetor(fileobj, keywords, comment_tags, options):\n    \"\"\"Extract i18n messages from web.py templates.\"\"\"\n    try:\n        instring = fileobj.read().decode('utf-8')\n        # Replace/remove inline js '\\$' which interferes with the Babel python parser:\n        cleaned_string = instring.replace('\\$', '')\n        code = web.template.Template.generate_code(cleaned_string, fileobj.name)\n        f = BytesIO(code.encode('utf-8')) # Babel wants bytes, not strings\n    except Exception as e:\n        print('Failed to extract ' + fileobj.name + ':', repr(e), file=web.debug)\n        return []\n    return extract_python(f, keywords, comment_tags, options)\n\n\ndef extract_messages(dirs: List[str]):\n    catalog = Catalog(\n        project='Open Library',\n        copyright_holder='Internet Archive'\n    )\n    METHODS = [\n        (\"**.py\", \"python\"),\n        (\"**.html\", \"openlibrary.i18n:extract_templetor\")\n    ]\n    COMMENT_TAGS = [\"NOTE:\"]\n\n    for d in dirs:\n        extracted = extract_from_dir(d, METHODS, comment_tags=COMMENT_TAGS,\n                                     strip_comment_tags=True)\n\n        counts = {}\n        for filename, lineno, message, comments, context in extracted:\n            counts[filename] = counts.get(filename, 0) + 1\n            catalog.add(message, None, [(filename, lineno)], auto_comments=comments)\n\n        for filename, count in counts.items():\n            path = filename if d == filename else os.path.join(d, filename)\n            print(f\"{count}\\t{path}\", file=sys.stderr)\n\n    path = os.path.join(root, 'messages.pot')\n    f = open(path, 'wb')\n    write_po(f, catalog)\n    f.close()\n\n    print('wrote template to', path)\n\ndef compile_translations():\n    for locale in get_locales():\n        po_path = os.path.join(root, locale, 'messages.po')\n        mo_path = os.path.join(root, locale, 'messages.mo')\n\n        if os.path.exists(po_path):\n            _compile_translation(po_path, mo_path)\n\ndef update_translations():\n    pot_path = os.path.join(root, 'messages.pot')\n    template = read_po(open(pot_path, 'rb'))\n\n    for locale in get_locales():\n        po_path = os.path.join(root, locale, 'messages.po')\n        mo_path = os.path.join(root, locale, 'messages.mo')\n\n        if os.path.exists(po_path):\n            catalog = read_po(open(po_path, 'rb'))\n            catalog.update(template)\n\n            f = open(po_path, 'wb')\n            write_po(f, catalog)\n            f.close()\n            print('updated', po_path)\n\n    compile_translations()\n\n\ndef generate_po(args):\n    if args:\n        po_dir = os.path.join(root, args[0])\n        pot_src = os.path.join(root, 'messages.pot')\n        po_dest = os.path.join(po_dir, 'messages.po')\n\n        if os.path.exists(po_dir):\n            if os.path.exists(po_dest):\n                print(f\"Portable object file already exists at {po_dest}\")\n            else:\n                shutil.copy(pot_src, po_dest)\n                os.chmod(po_dest, 0o666)\n                print(f\"File created at {po_dest}\")\n        else:\n            os.mkdir(po_dir)\n            os.chmod(po_dir, 0o777)\n            shutil.copy(pot_src, po_dest)\n            os.chmod(po_dest, 0o666)\n            print(f\"File created at {po_dest}\")\n    else:\n        print(\"Add failed. Missing required locale code.\")\n\n\n@web.memoize\ndef load_translations(lang):\n    po = os.path.join(root, lang, 'messages.po')\n    mo_path = os.path.join(root, lang, 'messages.mo')\n\n    if os.path.exists(mo_path):\n        return Translations(open(mo_path, 'rb'))\n\n@web.memoize\ndef load_locale(lang):\n    try:\n        return babel.Locale(lang)\n    except babel.UnknownLocaleError:\n        pass\n\nclass GetText:\n    def __call__(self, string, *args, **kwargs):\n        \"\"\"Translate a given string to the language of the current locale.\"\"\"\n        # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n        translations = load_translations(web.ctx.lang)\n        value = (translations and translations.ugettext(string)) or string\n\n        if args:\n            value = value % args\n        elif kwargs:\n            value = value % kwargs\n\n        return value\n\n    def __getattr__(self, key):\n        from infogami.utils.i18n import strings\n        # for backward-compatability\n        return strings.get('', key)\n\nclass LazyGetText:\n    def __call__(self, string, *args, **kwargs):\n        \"\"\"Translate a given string lazily.\"\"\"\n        return LazyObject(lambda: GetText()(string, *args, **kwargs))\n\nclass LazyObject:\n    def __init__(self, creator):\n        self._creator = creator\n\n    def __str__(self):\n        return web.safestr(self._creator())\n\n    def __repr__(self):\n        return repr(self._creator())\n\n    def __add__(self, other):\n        return self._creator() + other\n\n    def __radd__(self, other):\n        return other + self._creator()\n\n\ndef ungettext(s1, s2, _n, *a, **kw):\n    # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n    translations = load_translations(web.ctx.lang)\n    value = translations and translations.ungettext(s1, s2, _n)\n    if not value:\n        # fallback when translation is not provided\n        if _n == 1:\n            value = s1\n        else:\n            value = s2\n\n    if a:\n        return value % a\n    elif kw:\n        return value % kw\n    else:\n        return value\n\ndef gettext_territory(code):\n    \"\"\"Returns the territory name in the current locale.\"\"\"\n    # Get the website locale from the global ctx.lang variable, set in i18n_loadhook\n    locale = load_locale(web.ctx.lang)\n    return locale.territories.get(code, code)\n\ngettext = GetText()\nugettext = gettext\nlgettext = LazyGetText()\n_ = gettext\n", "path": "openlibrary/i18n/__init__.py"}]}