nss: upgrade to release 3.73
[LibreOffice.git] / wizards / source / scriptforge / SF_L10N.xba
blobfcb87ef1471b95d0c97957cae10e41210f37e0e7
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
3 <script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_L10N" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
4 REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
5 REM === Full documentation is available on https://help.libreoffice.org/ ===
6 REM =======================================================================================================================
8 Option Compatible
9 Option ClassModule
10 &apos;Option Private Module
12 Option Explicit
14 &apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
15 &apos;&apos;&apos; L10N (aka SF_L10N)
16 &apos;&apos;&apos; ====
17 &apos;&apos;&apos; Implementation of a Basic class for providing a number of services
18 &apos;&apos;&apos; related to the translation of user interfaces into a huge number of languages
19 &apos;&apos;&apos; with a minimal impact on the program code itself
20 &apos;&apos;&apos;
21 &apos;&apos;&apos; The design choices of this module are based on so-called PO-files
22 &apos;&apos;&apos; PO-files (portable object files) have long been promoted in the free software industry
23 &apos;&apos;&apos; as a mean of providing multilingual UIs. This is accomplished through the use of human-readable
24 &apos;&apos;&apos; text files with a well defined structure that specifies, for any given language,
25 &apos;&apos;&apos; the source language string and the localized string
26 &apos;&apos;&apos;
27 &apos;&apos;&apos; To read more about the PO format and its ecosystem of associated toolsets:
28 &apos;&apos;&apos; https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#PO-Files
29 &apos;&apos;&apos; and, IMHO, a very good tutorial:
30 &apos;&apos;&apos; http://pology.nedohodnik.net/doc/user/en_US/ch-about.html
31 &apos;&apos;&apos;
32 &apos;&apos;&apos; The main advantage of the PO format is the complete dissociation between the two
33 &apos;&apos;&apos; very different profiles, i.e. the programmer and the translator(s).
34 &apos;&apos;&apos; Being independent text files, one per language to support, the programmer may give away
35 &apos;&apos;&apos; pristine PO template files (known as POT-files) for a translator to process.
36 &apos;&apos;&apos;
37 &apos;&apos;&apos; This class implements mainly 3 mechanisms:
38 &apos;&apos;&apos; - AddText: for the programmer to build a set of words or sentences
39 &apos;&apos;&apos; meant for being translated later
40 &apos;&apos;&apos; - ExportToPOTFile: All the above texts are exported into a pristine POT-file
41 &apos;&apos;&apos; - GetText: At runtime get the text in the user language
42 &apos;&apos;&apos; Note that the first two are optional: POT and PO-files may be built with a simple text editor
43 &apos;&apos;&apos;
44 &apos;&apos;&apos; Several instances of the L10N class may coexist
45 &apos; The constraint however is that each instance should find its PO-files
46 &apos;&apos;&apos; in a separate directory
47 &apos;&apos;&apos; PO-files must be named with the targeted locale: f.i. &quot;en-US.po&quot; or &quot;fr-BE.po&quot;
48 &apos;&apos;&apos;
49 &apos;&apos;&apos; Service invocation syntax
50 &apos;&apos;&apos; CreateScriptService(&quot;L10N&quot;[, FolderName[, Locale]])
51 &apos;&apos;&apos; FolderName: the folder containing the PO-files (in SF_FileSystem.FileNaming notation)
52 &apos;&apos;&apos; Locale: in the form la-CO (language-COUNTRY)
53 &apos;&apos;&apos; Service invocation examples:
54 &apos;&apos;&apos; Dim myPO As Variant
55 &apos;&apos;&apos; myPO = CreateScriptService(&quot;L10N&quot;) &apos; AddText and ExportToPOTFile are allowed
56 &apos;&apos;&apos; myPO = CreateScriptService(&quot;L10N&quot;, &quot;C:\myPOFiles\&quot;, &quot;fr-BE&quot;)
57 &apos;&apos;&apos; &apos;All functionalities are available
58 &apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
60 REM =============================================================== PRIVATE TYPES
62 &apos;&apos;&apos; The recognized elements of an entry in a PO file are (other elements are ignored) :
63 &apos;&apos;&apos; #. Extracted comments (given by the programmer to the translator)
64 &apos;&apos;&apos; #, flag (the kde-format flag when the string contains tokens)
65 &apos;&apos;&apos; msgctxt Context (to store an acronym associated with the message, this is a distortion of the norm)
66 &apos;&apos;&apos; msgid untranslated-string
67 &apos;&apos;&apos; msgstr translated-string
68 &apos;&apos;&apos; NB: plural forms are not supported
70 Type POEntry
71 Comment As String
72 Flag As String
73 Context As String
74 MsgId As String
75 MsgStr As String
76 End Type
78 REM ================================================================== EXCEPTIONS
80 Const DUPLICATEKEYERROR = &quot;DUPLICATEKEYERROR&quot;
82 REM ============================================================= PRIVATE MEMBERS
84 Private [Me] As Object
85 Private [_Parent] As Object
86 Private ObjectType As String &apos; Must be &quot;L10N&quot;
87 Private ServiceName As String
88 Private _POFolder As String &apos; PO files container
89 Private _Locale As String &apos; la-CO
90 Private _POFile As String &apos; PO file in URL format
91 Private _Encoding As String &apos; Used to open the PO file, default = UTF-8
92 Private _Dictionary As Object &apos; SF_Dictionary
94 REM ===================================================== CONSTRUCTOR/DESTRUCTOR
96 REM -----------------------------------------------------------------------------
97 Private Sub Class_Initialize()
98 Set [Me] = Nothing
99 Set [_Parent] = Nothing
100 ObjectType = &quot;L10N&quot;
101 ServiceName = &quot;ScriptForge.L10N&quot;
102 _POFolder = &quot;&quot;
103 _Locale = &quot;&quot;
104 _POFile = &quot;&quot;
105 Set _Dictionary = Nothing
106 End Sub &apos; ScriptForge.SF_L10N Constructor
108 REM -----------------------------------------------------------------------------
109 Private Sub Class_Terminate()
111 If Not IsNull(_Dictionary) Then Set _Dictionary = _Dictionary.Dispose()
112 Call Class_Initialize()
113 End Sub &apos; ScriptForge.SF_L10N Destructor
115 REM -----------------------------------------------------------------------------
116 Public Function Dispose() As Variant
117 Call Class_Terminate()
118 Set Dispose = Nothing
119 End Function &apos; ScriptForge.SF_L10N Explicit Destructor
121 REM ================================================================== PROPERTIES
123 REM -----------------------------------------------------------------------------
124 Property Get Folder() As String
125 &apos;&apos;&apos; Returns the FolderName containing the PO-files expressed as given by the current FileNaming
126 &apos;&apos;&apos; property of the SF_FileSystem service. Default = URL format
127 &apos;&apos;&apos; May be empty
128 &apos;&apos;&apos; Example:
129 &apos;&apos;&apos; myPO.Folder
131 Folder = _PropertyGet(&quot;Folder&quot;)
133 End Property &apos; ScriptForge.SF_L10N.Folder
135 REM -----------------------------------------------------------------------------
136 Property Get Languages() As Variant
137 &apos;&apos;&apos; Returns a zero-based array listing all the BaseNames of the PO-files found in Folder,
138 &apos;&apos;&apos; Example:
139 &apos;&apos;&apos; myPO.Languages
141 Languages = _PropertyGet(&quot;Languages&quot;)
143 End Property &apos; ScriptForge.SF_L10N.Languages
145 REM -----------------------------------------------------------------------------
146 Property Get Locale() As String
147 &apos;&apos;&apos; Returns the currently active language-COUNTRY combination. May be empty
148 &apos;&apos;&apos; Example:
149 &apos;&apos;&apos; myPO.Locale
151 Locale = _PropertyGet(&quot;Locale&quot;)
153 End Property &apos; ScriptForge.SF_L10N.Locale
155 REM ===================================================================== METHODS
157 REM -----------------------------------------------------------------------------
158 Public Function AddText(Optional ByVal Context As Variant _
159 , Optional ByVal MsgId As Variant _
160 , Optional ByVal Comment As Variant _
161 , Optional ByVal MsgStr As Variant _
162 ) As Boolean
163 &apos;&apos;&apos; Add a new entry in the list of localizable text strings
164 &apos;&apos;&apos; Args:
165 &apos;&apos;&apos; Context: when not empty, the key to retrieve the translated string via GetText. Default = &quot;&quot;
166 &apos;&apos;&apos; MsgId: the untranslated string, i.e. the text appearing in the program code. Must not be empty
167 &apos;&apos;&apos; The key to retrieve the translated string via GetText when Context is empty
168 &apos;&apos;&apos; May contain placeholders (%1 ... %9) for dynamic arguments to be inserted in the text at run-time
169 &apos;&apos;&apos; If the string spans multiple lines, insert escape sequences (\n) where relevant
170 &apos;&apos;&apos; Comment: the so-called &quot;extracted-comments&quot; intended to inform/help translators
171 &apos;&apos;&apos; If the string spans multiple lines, insert escape sequences (\n) where relevant
172 &apos;&apos;&apos; MsgStr: (internal use only) the translated string
173 &apos;&apos;&apos; If the string spans multiple lines, insert escape sequences (\n) where relevant
174 &apos;&apos;&apos; Returns:
175 &apos;&apos;&apos; True if successful
176 &apos;&apos;&apos; Exceptions:
177 &apos;&apos;&apos; DUPLICATEKEYERROR: such a key exists already
178 &apos;&apos;&apos; Examples:
179 &apos;&apos;&apos; myPO.AddText(, &quot;This is a text to be included in a POT file&quot;)
181 Dim bAdd As Boolean &apos; Output buffer
182 Dim sKey As String &apos; The key part of the new entry in the dictionary
183 Dim vItem As POEntry &apos; The item part of the new entry in the dictionary
184 Const cstPipe = &quot;|&quot; &apos; Pipe forbidden in MsgId&apos;s
185 Const cstThisSub = &quot;L10N.AddText&quot;
186 Const cstSubArgs = &quot;[Context=&quot;&quot;&quot;&quot;], MsgId, [Comment=&quot;&quot;&quot;&quot;]&quot;
188 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
189 bAdd = False
191 Check:
192 If IsMissing(Context) Or IsMissing(Context) Then Context = &quot;&quot;
193 If IsMissing(Comment) Or IsMissing(Comment) Then Comment = &quot;&quot;
194 If IsMissing(MsgStr) Or IsMissing(MsgStr) Then MsgStr = &quot;&quot;
195 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
196 If Not SF_Utils._Validate(Context, &quot;Context&quot;, V_STRING) Then GoTo Finally
197 If Not SF_Utils._Validate(MsgId, &quot;MsgId&quot;, V_STRING) Then GoTo Finally
198 If Not SF_Utils._Validate(Comment, &quot;Comment&quot;, V_STRING) Then GoTo Finally
199 If Not SF_Utils._Validate(MsgStr, &quot;MsgStr&quot;, V_STRING) Then GoTo Finally
200 End If
201 If Len(MsgId) = 0 Then GoTo Finally
203 Try:
204 If Len(Context) &gt; 0 Then sKey = Context Else sKey = MsgId
205 If _Dictionary.Exists(sKey) Then GoTo CatchDuplicate
207 With vItem
208 .Comment = Comment
209 If InStr(MsgId, &quot;%&quot;) &gt; 0 Then .Flag = &quot;kde-format&quot; Else .Flag = &quot;&quot;
210 .Context = Replace(Context, cstPipe, &quot; &quot;)
211 .MsgId = Replace(MsgId, cstPipe, &quot; &quot;)
212 .MsgStr = MsgStr
213 End With
214 _Dictionary.Add(sKey, vItem)
216 Finally:
217 AddText = bAdd
218 SF_Utils._ExitFunction(cstThisSub)
219 Exit Function
220 Catch:
221 GoTo Finally
222 CatchDuplicate:
223 SF_Exception.RaiseFatal(DUPLICATEKEYERROR, Iif(Len(Context) &gt; 0, &quot;Context&quot;, &quot;MsgId&quot;), sKey)
224 GoTo Finally
225 End Function &apos; ScriptForge.SF_L10N.AddText
227 REM -----------------------------------------------------------------------------
228 Public Function ExportToPOTFile(Optional ByVal FileName As Variant _
229 , Optional ByVal Header As Variant _
230 , Optional ByVal Encoding As Variant _
231 ) As Boolean
232 &apos;&apos;&apos; Export a set of untranslated strings as a POT file
233 &apos;&apos;&apos; The set of strings has been built either by a succession of AddText() methods
234 &apos;&apos;&apos; or by a successful invocation of the L10N service with the FolderName argument
235 &apos;&apos;&apos; The generated file should pass successfully the &quot;msgfmt --check &apos;the pofile&apos;&quot; GNU command
236 &apos;&apos;&apos; Args:
237 &apos;&apos;&apos; FileName: the complete file name to export to. If it exists, is overwritten without warning
238 &apos;&apos;&apos; Header: Comments that will appear on top of the generated file. Do not include any leading &quot;#&quot;
239 &apos;&apos;&apos; If the string spans multiple lines, insert escape sequences (\n) where relevant
240 &apos;&apos;&apos; A standard header will be added anyway
241 &apos;&apos;&apos; Encoding: The character set that should be used
242 &apos;&apos;&apos; Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
243 &apos;&apos;&apos; Note that LibreOffice probably does not implement all existing sets
244 &apos;&apos;&apos; Default = UTF-8
245 &apos;&apos;&apos; Returns:
246 &apos;&apos;&apos; True if successful
247 &apos;&apos;&apos; Examples:
248 &apos;&apos;&apos; myPO.ExportToPOTFile(&quot;myFile.pot&quot;, Header := &quot;Top comment\nSecond line of top comment&quot;)
250 Dim bExport As Boolean &apos; Return value
251 Dim oFile As Object &apos; Generated file handler
252 Dim vLines As Variant &apos; Wrapped lines
253 Dim sLine As String &apos; A single line
254 Dim vItems As Variant &apos; Array of dictionary items
255 Dim vItem As Variant &apos; POEntry type
256 Const cstSharp = &quot;# &quot;, cstSharpDot = &quot;#. &quot;, cstFlag = &quot;#, kde-format&quot;
257 Const cstTabSize = 4
258 Const cstWrap = 70
259 Const cstThisSub = &quot;L10N.ExportToPOTFile&quot;
260 Const cstSubArgs = &quot;FileName, [Header=&quot;&quot;&quot;&quot;], [Encoding=&quot;&quot;UTF-8&quot;&quot;&quot;
262 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
263 bExport = False
265 Check:
266 If IsMissing(Header) Or IsMissing(Header) Then Header = &quot;&quot;
267 If IsMissing(Encoding) Or IsMissing(Encoding) Then Encoding = &quot;UTF-8&quot;
268 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
269 If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
270 If Not SF_Utils._Validate(Header, &quot;Header&quot;, V_STRING) Then GoTo Finally
271 If Not SF_Utils._Validate(Encoding, &quot;Encoding&quot;, V_STRING) Then GoTo Finally
272 End If
274 Try:
275 Set oFile = SF_FileSystem.CreateTextFile(FileName, Overwrite := True, Encoding := Encoding)
276 If Not IsNull(oFile) Then
277 With oFile
278 &apos; Standard header
279 .WriteLine(cstSharp)
280 .WriteLine(cstSharp &amp; &quot;This pristine POT file has been generated by LibreOffice/ScriptForge&quot;)
281 .WriteLine(cstSharp &amp; &quot;Full documentation is available on https://help.libreoffice.org/&quot;)
282 &apos; User header
283 If Len(Header) &gt; 0 Then
284 .WriteLine(cstSharp)
285 vLines = SF_String.Wrap(Header, cstWrap, cstTabSize)
286 For Each sLine In vLines
287 .WriteLine(cstSharp &amp; Replace(sLine, SF_String.sfLF, &quot;&quot;))
288 Next sLine
289 End If
290 &apos; Standard header
291 .WriteLine(cstSharp)
292 .WriteLine(&quot;msgid &quot;&quot;&quot;&quot;&quot;)
293 .WriteLine(&quot;msgstr &quot;&quot;&quot;&quot;&quot;)
294 .WriteLine(SF_String.Quote(&quot;Project-Id-Version: PACKAGE VERSION\n&quot;))
295 .WriteLine(SF_String.Quote(&quot;Report-Msgid-Bugs-To: &quot; _
296 &amp; &quot;https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&amp;bug_status=UNCONFIRMED&amp;component=UI\n&quot;))
297 .WriteLine(SF_String.Quote(&quot;POT-Creation-Date: &quot; &amp; SF_STring.Represent(Now()) &amp; &quot;\n&quot;))
298 .WriteLine(SF_String.Quote(&quot;PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n&quot;))
299 .WriteLine(SF_String.Quote(&quot;Last-Translator: FULL NAME &lt;EMAIL@ADDRESS&gt;\n&quot;))
300 .WriteLine(SF_String.Quote(&quot;Language-Team: LANGUAGE &lt;EMAIL@ADDRESS&gt;\n&quot;))
301 .WriteLine(SF_String.Quote(&quot;Language: en_US\n&quot;))
302 .WriteLine(SF_String.Quote(&quot;MIME-Version: 1.0\n&quot;))
303 .WriteLine(SF_String.Quote(&quot;Content-Type: text/plain; charset=&quot; &amp; Encoding &amp; &quot;\n&quot;))
304 .WriteLine(SF_String.Quote(&quot;Content-Transfer-Encoding: 8bit\n&quot;))
305 .WriteLine(SF_String.Quote(&quot;Plural-Forms: nplurals=2; plural=n &gt; 1;\n&quot;))
306 .WriteLine(SF_String.Quote(&quot;X-Generator: LibreOffice - ScriptForge\n&quot;))
307 .WriteLine(SF_String.Quote(&quot;X-Accelerator-Marker: ~\n&quot;))
308 &apos; Individual translatable strings
309 vItems = _Dictionary.Items()
310 For Each vItem in vItems
311 .WriteBlankLines(1)
312 &apos; Comments
313 vLines = Split(vItem.Comment, &quot;\n&quot;)
314 For Each sLine In vLines
315 .WriteLine(cstSharpDot &amp; SF_String.ExpandTabs(SF_String.Unescape(sLine), cstTabSize))
316 Next sLine
317 &apos; Flag
318 If InStr(vItem.MsgId, &quot;%&quot;) &gt; 0 Then .WriteLine(cstFlag)
319 &apos; Context
320 If Len(vItem.Context) &gt; 0 Then
321 .WriteLine(&quot;msgctxt &quot; &amp; SF_String.Quote(vItem.Context))
322 End If
323 &apos; MsgId
324 vLines = SF_String.Wrap(vItem.MsgId, cstWrap, cstTabSize)
325 If UBound(vLines) = 0 Then
326 .WriteLine(&quot;msgid &quot; &amp; SF_String.Quote(SF_String.Escape(vLines(0))))
327 Else
328 .WriteLine(&quot;msgid &quot;&quot;&quot;&quot;&quot;)
329 For Each sLine in vLines
330 .WriteLine(SF_String.Quote(SF_String.Escape(sLine)))
331 Next sLine
332 End If
333 &apos; MsgStr
334 .WriteLine(&quot;msgstr &quot;&quot;&quot;&quot;&quot;)
335 Next vItem
336 .CloseFile()
337 End With
338 End If
339 bExport = True
341 Finally:
342 If Not IsNull(oFile) Then Set oFile = oFile.Dispose()
343 ExportToPOTFile = bExport
344 SF_Utils._ExitFunction(cstThisSub)
345 Exit Function
346 Catch:
347 GoTo Finally
348 End Function &apos; ScriptForge.SF_L10N.ExportToPOTFile
350 REM -----------------------------------------------------------------------------
351 Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
352 &apos;&apos;&apos; Return the actual value of the given property
353 &apos;&apos;&apos; Args:
354 &apos;&apos;&apos; PropertyName: the name of the property as a string
355 &apos;&apos;&apos; Returns:
356 &apos;&apos;&apos; The actual value of the property
357 &apos;&apos;&apos; If the property does not exist, returns Null
358 &apos;&apos;&apos; Exceptions:
359 &apos;&apos;&apos; ARGUMENTERROR The property does not exist
360 &apos;&apos;&apos; Examples:
361 &apos;&apos;&apos; myL10N.GetProperty(&quot;MyProperty&quot;)
363 Const cstThisSub = &quot;L10N.GetProperty&quot;
364 Const cstSubArgs = &quot;&quot;
366 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
367 GetProperty = Null
369 Check:
370 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
371 If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
372 End If
374 Try:
375 GetProperty = _PropertyGet(PropertyName)
377 Finally:
378 SF_Utils._ExitFunction(cstThisSub)
379 Exit Function
380 Catch:
381 GoTo Finally
382 End Function &apos; ScriptForge.SF_L10N.GetProperty
384 REM -----------------------------------------------------------------------------
385 Public Function GetText(Optional ByVal MsgId As Variant _
386 , ParamArray pvArgs As Variant _
387 ) As String
388 &apos;&apos;&apos; Get the translated string corresponding with the given argument
389 &apos;&apos;&apos; Args:
390 &apos;&apos;&apos; MsgId: the identifier of the string or the untranslated string
391 &apos;&apos;&apos; Either - the untranslated text (MsgId)
392 &apos;&apos;&apos; - the reference to the untranslated text (Context)
393 &apos;&apos;&apos; - both (Context|MsgId) : the pipe character is essential
394 &apos;&apos;&apos; pvArgs(): a list of arguments present as %1, %2, ... in the (un)translated string)
395 &apos;&apos;&apos; to be substituted in the returned string
396 &apos;&apos;&apos; Any type is admitted but only strings, numbers or dates are relevant
397 &apos;&apos;&apos; Returns:
398 &apos;&apos;&apos; The translated string
399 &apos;&apos;&apos; If not found the MsgId string or the Context string
400 &apos;&apos;&apos; Anyway the substitution is done
401 &apos;&apos;&apos; Examples:
402 &apos;&apos;&apos; myPO.GetText(&quot;This is a text to be included in a POT file&quot;)
403 &apos;&apos;&apos; &apos; Ceci est un text à inclure dans un fichier POT
405 Dim sText As String &apos; Output buffer
406 Dim sContext As String &apos; Context part of argument
407 Dim sMsgId As String &apos; MsgId part of argument
408 Dim vItem As POEntry &apos; Entry in the dictionary
409 Dim vMsgId As Variant &apos; MsgId split on pipe
410 Dim sKey As String &apos; Key of dictionary
411 Dim sPercent As String &apos; %1, %2, ... placeholders
412 Dim i As Long
413 Const cstPipe = &quot;|&quot;
414 Const cstThisSub = &quot;L10N.GetText&quot;
415 Const cstSubArgs = &quot;MsgId, [Arg0, Arg1, ...]&quot;
417 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
418 sText = &quot;&quot;
420 Check:
421 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
422 If Not SF_Utils._Validate(MsgId, &quot;MsgId&quot;, V_STRING) Then GoTo Finally
423 End If
424 If Len(Trim(MsgId)) = 0 Then GoTo Finally
425 sText = MsgId
427 Try:
428 &apos; Find and load entry from dictionary
429 If Left(MsgId, 1) = cstPipe then MsgId = Mid(MsgId, 2)
430 vMsgId = Split(MsgId, cstPipe)
431 sKey = vMsgId(0)
432 If Not _Dictionary.Exists(sKey) Then &apos; Not found
433 If UBound(vMsgId) = 0 Then sText = vMsgId(0) Else sText = Mid(MsgId, InStr(MsgId, cstPipe) + 1)
434 Else
435 vItem = _Dictionary.Item(sKey)
436 If Len(vItem.MsgStr) &gt; 0 Then sText = vItem.MsgStr Else sText = vItem.MsgId
437 End If
439 &apos; Substitute %i placeholders
440 For i = UBound(pvArgs) To 0 Step -1 &apos; Go downwards to not have a limit in number of args
441 sPercent = &quot;%&quot; &amp; (i + 1)
442 sText = Replace(sText, sPercent, SF_String.Represent(pvArgs(i)))
443 Next i
445 Finally:
446 GetText = sText
447 SF_Utils._ExitFunction(cstThisSub)
448 Exit Function
449 Catch:
450 GoTo Finally
451 End Function &apos; ScriptForge.SF_L10N.GetText
453 REM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
454 Public Function _(Optional ByVal MsgId As Variant _
455 , ParamArray pvArgs As Variant _
456 ) As String
457 &apos;&apos;&apos; Get the translated string corresponding with the given argument
458 &apos;&apos;&apos; Alias of GetText() - See above
459 &apos;&apos;&apos; Examples:
460 &apos;&apos;&apos; myPO._(&quot;This is a text to be included in a POT file&quot;)
461 &apos;&apos;&apos; &apos; Ceci est un text à inclure dans un fichier POT
463 Dim sText As String &apos; Output buffer
464 Dim sPercent As String &apos; %1, %2, ... placeholders
465 Dim i As Long
466 Const cstPipe = &quot;|&quot;
467 Const cstThisSub = &quot;L10N._&quot;
468 Const cstSubArgs = &quot;MsgId, [Arg0, Arg1, ...]&quot;
470 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
471 sText = &quot;&quot;
473 Check:
474 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
475 If Not SF_Utils._Validate(MsgId, &quot;MsgId&quot;, V_STRING) Then GoTo Finally
476 End If
477 If Len(Trim(MsgId)) = 0 Then GoTo Finally
479 Try:
480 &apos; Find and load entry from dictionary
481 sText = GetText(MsgId)
483 &apos; Substitute %i placeholders - done here, not in GetText(), because # of arguments is undefined
484 For i = 0 To UBound(pvArgs)
485 sPercent = &quot;%&quot; &amp; (i + 1)
486 sText = Replace(sText, sPercent, SF_String.Represent(pvArgs(i)))
487 Next i
489 Finally:
490 _ = sText
491 SF_Utils._ExitFunction(cstThisSub)
492 Exit Function
493 Catch:
494 GoTo Finally
495 End Function &apos; ScriptForge.SF_L10N._
497 REM -----------------------------------------------------------------------------
498 Public Function Methods() As Variant
499 &apos;&apos;&apos; Return the list of public methods of the L10N service as an array
501 Methods = Array( _
502 &quot;AddText&quot; _
503 , &quot;ExportToPOTFile&quot; _
504 , &quot;GetText&quot; _
505 , &quot;_&quot; _
508 End Function &apos; ScriptForge.SF_L10N.Methods
510 REM -----------------------------------------------------------------------------
511 Public Function Properties() As Variant
512 &apos;&apos;&apos; Return the list or properties of the Timer class as an array
514 Properties = Array( _
515 &quot;Folder&quot; _
516 , &quot;Languages&quot; _
517 , &quot;Locale&quot; _
520 End Function &apos; ScriptForge.SF_L10N.Properties
522 REM -----------------------------------------------------------------------------
523 Public Function SetProperty(Optional ByVal PropertyName As Variant _
524 , Optional ByRef Value As Variant _
525 ) As Boolean
526 &apos;&apos;&apos; Set a new value to the given property
527 &apos;&apos;&apos; Args:
528 &apos;&apos;&apos; PropertyName: the name of the property as a string
529 &apos;&apos;&apos; Value: its new value
530 &apos;&apos;&apos; Exceptions
531 &apos;&apos;&apos; ARGUMENTERROR The property does not exist
533 Const cstThisSub = &quot;L10N.SetProperty&quot;
534 Const cstSubArgs = &quot;PropertyName, Value&quot;
536 If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
537 SetProperty = False
539 Check:
540 If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
541 If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
542 End If
544 Try:
545 Select Case UCase(PropertyName)
546 Case Else
547 End Select
549 Finally:
550 SF_Utils._ExitFunction(cstThisSub)
551 Exit Function
552 Catch:
553 GoTo Finally
554 End Function &apos; ScriptForge.SF_L10N.SetProperty
556 REM =========================================================== PRIVATE FUNCTIONS
558 REM -----------------------------------------------------------------------------
559 Public Sub _Initialize(ByVal psPOFile As String _
560 , ByVal Encoding As String _
562 &apos;&apos;&apos; Completes initialization of the current instance requested from CreateScriptService()
563 &apos;&apos;&apos; Load the POFile in the dictionary, otherwise leave the dictionary empty
564 &apos;&apos;&apos; Args:
565 &apos;&apos;&apos; psPOFile: the file to load the translated strings from
566 &apos;&apos;&apos; Encoding: The character set that should be used. Default = UTF-8
568 Dim oFile As Object &apos; PO file handler
569 Dim sContext As String &apos; Collected context string
570 Dim sMsgId As String &apos; Collected untranslated string
571 Dim sComment As String &apos; Collected comment string
572 Dim sMsgStr As String &apos; Collected translated string
573 Dim sLine As String &apos; Last line read
574 Dim iContinue As Integer &apos; 0 = None, 1 = MsgId, 2 = MsgStr
575 Const cstMsgId = 1, cstMsgStr = 2
577 Try:
578 &apos; Initialize dictionary anyway
579 Set _Dictionary = SF_Services.CreateScriptService(&quot;Dictionary&quot;)
580 Set _Dictionary.[_Parent] = [Me]
582 &apos; Load PO file
583 If Len(psPOFile) &gt; 0 Then
584 With SF_FileSystem
585 _POFolder = ._ConvertToUrl(.GetParentFolderName(psPOFile))
586 _Locale = .GetBaseName(psPOFile)
587 _POFile = ._ConvertToUrl(psPOFile)
588 End With
589 &apos; Load PO file
590 Set oFile = SF_FileSystem.OpenTextFile(psPOFile, IOMode := SF_FileSystem.ForReading, Encoding := Encoding)
591 If Not IsNull(oFile) Then
592 With oFile
593 &apos; The PO file is presumed valid =&gt; syntax check is not very strict
594 sContext = &quot;&quot; : sMsgId = &quot;&quot; : sComment = &quot;&quot; : sMsgStr = &quot;&quot;
595 Do While Not .AtEndOfStream
596 sLine = Trim(.ReadLine())
597 &apos; Trivial examination of line header
598 Select Case True
599 Case sLine = &quot;&quot;
600 If Len(sMsgId) &gt; 0 Then AddText(sContext, sMsgId, sComment, sMsgStr)
601 sContext = &quot;&quot; : sMsgId = &quot;&quot; : sComment = &quot;&quot; : sMsgStr = &quot;&quot;
602 iContinue = 0
603 Case Left(sLine, 3) = &quot;#. &quot;
604 sComment = sComment &amp; Iif(Len(sComment) &gt; 0, &quot;\n&quot;, &quot;&quot;) &amp; Trim(Mid(sLine, 4))
605 iContinue = 0
606 Case Left(sLine, 8) = &quot;msgctxt &quot;
607 sContext = SF_String.Unquote(Trim(Mid(sLine, 9)))
608 iContinue = 0
609 Case Left(sLine, 6) = &quot;msgid &quot;
610 sMsgId = SF_String.Unquote(Trim(Mid(sLine, 7)))
611 iContinue = cstMsgId
612 Case Left(sLine, 7) = &quot;msgstr &quot;
613 sMsgStr = sMsgStr &amp; SF_String.Unquote(Trim(Mid(sLine, 8)))
614 iContinue = cstMsgStr
615 Case Left(sLine, 1) = &quot;&quot;&quot;&quot;
616 If iContinue = cstMsgId Then
617 sMsgId = sMsgId &amp; SF_String.Unquote(sLine)
618 ElseIf iContinue = cstMsgStr Then
619 sMsgStr = sMsgStr &amp; SF_String.Unquote(sLine)
620 Else
621 iContinue = 0
622 End If
623 Case Else &apos; Skip line
624 iContinue = 0
625 End Select
626 Loop
627 &apos; Be sure to store the last entry
628 If Len(sMsgId) &gt; 0 Then AddText(sContext, sMsgId, sComment, sMsgStr)
629 .CloseFile()
630 Set oFile = .Dispose()
631 End With
632 End If
633 Else
634 _POFolder = &quot;&quot;
635 _Locale = &quot;&quot;
636 _POFile = &quot;&quot;
637 End If
639 Finally:
640 Exit Sub
641 End Sub &apos; ScriptForge.SF_L10N._Initialize
643 REM -----------------------------------------------------------------------------
644 Private Function _PropertyGet(Optional ByVal psProperty As String)
645 &apos;&apos;&apos; Return the value of the named property
646 &apos;&apos;&apos; Args:
647 &apos;&apos;&apos; psProperty: the name of the property
649 Dim vFiles As Variant &apos; Array of PO-files
650 Dim i As Long
651 Dim cstThisSub As String
652 Dim cstSubArgs As String
654 cstThisSub = &quot;SF_L10N.get&quot; &amp; psProperty
655 cstSubArgs = &quot;&quot;
656 SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
658 With SF_FileSystem
659 Select Case psProperty
660 Case &quot;Folder&quot;
661 If Len(_POFolder) &gt; 0 Then _PropertyGet = ._ConvertFromUrl(_POFolder) Else _PropertyGet = &quot;&quot;
662 Case &quot;Languages&quot;
663 If Len(_POFolder) &gt; 0 Then
664 vFiles = .Files(._ConvertFromUrl(_POFolder), &quot;??-??.po&quot;)
665 For i = 0 To UBound(vFiles)
666 vFiles(i) = SF_FileSystem.GetBaseName(vFiles(i))
667 Next i
668 Else
669 vFiles = Array()
670 End If
671 _PropertyGet = vFiles
672 Case &quot;Locale&quot;
673 _PropertyGet = _Locale
674 Case Else
675 _PropertyGet = Null
676 End Select
677 End With
679 Finally:
680 SF_Utils._ExitFunction(cstThisSub)
681 Exit Function
682 End Function &apos; ScriptForge.SF_L10N._PropertyGet
684 REM -----------------------------------------------------------------------------
685 Private Function _Repr() As String
686 &apos;&apos;&apos; Convert the L10N instance to a readable string, typically for debugging purposes (DebugPrint ...)
687 &apos;&apos;&apos; Args:
688 &apos;&apos;&apos; Return:
689 &apos;&apos;&apos; &quot;[L10N]: PO file&quot;
691 _Repr = &quot;[L10N]: &quot; &amp; _POFile
693 End Function &apos; ScriptForge.SF_L10N._Repr
695 REM ============================================ END OF SCRIPTFORGE.SF_L10N
696 </script:module>