rename module to something less generic.
[blender.git] / release / scripts / modules / bl_i18n_utils / check_po.py
1 #!/usr/bin/python3
2
3 # ***** BEGIN GPL LICENSE BLOCK *****
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 #
19 # ***** END GPL LICENSE BLOCK *****
20
21 # <pep8 compliant>
22
23 # Check po’s in branches (or in trunk) for missing/unneeded messages.
24
25 import os
26 import sys
27 from codecs import open
28
29 import settings
30 import utils
31
32 TRUNK_PO_DIR = settings.TRUNK_PO_DIR
33 BRANCHES_DIR = settings.BRANCHES_DIR
34
35 FILE_NAME_POT = settings.FILE_NAME_POT
36
37
38 def print_diff(ref_messages, messages, states):
39     # Remove comments from messages list!
40     messages = set(messages.keys()) - states["comm_msg"]
41     unneeded = (messages - ref_messages)
42     for msgid in unneeded:
43         print('\tUnneeded message id "{}"'.format(msgid))
44
45     missing = (ref_messages - messages)
46     for msgid in missing:
47         print('\tMissing message id "{}"'.format(msgid))
48
49     for msgid in states["comm_msg"]:
50         print('\tCommented message id "{}"'.format(msgid))
51
52     print("\t{} unneeded messages, {} missing messages, {} commented messages." \
53           "".format(len(unneeded), len(missing), len(states["comm_msg"])))
54     return 0
55
56
57 def process_po(ref_messages, po, glob_stats, do_stats, do_messages):
58     print("Checking {}...".format(po))
59     ret = 0
60
61     messages, states, stats = utils.parse_messages(po)
62     if do_messages:
63         t = print_diff(ref_messages, messages, states)
64         if t:
65             ret = t
66     if do_stats:
67         print("\tStats:")
68         t = utils.print_stats(stats, glob_stats, prefix="        ")
69         if t:
70             ret = t
71     if states["is_broken"]:
72         print("\tERROR! This .po is broken!")
73         ret = 1
74     return ret
75
76
77 def main():
78     import argparse
79     parser = argparse.ArgumentParser(description="Check po’s in branches " \
80                                                  "(or in trunk) for missing" \
81                                                  "/unneeded messages.")
82     parser.add_argument('-s', '--stats', action="store_true",
83                         help="Print po’s stats.")
84     parser.add_argument('-m', '--messages', action="store_true",
85                         help="Print po’s missing/unneeded/commented messages.")
86     parser.add_argument('-t', '--trunk', action="store_true",
87                         help="Check po’s in /trunk/po rather than /branches.")
88     parser.add_argument('-p', '--pot',
89                         help="Specify the .pot file used as reference.")
90     parser.add_argument('langs', metavar='ISO_code', nargs='*',
91                         help="Restrict processed languages to those.")
92     args = parser.parse_args()
93
94
95     if args.pot:
96         global FILE_NAME_POT
97         FILE_NAME_POT = args.pot
98     glob_stats = {"nbr"               : 0.0,
99                   "lvl"               : 0.0,
100                   "lvl_ttips"         : 0.0,
101                   "lvl_trans_ttips"   : 0.0,
102                   "lvl_ttips_in_trans": 0.0,
103                   "lvl_comm"          : 0.0,
104                   "nbr_signs"         : 0,
105                   "nbr_trans_signs"   : 0,
106                   "contexts"          : set()}
107     ret = 0
108
109     pot_messages = None
110     if args.messages:
111         pot_messages, u1, pot_stats = utils.parse_messages(FILE_NAME_POT)
112         pot_messages = set(pot_messages.keys())
113         glob_stats["nbr_signs"] = pot_stats["nbr_signs"]
114
115     if args.langs:
116         for lang in args.langs:
117             if args.trunk:
118                 po = os.path.join(TRUNK_PO_DIR, ".".join((lang, "po")))
119             else:
120                 po = os.path.join(BRANCHES_DIR, lang, ".".join((lang, "po")))
121             if os.path.exists(po):
122                 t = process_po(pot_messages, po, glob_stats,
123                                args.stats, args.messages)
124                 if t:
125                     ret = t
126     elif args.trunk:
127         for po in os.listdir(TRUNK_PO_DIR):
128             if po.endswith(".po"):
129                 po = os.path.join(TRUNK_PO_DIR, po)
130                 t = process_po(pot_messages, po, glob_stats,
131                                args.stats, args.messages)
132                 if t:
133                     ret = t
134     else:
135         for lang in os.listdir(BRANCHES_DIR):
136             for po in os.listdir(os.path.join(BRANCHES_DIR, lang)):
137                 if po.endswith(".po"):
138                     po = os.path.join(BRANCHES_DIR, lang, po)
139                     t = process_po(pot_messages, po, glob_stats,
140                                    args.stats, args.messages)
141                     if t:
142                         ret = t
143
144     if args.stats and glob_stats["nbr"] != 0.0:
145         nbr_contexts = len(glob_stats["contexts"]-{""})
146         if nbr_contexts != 1:
147             if nbr_contexts == 0:
148                 nbr_contexts = "No"
149             _ctx_txt = "s are"
150         else:
151             _ctx_txt = " is"
152         print("\nAverage stats for all {:.0f} processed files:\n" \
153               "    {:>6.1%} done!\n" \
154               "    {:>6.1%} of messages are tooltips.\n" \
155               "    {:>6.1%} of tooltips are translated.\n" \
156               "    {:>6.1%} of translated messages are tooltips.\n" \
157               "    {:>6.1%} of messages are commented.\n" \
158               "    The org msgids are currently made of {} signs.\n" \
159               "    All processed translations are currently made of {} signs.\n" \
160               "    {} specific context{} present:\n            {}\n" \
161               "".format(glob_stats["nbr"], glob_stats["lvl"]/glob_stats["nbr"],
162                         glob_stats["lvl_ttips"]/glob_stats["nbr"],
163                         glob_stats["lvl_trans_ttips"]/glob_stats["nbr"],
164                         glob_stats["lvl_ttips_in_trans"]/glob_stats["nbr"],
165                         glob_stats["lvl_comm"]/glob_stats["nbr"], glob_stats["nbr_signs"],
166                         glob_stats["nbr_trans_signs"], nbr_contexts, _ctx_txt,
167                         "\n            ".join(glob_stats["contexts"]-{""})))
168
169     return ret
170
171
172 if __name__ == "__main__":
173     print("\n\n *** Running {} *** \n".format(__file__))
174     print(" *** WARNING! Number of tooltips is only an estimation! ***\n")
175     sys.exit(main())