697 lines
30 KiB
Java
697 lines
30 KiB
Java
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
|
|
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_L10N" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
|
|
REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
|
|
REM === Full documentation is available on https://help.libreoffice.org/ ===
|
|
REM =======================================================================================================================
|
|
|
|
Option Compatible
|
|
Option ClassModule
|
|
'Option Private Module
|
|
|
|
Option Explicit
|
|
|
|
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
''' L10N (aka SF_L10N)
|
|
''' ====
|
|
''' Implementation of a Basic class for providing a number of services
|
|
''' related to the translation of user interfaces into a huge number of languages
|
|
''' with a minimal impact on the program code itself
|
|
'''
|
|
''' The design choices of this module are based on so-called PO-files
|
|
''' PO-files (portable object files) have long been promoted in the free software industry
|
|
''' as a mean of providing multilingual UIs. This is accomplished through the use of human-readable
|
|
''' text files with a well defined structure that specifies, for any given language,
|
|
''' the source language string and the localized string
|
|
'''
|
|
''' To read more about the PO format and its ecosystem of associated toolsets:
|
|
''' https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html#PO-Files
|
|
''' and, IMHO, a very good tutorial:
|
|
''' http://pology.nedohodnik.net/doc/user/en_US/ch-about.html
|
|
'''
|
|
''' The main advantage of the PO format is the complete dissociation between the two
|
|
''' very different profiles, i.e. the programmer and the translator(s).
|
|
''' Being independent text files, one per language to support, the programmer may give away
|
|
''' pristine PO template files (known as POT-files) for a translator to process.
|
|
'''
|
|
''' This class implements mainly 3 mechanisms:
|
|
''' - AddText: for the programmer to build a set of words or sentences
|
|
''' meant for being translated later
|
|
''' - ExportToPOTFile: All the above texts are exported into a pristine POT-file
|
|
''' - GetText: At runtime get the text in the user language
|
|
''' Note that the first two are optional: POT and PO-files may be built with a simple text editor
|
|
'''
|
|
''' Several instances of the L10N class may coexist
|
|
' The constraint however is that each instance should find its PO-files
|
|
''' in a separate directory
|
|
''' PO-files must be named with the targeted locale: f.i. "en-US.po" or "fr-BE.po"
|
|
'''
|
|
''' Service invocation syntax
|
|
''' CreateScriptService("L10N"[, FolderName[, Locale]])
|
|
''' FolderName: the folder containing the PO-files (in SF_FileSystem.FileNaming notation)
|
|
''' Locale: in the form la-CO (language-COUNTRY)
|
|
''' Service invocation examples:
|
|
''' Dim myPO As Variant
|
|
''' myPO = CreateScriptService("L10N") ' AddText and ExportToPOTFile are allowed
|
|
''' myPO = CreateScriptService("L10N", "C:\myPOFiles\", "fr-BE")
|
|
''' 'All functionalities are available
|
|
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
REM =============================================================== PRIVATE TYPES
|
|
|
|
''' The recognized elements of an entry in a PO file are (other elements are ignored) :
|
|
''' #. Extracted comments (given by the programmer to the translator)
|
|
''' #, flag (the kde-format flag when the string contains tokens)
|
|
''' msgctxt Context (to store an acronym associated with the message, this is a distortion of the norm)
|
|
''' msgid untranslated-string
|
|
''' msgstr translated-string
|
|
''' NB: plural forms are not supported
|
|
|
|
Type POEntry
|
|
Comment As String
|
|
Flag As String
|
|
Context As String
|
|
MsgId As String
|
|
MsgStr As String
|
|
End Type
|
|
|
|
REM ================================================================== EXCEPTIONS
|
|
|
|
Const DUPLICATEKEYERROR = "DUPLICATEKEYERROR"
|
|
|
|
REM ============================================================= PRIVATE MEMBERS
|
|
|
|
Private [Me] As Object
|
|
Private [_Parent] As Object
|
|
Private ObjectType As String ' Must be "L10N"
|
|
Private ServiceName As String
|
|
Private _POFolder As String ' PO files container
|
|
Private _Locale As String ' la-CO
|
|
Private _POFile As String ' PO file in URL format
|
|
Private _Encoding As String ' Used to open the PO file, default = UTF-8
|
|
Private _Dictionary As Object ' SF_Dictionary
|
|
|
|
REM ===================================================== CONSTRUCTOR/DESTRUCTOR
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Private Sub Class_Initialize()
|
|
Set [Me] = Nothing
|
|
Set [_Parent] = Nothing
|
|
ObjectType = "L10N"
|
|
ServiceName = "ScriptForge.L10N"
|
|
_POFolder = ""
|
|
_Locale = ""
|
|
_POFile = ""
|
|
Set _Dictionary = Nothing
|
|
End Sub ' ScriptForge.SF_L10N Constructor
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Private Sub Class_Terminate()
|
|
|
|
If Not IsNull(_Dictionary) Then Set _Dictionary = _Dictionary.Dispose()
|
|
Call Class_Initialize()
|
|
End Sub ' ScriptForge.SF_L10N Destructor
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function Dispose() As Variant
|
|
Call Class_Terminate()
|
|
Set Dispose = Nothing
|
|
End Function ' ScriptForge.SF_L10N Explicit Destructor
|
|
|
|
REM ================================================================== PROPERTIES
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Property Get Folder() As String
|
|
''' Returns the FolderName containing the PO-files expressed as given by the current FileNaming
|
|
''' property of the SF_FileSystem service. Default = URL format
|
|
''' May be empty
|
|
''' Example:
|
|
''' myPO.Folder
|
|
|
|
Folder = _PropertyGet("Folder")
|
|
|
|
End Property ' ScriptForge.SF_L10N.Folder
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Property Get Languages() As Variant
|
|
''' Returns a zero-based array listing all the BaseNames of the PO-files found in Folder,
|
|
''' Example:
|
|
''' myPO.Languages
|
|
|
|
Languages = _PropertyGet("Languages")
|
|
|
|
End Property ' ScriptForge.SF_L10N.Languages
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Property Get Locale() As String
|
|
''' Returns the currently active language-COUNTRY combination. May be empty
|
|
''' Example:
|
|
''' myPO.Locale
|
|
|
|
Locale = _PropertyGet("Locale")
|
|
|
|
End Property ' ScriptForge.SF_L10N.Locale
|
|
|
|
REM ===================================================================== METHODS
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function AddText(Optional ByVal Context As Variant _
|
|
, Optional ByVal MsgId As Variant _
|
|
, Optional ByVal Comment As Variant _
|
|
, Optional ByVal MsgStr As Variant _
|
|
) As Boolean
|
|
''' Add a new entry in the list of localizable text strings
|
|
''' Args:
|
|
''' Context: when not empty, the key to retrieve the translated string via GetText. Default = ""
|
|
''' MsgId: the untranslated string, i.e. the text appearing in the program code. Must not be empty
|
|
''' The key to retrieve the translated string via GetText when Context is empty
|
|
''' May contain placeholders (%1 ... %9) for dynamic arguments to be inserted in the text at run-time
|
|
''' If the string spans multiple lines, insert escape sequences (\n) where relevant
|
|
''' Comment: the so-called "extracted-comments" intended to inform/help translators
|
|
''' If the string spans multiple lines, insert escape sequences (\n) where relevant
|
|
''' MsgStr: (internal use only) the translated string
|
|
''' If the string spans multiple lines, insert escape sequences (\n) where relevant
|
|
''' Returns:
|
|
''' True if successful
|
|
''' Exceptions:
|
|
''' DUPLICATEKEYERROR: such a key exists already
|
|
''' Examples:
|
|
''' myPO.AddText(, "This is a text to be included in a POT file")
|
|
|
|
Dim bAdd As Boolean ' Output buffer
|
|
Dim sKey As String ' The key part of the new entry in the dictionary
|
|
Dim vItem As POEntry ' The item part of the new entry in the dictionary
|
|
Const cstPipe = "|" ' Pipe forbidden in MsgId's
|
|
Const cstThisSub = "L10N.AddText"
|
|
Const cstSubArgs = "[Context=""""], MsgId, [Comment=""""]"
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
bAdd = False
|
|
|
|
Check:
|
|
If IsMissing(Context) Or IsMissing(Context) Then Context = ""
|
|
If IsMissing(Comment) Or IsMissing(Comment) Then Comment = ""
|
|
If IsMissing(MsgStr) Or IsMissing(MsgStr) Then MsgStr = ""
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._Validate(Context, "Context", V_STRING) Then GoTo Finally
|
|
If Not SF_Utils._Validate(MsgId, "MsgId", V_STRING) Then GoTo Finally
|
|
If Not SF_Utils._Validate(Comment, "Comment", V_STRING) Then GoTo Finally
|
|
If Not SF_Utils._Validate(MsgStr, "MsgStr", V_STRING) Then GoTo Finally
|
|
End If
|
|
If Len(MsgId) = 0 Then GoTo Finally
|
|
|
|
Try:
|
|
If Len(Context) > 0 Then sKey = Context Else sKey = MsgId
|
|
If _Dictionary.Exists(sKey) Then GoTo CatchDuplicate
|
|
|
|
With vItem
|
|
.Comment = Comment
|
|
If InStr(MsgId, "%") > 0 Then .Flag = "kde-format" Else .Flag = ""
|
|
.Context = Replace(Context, cstPipe, " ")
|
|
.MsgId = Replace(MsgId, cstPipe, " ")
|
|
.MsgStr = MsgStr
|
|
End With
|
|
_Dictionary.Add(sKey, vItem)
|
|
|
|
Finally:
|
|
AddText = bAdd
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
CatchDuplicate:
|
|
SF_Exception.RaiseFatal(DUPLICATEKEYERROR, Iif(Len(Context) > 0, "Context", "MsgId"), sKey)
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N.AddText
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function ExportToPOTFile(Optional ByVal FileName As Variant _
|
|
, Optional ByVal Header As Variant _
|
|
, Optional ByVal Encoding As Variant _
|
|
) As Boolean
|
|
''' Export a set of untranslated strings as a POT file
|
|
''' The set of strings has been built either by a succession of AddText() methods
|
|
''' or by a successful invocation of the L10N service with the FolderName argument
|
|
''' The generated file should pass successfully the "msgfmt --check 'the pofile'" GNU command
|
|
''' Args:
|
|
''' FileName: the complete file name to export to. If it exists, is overwritten without warning
|
|
''' Header: Comments that will appear on top of the generated file. Do not include any leading "#"
|
|
''' If the string spans multiple lines, insert escape sequences (\n) where relevant
|
|
''' A standard header will be added anyway
|
|
''' Encoding: The character set that should be used
|
|
''' Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
|
|
''' Note that LibreOffice probably does not implement all existing sets
|
|
''' Default = UTF-8
|
|
''' Returns:
|
|
''' True if successful
|
|
''' Examples:
|
|
''' myPO.ExportToPOTFile("myFile.pot", Header := "Top comment\nSecond line of top comment")
|
|
|
|
Dim bExport As Boolean ' Return value
|
|
Dim oFile As Object ' Generated file handler
|
|
Dim vLines As Variant ' Wrapped lines
|
|
Dim sLine As String ' A single line
|
|
Dim vItems As Variant ' Array of dictionary items
|
|
Dim vItem As Variant ' POEntry type
|
|
Const cstSharp = "# ", cstSharpDot = "#. ", cstFlag = "#, kde-format"
|
|
Const cstTabSize = 4
|
|
Const cstWrap = 70
|
|
Const cstThisSub = "L10N.ExportToPOTFile"
|
|
Const cstSubArgs = "FileName, [Header=""""], [Encoding=""UTF-8"""
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
bExport = False
|
|
|
|
Check:
|
|
If IsMissing(Header) Or IsMissing(Header) Then Header = ""
|
|
If IsMissing(Encoding) Or IsMissing(Encoding) Then Encoding = "UTF-8"
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._ValidateFile(FileName, "FileName") Then GoTo Finally
|
|
If Not SF_Utils._Validate(Header, "Header", V_STRING) Then GoTo Finally
|
|
If Not SF_Utils._Validate(Encoding, "Encoding", V_STRING) Then GoTo Finally
|
|
End If
|
|
|
|
Try:
|
|
Set oFile = SF_FileSystem.CreateTextFile(FileName, Overwrite := True, Encoding := Encoding)
|
|
If Not IsNull(oFile) Then
|
|
With oFile
|
|
' Standard header
|
|
.WriteLine(cstSharp)
|
|
.WriteLine(cstSharp & "This pristine POT file has been generated by LibreOffice/ScriptForge")
|
|
.WriteLine(cstSharp & "Full documentation is available on https://help.libreoffice.org/")
|
|
' User header
|
|
If Len(Header) > 0 Then
|
|
.WriteLine(cstSharp)
|
|
vLines = SF_String.Wrap(Header, cstWrap, cstTabSize)
|
|
For Each sLine In vLines
|
|
.WriteLine(cstSharp & Replace(sLine, SF_String.sfLF, ""))
|
|
Next sLine
|
|
End If
|
|
' Standard header
|
|
.WriteLine(cstSharp)
|
|
.WriteLine("msgid """"")
|
|
.WriteLine("msgstr """"")
|
|
.WriteLine(SF_String.Quote("Project-Id-Version: PACKAGE VERSION\n"))
|
|
.WriteLine(SF_String.Quote("Report-Msgid-Bugs-To: " _
|
|
& "https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"))
|
|
.WriteLine(SF_String.Quote("POT-Creation-Date: " & SF_STring.Represent(Now()) & "\n"))
|
|
.WriteLine(SF_String.Quote("PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n"))
|
|
.WriteLine(SF_String.Quote("Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"))
|
|
.WriteLine(SF_String.Quote("Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"))
|
|
.WriteLine(SF_String.Quote("Language: en_US\n"))
|
|
.WriteLine(SF_String.Quote("MIME-Version: 1.0\n"))
|
|
.WriteLine(SF_String.Quote("Content-Type: text/plain; charset=" & Encoding & "\n"))
|
|
.WriteLine(SF_String.Quote("Content-Transfer-Encoding: 8bit\n"))
|
|
.WriteLine(SF_String.Quote("Plural-Forms: nplurals=2; plural=n > 1;\n"))
|
|
.WriteLine(SF_String.Quote("X-Generator: LibreOffice - ScriptForge\n"))
|
|
.WriteLine(SF_String.Quote("X-Accelerator-Marker: ~\n"))
|
|
' Individual translatable strings
|
|
vItems = _Dictionary.Items()
|
|
For Each vItem in vItems
|
|
.WriteBlankLines(1)
|
|
' Comments
|
|
vLines = Split(vItem.Comment, "\n")
|
|
For Each sLine In vLines
|
|
.WriteLine(cstSharpDot & SF_String.ExpandTabs(SF_String.Unescape(sLine), cstTabSize))
|
|
Next sLine
|
|
' Flag
|
|
If InStr(vItem.MsgId, "%") > 0 Then .WriteLine(cstFlag)
|
|
' Context
|
|
If Len(vItem.Context) > 0 Then
|
|
.WriteLine("msgctxt " & SF_String.Quote(vItem.Context))
|
|
End If
|
|
' MsgId
|
|
vLines = SF_String.Wrap(vItem.MsgId, cstWrap, cstTabSize)
|
|
If UBound(vLines) = 0 Then
|
|
.WriteLine("msgid " & SF_String.Quote(SF_String.Escape(vLines(0))))
|
|
Else
|
|
.WriteLine("msgid """"")
|
|
For Each sLine in vLines
|
|
.WriteLine(SF_String.Quote(SF_String.Escape(sLine)))
|
|
Next sLine
|
|
End If
|
|
' MsgStr
|
|
.WriteLine("msgstr """"")
|
|
Next vItem
|
|
.CloseFile()
|
|
End With
|
|
End If
|
|
bExport = True
|
|
|
|
Finally:
|
|
If Not IsNull(oFile) Then Set oFile = oFile.Dispose()
|
|
ExportToPOTFile = bExport
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N.ExportToPOTFile
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
|
|
''' Return the actual value of the given property
|
|
''' Args:
|
|
''' PropertyName: the name of the property as a string
|
|
''' Returns:
|
|
''' The actual value of the property
|
|
''' If the property does not exist, returns Null
|
|
''' Exceptions:
|
|
''' ARGUMENTERROR The property does not exist
|
|
''' Examples:
|
|
''' myL10N.GetProperty("MyProperty")
|
|
|
|
Const cstThisSub = "L10N.GetProperty"
|
|
Const cstSubArgs = ""
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
GetProperty = Null
|
|
|
|
Check:
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._Validate(PropertyName, "PropertyName", V_STRING, Properties()) Then GoTo Catch
|
|
End If
|
|
|
|
Try:
|
|
GetProperty = _PropertyGet(PropertyName)
|
|
|
|
Finally:
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N.GetProperty
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function GetText(Optional ByVal MsgId As Variant _
|
|
, ParamArray pvArgs As Variant _
|
|
) As String
|
|
''' Get the translated string corresponding with the given argument
|
|
''' Args:
|
|
''' MsgId: the identifier of the string or the untranslated string
|
|
''' Either - the untranslated text (MsgId)
|
|
''' - the reference to the untranslated text (Context)
|
|
''' - both (Context|MsgId) : the pipe character is essential
|
|
''' pvArgs(): a list of arguments present as %1, %2, ... in the (un)translated string)
|
|
''' to be substituted in the returned string
|
|
''' Any type is admitted but only strings, numbers or dates are relevant
|
|
''' Returns:
|
|
''' The translated string
|
|
''' If not found the MsgId string or the Context string
|
|
''' Anyway the substitution is done
|
|
''' Examples:
|
|
''' myPO.GetText("This is a text to be included in a POT file")
|
|
''' ' Ceci est un text à inclure dans un fichier POT
|
|
|
|
Dim sText As String ' Output buffer
|
|
Dim sContext As String ' Context part of argument
|
|
Dim sMsgId As String ' MsgId part of argument
|
|
Dim vItem As POEntry ' Entry in the dictionary
|
|
Dim vMsgId As Variant ' MsgId split on pipe
|
|
Dim sKey As String ' Key of dictionary
|
|
Dim sPercent As String ' %1, %2, ... placeholders
|
|
Dim i As Long
|
|
Const cstPipe = "|"
|
|
Const cstThisSub = "L10N.GetText"
|
|
Const cstSubArgs = "MsgId, [Arg0, Arg1, ...]"
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
sText = ""
|
|
|
|
Check:
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._Validate(MsgId, "MsgId", V_STRING) Then GoTo Finally
|
|
End If
|
|
If Len(Trim(MsgId)) = 0 Then GoTo Finally
|
|
sText = MsgId
|
|
|
|
Try:
|
|
' Find and load entry from dictionary
|
|
If Left(MsgId, 1) = cstPipe then MsgId = Mid(MsgId, 2)
|
|
vMsgId = Split(MsgId, cstPipe)
|
|
sKey = vMsgId(0)
|
|
If Not _Dictionary.Exists(sKey) Then ' Not found
|
|
If UBound(vMsgId) = 0 Then sText = vMsgId(0) Else sText = Mid(MsgId, InStr(MsgId, cstPipe) + 1)
|
|
Else
|
|
vItem = _Dictionary.Item(sKey)
|
|
If Len(vItem.MsgStr) > 0 Then sText = vItem.MsgStr Else sText = vItem.MsgId
|
|
End If
|
|
|
|
' Substitute %i placeholders
|
|
For i = UBound(pvArgs) To 0 Step -1 ' Go downwards to not have a limit in number of args
|
|
sPercent = "%" & (i + 1)
|
|
sText = Replace(sText, sPercent, SF_String.Represent(pvArgs(i)))
|
|
Next i
|
|
|
|
Finally:
|
|
GetText = sText
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N.GetText
|
|
|
|
REM - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Public Function _(Optional ByVal MsgId As Variant _
|
|
, ParamArray pvArgs As Variant _
|
|
) As String
|
|
''' Get the translated string corresponding with the given argument
|
|
''' Alias of GetText() - See above
|
|
''' Examples:
|
|
''' myPO._("This is a text to be included in a POT file")
|
|
''' ' Ceci est un text à inclure dans un fichier POT
|
|
|
|
Dim sText As String ' Output buffer
|
|
Dim sPercent As String ' %1, %2, ... placeholders
|
|
Dim i As Long
|
|
Const cstPipe = "|"
|
|
Const cstThisSub = "L10N._"
|
|
Const cstSubArgs = "MsgId, [Arg0, Arg1, ...]"
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
sText = ""
|
|
|
|
Check:
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._Validate(MsgId, "MsgId", V_STRING) Then GoTo Finally
|
|
End If
|
|
If Len(Trim(MsgId)) = 0 Then GoTo Finally
|
|
|
|
Try:
|
|
' Find and load entry from dictionary
|
|
sText = GetText(MsgId)
|
|
|
|
' Substitute %i placeholders - done here, not in GetText(), because # of arguments is undefined
|
|
For i = 0 To UBound(pvArgs)
|
|
sPercent = "%" & (i + 1)
|
|
sText = Replace(sText, sPercent, SF_String.Represent(pvArgs(i)))
|
|
Next i
|
|
|
|
Finally:
|
|
_ = sText
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N._
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function Methods() As Variant
|
|
''' Return the list of public methods of the L10N service as an array
|
|
|
|
Methods = Array( _
|
|
"AddText" _
|
|
, "ExportToPOTFile" _
|
|
, "GetText" _
|
|
, "_" _
|
|
)
|
|
|
|
End Function ' ScriptForge.SF_L10N.Methods
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function Properties() As Variant
|
|
''' Return the list or properties of the Timer class as an array
|
|
|
|
Properties = Array( _
|
|
"Folder" _
|
|
, "Languages" _
|
|
, "Locale" _
|
|
)
|
|
|
|
End Function ' ScriptForge.SF_L10N.Properties
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Function SetProperty(Optional ByVal PropertyName As Variant _
|
|
, Optional ByRef Value As Variant _
|
|
) As Boolean
|
|
''' Set a new value to the given property
|
|
''' Args:
|
|
''' PropertyName: the name of the property as a string
|
|
''' Value: its new value
|
|
''' Exceptions
|
|
''' ARGUMENTERROR The property does not exist
|
|
|
|
Const cstThisSub = "L10N.SetProperty"
|
|
Const cstSubArgs = "PropertyName, Value"
|
|
|
|
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
|
|
SetProperty = False
|
|
|
|
Check:
|
|
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
|
|
If Not SF_Utils._Validate(PropertyName, "PropertyName", V_STRING, Properties()) Then GoTo Catch
|
|
End If
|
|
|
|
Try:
|
|
Select Case UCase(PropertyName)
|
|
Case Else
|
|
End Select
|
|
|
|
Finally:
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
Catch:
|
|
GoTo Finally
|
|
End Function ' ScriptForge.SF_L10N.SetProperty
|
|
|
|
REM =========================================================== PRIVATE FUNCTIONS
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Public Sub _Initialize(ByVal psPOFile As String _
|
|
, ByVal Encoding As String _
|
|
)
|
|
''' Completes initialization of the current instance requested from CreateScriptService()
|
|
''' Load the POFile in the dictionary, otherwise leave the dictionary empty
|
|
''' Args:
|
|
''' psPOFile: the file to load the translated strings from
|
|
''' Encoding: The character set that should be used. Default = UTF-8
|
|
|
|
Dim oFile As Object ' PO file handler
|
|
Dim sContext As String ' Collected context string
|
|
Dim sMsgId As String ' Collected untranslated string
|
|
Dim sComment As String ' Collected comment string
|
|
Dim sMsgStr As String ' Collected translated string
|
|
Dim sLine As String ' Last line read
|
|
Dim iContinue As Integer ' 0 = None, 1 = MsgId, 2 = MsgStr
|
|
Const cstMsgId = 1, cstMsgStr = 2
|
|
|
|
Try:
|
|
' Initialize dictionary anyway
|
|
Set _Dictionary = SF_Services.CreateScriptService("Dictionary")
|
|
Set _Dictionary.[_Parent] = [Me]
|
|
|
|
' Load PO file
|
|
If Len(psPOFile) > 0 Then
|
|
With SF_FileSystem
|
|
_POFolder = ._ConvertToUrl(.GetParentFolderName(psPOFile))
|
|
_Locale = .GetBaseName(psPOFile)
|
|
_POFile = ._ConvertToUrl(psPOFile)
|
|
End With
|
|
' Load PO file
|
|
Set oFile = SF_FileSystem.OpenTextFile(psPOFile, IOMode := SF_FileSystem.ForReading, Encoding := Encoding)
|
|
If Not IsNull(oFile) Then
|
|
With oFile
|
|
' The PO file is presumed valid => syntax check is not very strict
|
|
sContext = "" : sMsgId = "" : sComment = "" : sMsgStr = ""
|
|
Do While Not .AtEndOfStream
|
|
sLine = Trim(.ReadLine())
|
|
' Trivial examination of line header
|
|
Select Case True
|
|
Case sLine = ""
|
|
If Len(sMsgId) > 0 Then AddText(sContext, sMsgId, sComment, sMsgStr)
|
|
sContext = "" : sMsgId = "" : sComment = "" : sMsgStr = ""
|
|
iContinue = 0
|
|
Case Left(sLine, 3) = "#. "
|
|
sComment = sComment & Iif(Len(sComment) > 0, "\n", "") & Trim(Mid(sLine, 4))
|
|
iContinue = 0
|
|
Case Left(sLine, 8) = "msgctxt "
|
|
sContext = SF_String.Unquote(Trim(Mid(sLine, 9)))
|
|
iContinue = 0
|
|
Case Left(sLine, 6) = "msgid "
|
|
sMsgId = SF_String.Unquote(Trim(Mid(sLine, 7)))
|
|
iContinue = cstMsgId
|
|
Case Left(sLine, 7) = "msgstr "
|
|
sMsgStr = sMsgStr & SF_String.Unquote(Trim(Mid(sLine, 8)))
|
|
iContinue = cstMsgStr
|
|
Case Left(sLine, 1) = """"
|
|
If iContinue = cstMsgId Then
|
|
sMsgId = sMsgId & SF_String.Unquote(sLine)
|
|
ElseIf iContinue = cstMsgStr Then
|
|
sMsgStr = sMsgStr & SF_String.Unquote(sLine)
|
|
Else
|
|
iContinue = 0
|
|
End If
|
|
Case Else ' Skip line
|
|
iContinue = 0
|
|
End Select
|
|
Loop
|
|
' Be sure to store the last entry
|
|
If Len(sMsgId) > 0 Then AddText(sContext, sMsgId, sComment, sMsgStr)
|
|
.CloseFile()
|
|
Set oFile = .Dispose()
|
|
End With
|
|
End If
|
|
Else
|
|
_POFolder = ""
|
|
_Locale = ""
|
|
_POFile = ""
|
|
End If
|
|
|
|
Finally:
|
|
Exit Sub
|
|
End Sub ' ScriptForge.SF_L10N._Initialize
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Private Function _PropertyGet(Optional ByVal psProperty As String)
|
|
''' Return the value of the named property
|
|
''' Args:
|
|
''' psProperty: the name of the property
|
|
|
|
Dim vFiles As Variant ' Array of PO-files
|
|
Dim i As Long
|
|
Dim cstThisSub As String
|
|
Dim cstSubArgs As String
|
|
|
|
cstThisSub = "SF_L10N.get" & psProperty
|
|
cstSubArgs = ""
|
|
SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
|
|
|
|
With SF_FileSystem
|
|
Select Case psProperty
|
|
Case "Folder"
|
|
If Len(_POFolder) > 0 Then _PropertyGet = ._ConvertFromUrl(_POFolder) Else _PropertyGet = ""
|
|
Case "Languages"
|
|
If Len(_POFolder) > 0 Then
|
|
vFiles = .Files(._ConvertFromUrl(_POFolder), "??-??.po")
|
|
For i = 0 To UBound(vFiles)
|
|
vFiles(i) = SF_FileSystem.GetBaseName(vFiles(i))
|
|
Next i
|
|
Else
|
|
vFiles = Array()
|
|
End If
|
|
_PropertyGet = vFiles
|
|
Case "Locale"
|
|
_PropertyGet = _Locale
|
|
Case Else
|
|
_PropertyGet = Null
|
|
End Select
|
|
End With
|
|
|
|
Finally:
|
|
SF_Utils._ExitFunction(cstThisSub)
|
|
Exit Function
|
|
End Function ' ScriptForge.SF_L10N._PropertyGet
|
|
|
|
REM -----------------------------------------------------------------------------
|
|
Private Function _Repr() As String
|
|
''' Convert the L10N instance to a readable string, typically for debugging purposes (DebugPrint ...)
|
|
''' Args:
|
|
''' Return:
|
|
''' "[L10N]: PO file"
|
|
|
|
_Repr = "[L10N]: " & _POFile
|
|
|
|
End Function ' ScriptForge.SF_L10N._Repr
|
|
|
|
REM ============================================ END OF SCRIPTFORGE.SF_L10N
|
|
</script:module>
|