123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265(**************************************************************************)(* ocaml-gettext: a library to translate messages *)(* *)(* Copyright (C) 2003-2008 Sylvain Le Gall <sylvain@le-gall.net> *)(* *)(* This library is free software; you can redistribute it and/or *)(* modify it under the terms of the GNU Lesser General Public *)(* License as published by the Free Software Foundation; either *)(* version 2.1 of the License, or (at your option) any later version; *)(* with the OCaml static compilation exception. *)(* *)(* This library is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *)(* Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public *)(* License along with this library; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *)(* USA *)(**************************************************************************)(**
@author Sylvain Le Gall
*)openGettextTypes(** empty_po : value representing an empty PO *)letempty_po=GettextPo_utils.empty_po(** add_po_translation_no_domain po (comment_lst,location_lst,translation) :
add a translation to a corpus of already defined translation with no domain
defined. If the translation already exist, they are merged concerning
location, and follow these rules for the translation itself :
- singular and singular : if there is an empty string ( "" ) in one
of the translation, use the other translation,
- plural and plural : if there is an empty string list ( [ "" ; "" ] ) in
one of the translaiton, use the other translation,
- singular and plural : merge into a plural form.
There is checks during the merge that can raise PoInconsistentMerge :
- for one singular string if the two plural strings differs
- if there is some elements that differs (considering the special case of
the empty string ) in the translation
*)letadd_po_translation_no_domainpopo_translation=tryGettextPo_utils.add_po_translation_no_domainpopo_translationwithPoInconsistentMerge(str1,str2)->raise(PoInconsistentMerge(str1,str2))(** add_po_translation_domain po domain (comment_lst,location_lst,translation):
add a translation to the already defined translation with the domain
defined. See add_translation_no_domain for details.
*)letadd_po_translation_domainpodomainpo_translation=tryGettextPo_utils.add_po_translation_domainpodomainpo_translationwithPoInconsistentMerge(str1,str2)->raise(PoInconsistentMerge(str1,str2))(** merge_po po1 po2 : merge two PO. The rule for merging are the same as
defined in add_po_translation_no_domain. Can raise PoInconsistentMerge
*)letmerge_popo1po2=(* We take po2 as the initial set, we merge po1 into po2 beginning with
po1.no_domain and then po1.domain *)letmerge_no_domain=MapString.fold(fun_translationpo->add_po_translation_no_domainpotranslation)po1.no_domainpo2inletmerge_one_domaindomainmap_domainpo=MapString.fold(fun_translationpo->add_po_translation_domaindomainpotranslation)map_domainpoinMapTextdomain.foldmerge_one_domainpo1.domainmerge_no_domain(** merge_pot po pot : merge a PO with a POT. Only consider strings that
exists in the pot. Always use location as defined in the POT. If a string
is not found, use the translation provided in the POT. If a plural is found
and a singular should be used, downgrade the plural to singular. If a
singular is found and a plural should be used, upgrade singular to plural,
using the strings provided in the POT for ending the translation.
*)letmerge_potpotpo=letorder_po_map?domain()=matchdomainwith|None->po.no_domain::MapTextdomain.fold(fun_xlst->x::lst)po.domain[]|Somedomain->(lettl=po.no_domain::MapTextdomain.fold(funkeyxlst->ifkey=domainthenlstelsex::lst)po.domain[]intryMapTextdomain.finddomainpo.domain::tlwithNot_found->tl)inletmerge_translationmap_lstkeycommented_translation_pot=lettranslation_pot=commented_translation_pot.po_comment_translationinlettranslation_merged=tryletcommented_translation_po=letmap_po=List.find(MapString.memkey)map_lstinMapString.findkeymap_poinlettranslation_po=commented_translation_po.po_comment_translationin(* Implementation of the rule given above *)match(translation_pot,translation_po)with|PoSingular(str_id,_),PoPlural(_,_,str::_)->PoSingular(str_id,str)|PoPlural(str_id,str_plural,_::tl),PoSingular(_,str)->PoPlural(str_id,str_plural,str::tl)|PoPlural(str_id,str_plural,[]),PoSingular(_,str)->PoPlural(str_id,str_plural,[str])|_,translation->translationwithNot_found->(* Fallback to the translation provided in the POT *)translation_potin{commented_translation_potwithpo_comment_translation=translation_merged;}in(* We begin with an empty po, and merge everything according to the rule
above. *)letmerge_no_domain=MapString.fold(funkeypot_translationpo->add_po_translation_no_domainpo(merge_translation(order_po_map())keypot_translation))pot.no_domainempty_poinletmerge_one_domaindomainmap_domainpo=MapString.fold(funkeypot_translationpo->add_po_translation_domaindomainpo(merge_translation(order_po_map~domain())keypot_translation))map_domainpoinMapTextdomain.foldmerge_one_domainpot.domainmerge_no_domainletinput_pochn=letlexbuf=Lexing.from_channelchnintryGettextPo_parser.msgfmtGettextPo_lexer.tokenlexbufwith|Parsing.Parse_error->raise(PoInvalidFile("parse error",lexbuf,chn))|Failures->raise(PoInvalidFile(s,lexbuf,chn))|PoInconsistentMerge(str1,str2)->raise(PoInconsistentMerge(str1,str2))letoutput_pochnpo=let()=set_binary_mode_outchntrueinletcomment_max_length=80inletfpfx=Printf.fprintfchnxinletescape_stringstr=letrecescape_string_auxbuffi=ifi<String.lengthstrthenlet()=matchstr.[i]with|'\n'->Buffer.add_stringbuff"\\n"|'\t'->Buffer.add_stringbuff"\\t"|'\b'->Buffer.add_stringbuff"\\b"|'\r'->Buffer.add_stringbuff"\\r"|'\012'->Buffer.add_stringbuff"\\f"|'\011'->Buffer.add_stringbuff"\\v"|'\007'->Buffer.add_stringbuff"\\a"|'"'->Buffer.add_stringbuff"\\\""|'\\'->Buffer.add_stringbuff"\\\\"|e->Buffer.add_charbuffeinescape_string_auxbuff(i+1)else()inletbuff=Buffer.create(String.lengthstr+2)inBuffer.add_charbuff'"';escape_string_auxbuff0;Buffer.add_charbuff'"';Buffer.contentsbuffinlethyphenschnlst=matchlstwith|[]->()|lst->Printf.fprintfchn"%s"(String.concat"\n"(List.mapescape_stringlst))inletcomment_linestr_hyphenstr_sepline_max_lengthtoken_lst=letstr_len=List.fold_left(funaccstr->acc+String.lengthstr)0token_lst+(List.lengthtoken_lst*String.lengthstr_sep)inletbuff=Buffer.create(str_len+(String.lengthstr_hyphen*(str_len/line_max_length)))inletreccomment_line_auxfirst_tokenline_lengthlst=matchlstwith|str::tl->letsep_length=iffirst_tokenthen0elseifString.lengthstr+line_length>line_max_lengththen(Buffer.add_charbuff'\n';Buffer.add_stringbuffstr_hyphen;Buffer.add_stringbuffstr_sep;String.lengthstr_hyphen+String.lengthstr_sep)else(Buffer.add_stringbuffstr_sep;String.lengthstr_sep)inBuffer.add_stringbuffstr;comment_line_auxfalse(sep_length+String.lengthstr+line_length)tl|[]->Buffer.contentsbuffincomment_line_auxtrue0token_lstinletoutput_po_translation_aux_commented_translation=(matchcommented_translation.po_comment_fileposwith|[]->()|lst->fpf"%s\n"(comment_line"#."" "comment_max_length("#:"::List.map(fun(str,line)->Printf.sprintf"%s:%d"strline)lst)));(matchcommented_translation.po_comment_specialwith|[]->()|lst->fpf"%s\n"(comment_line"#."" "comment_max_length("#,"::lst)));(matchcommented_translation.po_comment_translationwith|PoSingular(id,str)->fpf"msgid %a\n"hyphensid;fpf"msgstr %a\n"hyphensstr|PoPlural(id,id_plural,lst)->fpf"msgid %a\n"hyphensid;fpf"msgid_plural %a\n"hyphensid_plural;let_=List.fold_left(funis->fpf"msgstr[%i] %a\n"ihyphenss;i+1)0lstin());fpf"\n"inMapString.iteroutput_po_translation_auxpo.no_domain;MapTextdomain.iter(fundomainmap->fpf"domain %S\n\n"domain;MapString.iteroutput_po_translation_auxmap)po.domainlettranslation_of_po_translationpo_translation=matchpo_translationwith|PoSingular(id,str)->Singular(String.concat""id,String.concat""str)|PoPlural(id,id_plural,lst)->Plural(String.concat""id,String.concat""id_plural,List.map(String.concat"")lst)