2 # This file is part of the LibreOffice project.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 # This file incorporates work covered by the following license notice:
10 # Licensed to the Apache Software Foundation (ASF) under one or more
11 # contributor license agreements. See the NOTICE file distributed
12 # with this work for additional information regarding copyright
13 # ownership. The ASF licenses this file to you under the Apache
14 # License, Version 2.0 (the "License"); you may not use this file
15 # except in compliance with the License. You may obtain a copy of
16 # the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 """Translates multiple .desktop files at once with strings from .ulf
20 files; if you add new translatable .ulf files please add them to
21 l10ntools/source/localize.cxx in case the module is not already listed."""
29 def encode_desktop_string(s_value
):
30 """Function encoding strings to be used as values in .desktop files."""
31 # <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.1.html#
32 # value-types> says "The escape sequences \s, \n, \t, \r, and \\ are supported for values of
33 # type string and localestring, meaning ASCII space, newline, tab, carriage return, and
34 # backslash, respectively." <https://specifications.freedesktop.org/desktop-entry-spec/
35 # desktop-entry-spec-1.1.html#basic-format> says "A file is interpreted as a series of lines
36 # that are separated by linefeed characters", so it is apparently necessary to escape at least
37 # linefeed and backslash characters. It is unclear why that spec talks about "linefeed" in
38 # one place and about "newline" ("\n") and "carriage return" ("\r") in another, and how they are
39 # supposed to relate, so just escape any U+000A LINE FEED as "\n" and any U+000D CARRIAGE RETURN
40 # as "\r"; it is unclear exactly which occurrences of U+0020 SPACE and U+0009 CHARACTER
41 # TABULATION would need to be escaped, so they are mostly left unescaped, for readability:
42 s_value
= s_value
.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r")
43 if s_value
.startswith(" "):
44 # <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.1.html#
45 # entries> says "Space before and after the equals sign should be ignored", so escape a
46 # leading U+0020 SPACE as "\s" (while it is not clear whether "space" there means just
47 # U+0020 SPACE or any kind of white space, in which case at least a leading U+0009 CHARACTER
48 # TABULATION should similarly be escaped as "\t"; also, it is unclear whether such
49 # characters should also be escaped at the end):
50 s_value
= "\\s" + s_value
[1:]
54 parser
= argparse
.ArgumentParser()
55 parser
.add_argument("-p", dest
="productname", default
="LibreOffice")
56 parser
.add_argument("-d", dest
="workdir", default
=".")
57 parser
.add_argument("--prefix", dest
="prefix", default
="")
58 parser
.add_argument("--ext", dest
="ext")
59 parser
.add_argument("--template-dir", dest
="template_dir", default
=None)
60 parser
.add_argument("ifile")
62 o
= parser
.parse_args()
64 if o
.template_dir
is None:
65 template_dir
= f
"{o.workdir}/{o.prefix}"
67 template_dir
= o
.template_dir
72 source
= io
.open(o
.ifile
, encoding
="utf-8")
78 if line
.strip() == "":
80 # the headings in the ulf files for .desktop files are in the form [filename_Key]
82 heading
= line
.split("]", 1)[0][1:]
83 template
= heading
.split("_", 1)[0]
84 key
= heading
.split("_", 1)[1]
86 # For every section in the specified ulf file there should exist
87 # a template file in $workdir ..
88 entry
["outfile"] = f
"{template_dir}{template}.{o.ext}"
89 entry
["translations"] = {}
91 templates
[heading
] = entry
93 # split locale = "value" into 2 strings
96 locale
, value
= line
.split(" = ")
99 # replace en-US with en
100 locale
= locale
.replace("en-US", "en")
102 # use just anything inside the ""
103 assert value
[0] == '"'
104 # Some entries span multiple lines.
105 # An entry will always end on a double quote.
106 while not value
.endswith('"\n'):
107 value
+= source
.readline()
110 # replace resource placeholder
111 value
= value
.replace("%PRODUCTNAME", o
.productname
)
113 locale
= locale
.replace("-", "_")
115 templates
[heading
]["translations"][locale
] = value
121 for template
, entries
in templates
.items():
122 outfilename
= entries
["outfile"]
124 # open the template file - ignore sections for which no
127 template_file
= io
.open(outfilename
, encoding
="utf-8")
129 # string files processed one by one
133 f
"Warning: No template found for item '{template}' : '{outfilename}'\n"
138 tmpfilename
= f
"{outfilename}.tmp"
139 outfile
= io
.open(tmpfilename
, "w", encoding
="utf-8")
141 # emit the template to the output file
142 for line
in template_file
:
144 if keyline
.startswith(entries
["key"]):
145 # hack for Unity section
146 if entries
["key"] == "UnityQuickList":
149 OUTKEY
= entries
["key"]
150 keyline
= OUTKEY
+ keyline
[len(entries
["key"]) :]
151 outfile
.write(keyline
)
152 if entries
["key"] in line
:
153 translations
= entries
["translations"]
154 for locale
in sorted(translations
.keys()):
155 value
= translations
.get(locale
, None)
157 if o
.ext
in ("desktop", "str"):
158 if o
.ext
== "desktop":
159 value
= encode_desktop_string(value
)
160 outfile
.write(f
"{OUTKEY}[{locale}]={value}\n")
162 outfile
.write(f
"\t[{locale}]{OUTKEY}={value}\n")
164 template_file
.close()
167 if os
.path
.exists(outfilename
):
168 os
.unlink(outfilename
)
169 os
.rename(tmpfilename
, outfilename
)
171 if o
.ext
== "str" and processed
== 0:
172 sys
.exit("Warning: No matching templates processed")