2019-10-23 17:01:13 -05:00
#!/usr/bin/env python3
2017-12-06 11:36:14 -06:00
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
import os
import re
import sys
2020-10-13 14:55:52 -05:00
import polib
2017-12-06 11:36:14 -06:00
from lxml import etree
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
def usageAndExit ( ) :
message = """ usage: {program} [--check|--update|--translate] online_dir [...]
Checks , extracts , or translates . uno : command descriptions from the
LibreOffice XCU files .
Check whether all the commands in the menus have their descriptions in
unocommands . js :
{ program } - - check / path / to / online
Update the unocommands . js by fetching the . uno : commands descriptions from the
core . git . This is what you want to do after you add new . uno : commands or
dialogs to the menus :
{ program } - - update / path / to / online / path / to / loffice
Update the translations of unocommands . js before releasing :
{ program } - - translate / path / to / online / path / to / translations
"""
2020-10-13 14:55:52 -05:00
print ( message . format ( program = sys . argv [ 0 ] ) )
2017-12-06 11:36:14 -06:00
exit ( 1 )
def commandsFromLine ( line ) :
2020-10-13 14:55:52 -05:00
""" Extract uno commands name from lines like " ' Command1 ' , ' Command2 ' , """
2017-12-06 11:36:14 -06:00
commands = [ ]
inCommand = False
command = ' '
for c in line :
if c == " ' " :
inCommand = not inCommand
# command ended, collect it
if not inCommand and command != ' ' :
2020-10-13 14:55:52 -05:00
commands + = [ command ]
2017-12-06 11:36:14 -06:00
command = ' '
elif inCommand :
command + = c
return commands
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Extract uno commands name from lines like " {uno: '.uno:Command3',"
def commandFromMenuLine ( line ) :
2021-01-06 06:50:39 -06:00
m = re . search ( r " \ b_UNO \ ( ' .uno:([^ ' ]*) ' " , line )
2017-12-06 11:36:14 -06:00
if m :
2020-10-13 14:55:52 -05:00
return [ m . group ( 1 ) ]
2017-12-06 11:36:14 -06:00
2021-01-06 06:50:39 -06:00
m = re . search ( r " \ buno: * ' \ .uno:([^ ' ]*) ' " , line )
2017-12-06 11:36:14 -06:00
if m :
2020-10-13 14:55:52 -05:00
return [ m . group ( 1 ) ]
2017-12-06 11:36:14 -06:00
return [ ]
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Extract all the uno commands we are using in the Online menu
def extractMenuCommands ( path ) :
commands = [ ]
# extract from the menu specifications
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.Menubar.js ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
if line . find ( " uno: " ) > = 0 and line . find ( " name: " ) < 0 :
commands + = commandFromMenuLine ( line )
elif line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
# may the list unique
return set ( commands )
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Extract all the uno commands we are using in the Online context menu
def extractContextCommands ( path ) :
2019-11-17 08:38:05 -06:00
commandsToIgnore = [ " FontDialogForParagraph " ]
2017-12-06 11:36:14 -06:00
commands = [ ]
# extract from the comments whitelist
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.ContextMenu.js ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
readingCommands = False
for line in f :
if line . find ( ' UNOCOMMANDS_EXTRACT_START ' ) > = 0 :
readingCommands = True
elif line . find ( ' UNOCOMMANDS_EXTRACT_END ' ) > = 0 :
readingCommands = False
elif readingCommands :
commands + = commandsFromLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.ColumnHeader.ts ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.RowHeader.ts ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.Tabs.js ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2020-10-13 14:55:52 -05:00
commands = [ command for command in commands
if command not in commandsToIgnore ]
2017-12-06 11:36:14 -06:00
# may the list unique
return set ( commands )
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Extract all the uno commands we are using in the Online toolbar
def extractToolbarCommands ( path ) :
commands = [ ]
2020-06-23 07:05:00 -05:00
# extract from the toolbars
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.Toolbar.js ' , ' r ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.MobileBottomBar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.MobileTopBar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.MobileWizardBuilder.js ' , ' r ' , encoding = ' utf-8 ' )
2021-01-28 01:21:46 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2020-10-13 14:55:52 -05:00
f = open ( path +
2023-08-16 19:56:54 -05:00
' /browser/src/control/Control.NotebookbarBuilder.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.Notebookbar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-08-05 08:54:28 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.NotebookbarWriter.js ' , ' r ' , encoding = ' utf-8 ' )
2020-08-05 08:54:28 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.NotebookbarCalc.js ' , ' r ' , encoding = ' utf-8 ' )
2020-08-05 08:54:28 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2020-10-13 14:55:52 -05:00
f = open ( path +
2023-08-16 19:56:54 -05:00
' /browser/src/control/Control.NotebookbarImpress.js ' , ' r ' , encoding = ' utf-8 ' )
2020-08-05 08:54:28 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.NotebookbarDraw.js ' , ' r ' , encoding = ' utf-8 ' )
2021-02-26 03:45:56 -06:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.PresentationBar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.SearchBar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.StatusBar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2023-08-16 19:56:54 -05:00
f = open ( path + ' /browser/src/control/Control.TopToolbar.js ' , ' r ' , encoding = ' utf-8 ' )
2020-06-23 07:05:00 -05:00
for line in f :
if line . find ( " _UNO( " ) > = 0 :
commands + = commandFromMenuLine ( line )
2017-12-06 11:36:14 -06:00
# may the list unique
return set ( commands )
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Create mapping between the commands and appropriate strings
def collectCommandsFromXCU ( xcu , descriptions , commands , label , type ) :
root = etree . parse ( xcu )
2020-10-13 14:55:52 -05:00
nodes = root . xpath ( " /oor:component-data/node/node/node " , namespaces = {
2017-12-06 11:36:14 -06:00
' oor ' : ' http://openoffice.org/2001/registry ' ,
} )
for node in nodes :
# extract the uno command name
unoCommand = node . get ( ' { http://openoffice.org/2001/registry}name ' )
unoCommand = unoCommand [ 5 : ]
if unoCommand in commands :
# normal labels
2020-10-13 14:55:52 -05:00
textElement = node . xpath ( ' prop[@oor:name= " ' + label + ' " ]/value ' ,
namespaces = { ' oor ' :
' http://open ' +
' office.org/2001/registry ' , } )
2017-12-06 11:36:14 -06:00
if len ( textElement ) == 1 :
# extract the uno command's English text
text = ' ' . join ( textElement [ 0 ] . itertext ( ) )
command = { }
if unoCommand in descriptions . keys ( ) :
command = descriptions [ unoCommand ]
if not type in command :
command [ type ] = { }
menuType = ' menu '
if label == ' PopupLabel ' or label == ' TooltipLabel ' :
menuType = ' context '
if menuType in command [ type ] :
continue
command [ type ] [ menuType ] = text
descriptions [ unoCommand ] = command
return descriptions
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Print commands from all the XCU files, and collect them too
2020-10-13 14:55:52 -05:00
def writeUnocommandsJS (
onlineDir , lofficeDir , menuCommands , contextCommands , toolbarCommands ) :
2017-12-06 11:36:14 -06:00
descriptions = { }
dir = lofficeDir + ' /officecfg/registry/data/org/openoffice/Office/UI '
for file in os . listdir ( dir ) :
if file . endswith ( ' .xcu ' ) :
2020-10-13 14:55:52 -05:00
type = ' global '
2017-12-06 11:36:14 -06:00
if file . startswith ( ' Writer ' ) :
type = ' text '
elif file . startswith ( ' Calc ' ) :
type = ' spreadsheet '
elif file . startswith ( ' DrawImpress ' ) :
type = ' presentation '
# main menu
2020-10-13 14:55:52 -05:00
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions , menuCommands ,
' ContextLabel ' , type )
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
contextCommands ,
' ContextLabel ' , type )
2017-12-06 11:36:14 -06:00
# right-click menu
2020-10-13 14:55:52 -05:00
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
contextCommands ,
' PopupLabel ' , type )
2017-12-06 11:36:14 -06:00
# toolbar
2020-10-13 14:55:52 -05:00
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
toolbarCommands ,
' PopupLabel ' , type )
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
toolbarCommands ,
' TooltipLabel ' , type )
2017-12-06 11:36:14 -06:00
# fallbacks
2020-10-13 14:55:52 -05:00
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions , menuCommands ,
' Label ' , type )
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
contextCommands ,
' Label ' , type )
descriptions = collectCommandsFromXCU ( os . path . join ( dir , file ) ,
descriptions ,
toolbarCommands ,
' Label ' , type )
2017-12-06 11:36:14 -06:00
# output the unocommands.js
2021-11-03 08:43:05 -05:00
f = open ( onlineDir + ' /browser/src/unocommands.js ' , ' w ' ,
2020-10-13 14:55:52 -05:00
encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
f . write ( ''' // Don ' t modify, generated using unocommands.py
var unoCommandsArray = { \n ''' )
for key in sorted ( descriptions . keys ( ) ) :
2021-02-10 06:30:42 -06:00
f . write ( ' \t \' ' + key + ' \' : { ' )
2017-12-06 11:36:14 -06:00
for type in sorted ( descriptions [ key ] . keys ( ) ) :
2019-10-23 17:01:13 -05:00
f . write ( type + ' : { ' )
2017-12-06 11:36:14 -06:00
for menuType in sorted ( descriptions [ key ] [ type ] . keys ( ) ) :
2020-10-13 14:55:52 -05:00
f . write ( menuType + " :_( ' " + descriptions [ key ] [ type ] [ menuType ]
+ " ' ), " )
2019-10-23 17:01:13 -05:00
f . write ( ' }, ' )
f . write ( ' }, \n ' )
2017-12-06 11:36:14 -06:00
f . write ( ''' };
2018-04-12 14:57:00 -05:00
window . _UNO = function ( string , component , isContext ) {
2017-12-06 11:36:14 -06:00
\tvar command = string . substr ( 5 ) ;
\tvar context = ' menu ' ;
\tif ( isContext == = true ) {
\t \tcontext = ' context ' ;
\t }
\tvar entry = unoCommandsArray [ command ] ;
\tif ( entry == = undefined ) {
\t \treturn command ;
\t }
\tvar componentEntry = entry [ component ] ;
\tif ( componentEntry == = undefined ) {
\t \tcomponentEntry = entry [ ' global ' ] ;
\t \tif ( componentEntry == = undefined ) {
\t \t \treturn command ;
\t \t }
\t }
\tvar text = componentEntry [ context ] ;
\tif ( text == = undefined ) {
\t \ttext = componentEntry [ ' menu ' ] ;
\t \tif ( text == = undefined ) {
\t \t \treturn command ;
\t \t }
\t }
2019-11-17 08:38:05 -06:00
\treturn this . removeAccessKey ( text ) ;
}
window . removeAccessKey = function ( text ) {
2018-07-16 02:03:40 -05:00
\t / / Remove access key markers from translated strings
\t / / 1. access key in parenthesis in case of non - latin scripts
\ttext = text . replace ( / \( ~ [ A - Za - z ] \) / , ' ' ) ;
\t / / 2. remove normal access key
\ttext = text . replace ( ' ~ ' , ' ' ) ;
\treturn text ;
2017-12-06 11:36:14 -06:00
} \n ''' )
return descriptions
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Read the uno commands present in the unocommands.js for checking
def parseUnocommandsJS ( onlineDir ) :
strings = { }
2021-11-03 08:43:05 -05:00
f = open ( onlineDir + ' /browser/src/unocommands.js ' , ' r ' ,
2020-10-13 14:55:52 -05:00
encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
for line in f :
2021-02-10 06:30:42 -06:00
m = re . match ( r " \ t \ ' ([^:]*) \ ' :.* " , line )
2017-12-06 11:36:14 -06:00
if m :
command = m . group ( 1 )
n = re . findall ( r " _ \ ( ' ([^ ' ]*) ' \ ) " , line )
if n :
strings [ command ] = n
return strings
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
# Generate translation JSONs for the .uno: commands
def writeTranslations ( onlineDir , translationsDir , strings ) :
keys = set ( strings . keys ( ) )
dir = translationsDir + ' /source/ '
for lang in os . listdir ( dir ) :
2021-04-18 04:18:34 -05:00
poFile = dir + lang + ' /officecfg/registry/data/org/openoffice/Office/UI.po '
2017-12-06 11:36:14 -06:00
if not os . path . isfile ( poFile ) :
continue
sys . stderr . write ( ' Generating ' + lang + ' ... \n ' )
2020-10-13 14:55:52 -05:00
po = polib . pofile ( poFile , autodetect_encoding = False ,
encoding = " utf-8 " , wrapwidth = - 1 )
2017-12-06 11:36:14 -06:00
translations = { }
for entry in po . translated_entries ( ) :
m = re . search ( r " \ .uno:([^ \ n]*) \ n " , entry . msgctxt )
if m :
command = m . group ( 1 )
if command in keys :
for text in strings [ command ] :
if text == entry . msgid :
translations [ entry . msgid ] = entry . msgstr
2021-11-03 08:43:05 -05:00
f = open ( onlineDir + ' /browser/l10n/uno/ ' +
2020-10-13 14:55:52 -05:00
lang + ' .json ' , ' w ' , encoding = ' utf-8 ' )
2017-12-06 11:36:14 -06:00
f . write ( ' { \n ' )
writeComma = False
for key in sorted ( translations . keys ( ) ) :
if writeComma :
f . write ( ' , \n ' )
else :
writeComma = True
2020-10-13 14:55:52 -05:00
f . write ( ' " ' + key . replace ( ' " ' , ' \\ \" ' ) + ' " : " ' +
translations [ key ] . replace ( ' " ' , ' \\ \" ' ) + ' " ' )
2017-12-06 11:36:14 -06:00
f . write ( ' \n } \n ' )
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
if __name__ == " __main__ " :
if len ( sys . argv ) < 2 :
usageAndExit ( )
check = False
translate = False
onlineDir = ' '
lofficeDir = ' '
translationsDir = ' '
if ( sys . argv [ 1 ] == ' --check ' ) :
if len ( sys . argv ) != 3 :
usageAndExit ( )
check = True
onlineDir = sys . argv [ 2 ]
elif ( sys . argv [ 1 ] == ' --translate ' ) :
translate = True
if len ( sys . argv ) != 4 :
usageAndExit ( )
onlineDir = sys . argv [ 2 ]
translationsDir = sys . argv [ 3 ]
elif ( sys . argv [ 1 ] == " --update " ) :
if len ( sys . argv ) != 4 :
usageAndExit ( )
onlineDir = sys . argv [ 2 ]
lofficeDir = sys . argv [ 3 ]
else :
usageAndExit ( )
menuCommands = extractMenuCommands ( onlineDir )
contextCommands = extractContextCommands ( onlineDir )
toolbarCommands = extractToolbarCommands ( onlineDir )
processedCommands = set ( [ ] )
parsed = { }
if ( check or translate ) :
parsed = parseUnocommandsJS ( onlineDir )
processedCommands = set ( parsed . keys ( ) )
else :
2020-10-13 14:55:52 -05:00
written = writeUnocommandsJS ( onlineDir , lofficeDir , menuCommands ,
contextCommands , toolbarCommands )
2021-02-10 06:30:42 -06:00
processedCommands = set ( written . keys ( ) )
2017-12-06 11:36:14 -06:00
# check that we have translations for everything
2021-02-10 06:30:42 -06:00
requiredCommands = ( menuCommands | contextCommands | toolbarCommands )
2021-01-28 02:43:28 -06:00
dif = requiredCommands - processedCommands
2020-10-13 14:55:52 -05:00
2017-12-06 11:36:14 -06:00
if len ( dif ) > 0 :
sys . stderr . write ( " ERROR: The following commands are not covered in unocommands.js, run scripts/unocommands.py --update: \n \n .uno: " + ' \n .uno: ' . join ( dif ) + " \n \n " )
exit ( 1 )
if ( translate ) :
writeTranslations ( onlineDir , translationsDir , parsed )
# vim: set shiftwidth=4 softtabstop=4 expandtab: