2121import sys
2222import tempfile
2323import warnings
24- from collections import defaultdict
24+ from collections import Counter , defaultdict
2525from configparser import RawConfigParser
2626from io import StringIO
2727from typing import Any , BinaryIO , Iterable , Literal
@@ -892,19 +892,20 @@ class ConcatenateCatalog(CommandMixin):
892892 description = 'concatenates the specified PO files into single one'
893893 user_options = [
894894 ('input-files' , None , 'input files' ),
895- ('output-file=' , 'o' , 'write output to specified file' ),
896- ('less-than=' , '<' , 'print messages with less than this many'
897- 'definitions, defaults to infinite if not set ' ),
895+ ('output-file=' , 'o' , 'write output to specified file, the results are written '
896+ 'to standard output if no output file is specified or if it is \' -\' ' ),
897+ ('less-than=' , '<' , 'print messages with less than this many '
898+ 'definitions, defaults to infinite if not set' ),
898899 ('more-than=' , '>' , 'print messages with more than this many '
899900 'definitions, defaults to 0 if not set' ),
900901 ('unique' , 'u' , 'shorthand for --less-than=2, requests '
901902 'that only unique messages be printed' ),
902903 ('use-first' , None , 'use first available translation for each '
903904 'message, don\' t merge several translations' ),
904- ('no-location' , None , 'do not write \' #: filename: line\' lines ' ),
905- ('width=' , 'w' , 'set output page width' ),
905+ ('no-location' , None , 'do not include location comments with filename and line number ' ),
906+ ('width=' , 'w' , 'set output line width (default 76) ' ),
906907 ('no-wrap' , None , 'do not break long message lines, longer than '
907- 'the output page width, into several lines' ),
908+ 'the output line width, into several lines' ),
908909 ('sort-output' , 's' , 'generate sorted output' ),
909910 ('sort-by-file' , 'F' , 'sort output by file location' ),
910911 ]
@@ -937,8 +938,6 @@ def initialize_options(self):
937938 def finalize_options (self ):
938939 if not self .input_files :
939940 raise OptionError ('you must specify the input files' )
940- if not self .output_file :
941- raise OptionError ('you must specify the output file' )
942941
943942 if self .no_wrap and self .width :
944943 raise OptionError ("'--no-wrap' and '--width' are mutually exclusive" )
@@ -953,31 +952,34 @@ def finalize_options(self):
953952 self .more_than = int (self .more_than )
954953 if self .less_than is not None :
955954 self .less_than = int (self .less_than )
955+
956956 if self .unique :
957+ if self .less_than is not None or self .more_than :
958+ raise OptionError ("'--unique' is mutually exclusive with '--less-than' and '--more-than'" )
957959 self .less_than = 2
958960
959- def _prepare (self ):
961+ def _collect_message_info (self ):
960962 templates : list [tuple [str , Catalog ]] = []
961- message_info = {}
963+ message_counts : Counter = Counter ()
964+ message_strings : dict [object , set ] = defaultdict (set )
962965
963966 for filename in self .input_files :
964967 with open (filename , 'r' ) as pofile :
965968 template = read_po (pofile )
966969 for message in template :
967- if message .id not in message_info :
968- message_info [message .id ] = {
969- 'count' : 0 ,
970- 'strings' : set (),
971- }
972- message_info [message .id ]['count' ] += 1
973- message_info [message .id ]['strings' ].add (message .string if isinstance (message .string , str ) else tuple (message .string ))
974- templates .append ((filename , template , ))
970+ if not message .id :
971+ continue
972+ message_counts [message .id ] += 1
973+ message_strings [message .id ].add (
974+ message .string if isinstance (message .string , str ) else tuple (message .string )
975+ )
976+ templates .append ((filename , template ))
975977
976- return templates , message_info
978+ return templates , message_counts , message_strings
977979
978980 def run (self ):
979981 catalog = Catalog (fuzzy = False )
980- templates , message_info = self ._prepare ()
982+ templates , message_counts , message_strings = self ._collect_message_info ()
981983
982984 for path , template in templates :
983985 if catalog .locale is None :
@@ -987,12 +989,11 @@ def run(self):
987989 if not message .id :
988990 continue
989991
990- count = message_info [message .id ]['count' ]
991- diff_string_count = len (message_info [message .id ]['strings' ])
992+ count = message_counts [message .id ]
992993 if count <= self .more_than or (self .less_than is not None and count >= self .less_than ):
993994 continue
994995
995- if count > 1 and not self .use_first and diff_string_count > 1 :
996+ if count > 1 and not self .use_first and len ( message_strings [ message . id ]) > 1 :
996997 filename = os .path .basename (path )
997998 catalog .add_conflict (message , filename , template .project , template .version )
998999 message .flags |= {'fuzzy' }
@@ -1001,15 +1002,26 @@ def run(self):
10011002
10021003 catalog .fuzzy = any (message .fuzzy for message in catalog )
10031004
1004- with open (self .output_file , 'wb' ) as outfile :
1005+ output_file = self .output_file
1006+ if not output_file or output_file == '-' :
10051007 write_po (
1006- outfile ,
1008+ sys . stdout . buffer ,
10071009 catalog ,
10081010 width = self .width ,
10091011 sort_by_file = self .sort_by_file ,
10101012 sort_output = self .sort_output ,
10111013 no_location = self .no_location ,
10121014 )
1015+ else :
1016+ with open (output_file , 'wb' ) as outfile :
1017+ write_po (
1018+ outfile ,
1019+ catalog ,
1020+ width = self .width ,
1021+ sort_by_file = self .sort_by_file ,
1022+ sort_output = self .sort_output ,
1023+ no_location = self .no_location ,
1024+ )
10131025
10141026
10151027class MergeCatalog (CommandMixin ):
0 commit comments