Repository navigation
Add some missing translator comments and check for missing translator comments in future #7492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| #tests/checkPot.py | ||
| #A part of NonVisual Desktop Access (NVDA) | ||
| #This file is covered by the GNU General Public License. | ||
| #See the file COPYING for more details. | ||
| #Copyright (C) 2017 NV Access Limited | ||
|
|
||
| """Check a translation template (pot) for strings without translator comments. | ||
| """ | ||
|
|
||
| import sys | ||
|
|
||
| # Existing messages that we know don't have translator comments yet. | ||
| # Ideally, all of these should get translator comments, | ||
| # but this is not realistic right now. | ||
| # A message should be removed from here once a translator comment is added for it. | ||
| EXPECTED_MESSAGES_WITHOUT_COMMENTS = { | ||
| 'Focus mode', | ||
| '%s landmark', | ||
| 'border', | ||
| 'filler', | ||
| 'line', | ||
| 'data item', | ||
| 'header item', | ||
| 'calendar', | ||
| 'video', | ||
| 'audio', | ||
| 'modal', | ||
| 'iconified', | ||
| 'editable', | ||
| 'checkable', | ||
| 'draggable', | ||
| 'dragging', | ||
| 'sorted', | ||
| 'sorted ascending', | ||
| 'sorted descending', | ||
| 'gesture map File Error', | ||
| 'text \\"%s\\" not found', | ||
| 'Find Error', | ||
| 'Selected %s', | ||
| 'on', | ||
| 'Type help(object) to get help about object.', | ||
| 'Type exit() to exit the console', | ||
| 'NVDA Python Console', | ||
| 'Emulates pressing %s on the system keyboard', | ||
| 'continuous section break', | ||
| 'new column section break', | ||
| 'new page section break', | ||
| 'even pages section break', | ||
| 'odd pages section break', | ||
| 'column break', | ||
| 'background pattern {pattern}', | ||
| 'NVDA Speech Viewer', | ||
| 'text mode', | ||
| 'object mode', | ||
| 'NonVisual Desktop Access', | ||
| 'A free and open source screen reader for Microsoft Windows', | ||
| 'Copyright (C) {years} NVDA Contributors', | ||
| 'Display', | ||
| 'Reports and moves the review cursor to a recent message', | ||
| 'left', | ||
| 'right', | ||
| 'Show the Handy Tech driver configuration window.', | ||
| 'Recognition failed', | ||
| 'General settings', | ||
| 'Change the synthesizer to be used', | ||
| 'Choose the voice, rate, pitch and volume to use', | ||
| 'Change reporting of mouse shape and object under mouse', | ||
| 'Configure how and when the review cursor moves', | ||
| 'Change reporting of objects', | ||
| 'Change virtual buffers specific settings', | ||
| 'Change settings of document properties', | ||
| 'NVDA &web site', | ||
| 'Reset all settings to saved state', | ||
| 'Reset all settings to default state', | ||
| 'Write the current configuration to nvda.ini', | ||
| 'E&xit', | ||
| 'Options', | ||
| 'Error renaming profile.', | ||
| 'Use this profile for:', | ||
| 'This change requires administrator privileges.', | ||
| 'Insufficient Privileges', | ||
| 'Synthesizer Error', | ||
| 'Dictionary Entry Error', | ||
| 'Could not load the %s display.', | ||
| 'Braille Display Error', | ||
| 'word', | ||
| 'Taskbar', | ||
| '%s items', | ||
| 'invoke', | ||
| 'Desktop', | ||
| 'Input Message is {title}: {message}', | ||
| 'Input Message is {message}', | ||
| 'Series color: {colorName} ', | ||
| 'Comments', | ||
| 'Endnotes', | ||
| 'Even pages footer', | ||
| 'Even pages header', | ||
| 'First page footer', | ||
| 'First page header', | ||
| 'Footnotes', | ||
| 'Primary footer', | ||
| 'Primary header', | ||
| 'Text frame', | ||
| } | ||
|
|
||
| def checkPot(fileName): | ||
| """Returns the number of errors. | ||
| Also prints error messages and a summary to standard output. | ||
| """ | ||
| errors = 0 | ||
| expectedErrors = 0 | ||
| unexpectedSuccesses = 0 | ||
| with file(fileName, "rt") as pot: | ||
| for line in pot: | ||
| line = line.rstrip() | ||
| if not line: | ||
| # End of message. | ||
| hasComment = False | ||
| sourceLines = [] | ||
| context = "" | ||
| continue | ||
| if line == 'msgid ""': | ||
| # Header. | ||
| continue | ||
| if line.startswith("#. Translators: "): | ||
| # Example: "#. Translators: a message reported in the SetRowHeader script for Microsoft Word." | ||
| hasComment = True | ||
| continue | ||
| if line.startswith("#: "): | ||
| # Example: "#: NVDAObjects\window\winword.py:1322" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this, is this saying go and look at winword.py line 1322 for an example, or is the path part of the example? If so (the path case) it might be handy to add some further explanation text. EG "Sometimes we include paths in the translator comments, and so we must handle these because..." |
||
| # Strip the "#: " prefix (3 chars). | ||
| sourceLines.append(line[3:]) | ||
| continue | ||
| if line.startswith('msgctxt "'): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably assumed knowledge for this, but what are: |
||
| context = getStringFromLine(line) | ||
| continue | ||
| if line.startswith("msgid "): | ||
| # Get the message. | ||
| if line == 'msgid ""': | ||
| # Multi-line msgid. | ||
| # Subsequent lines are just quoted strings which should be concatenated. | ||
| msgid = "" | ||
| for line in pot: | ||
| if line.startswith("msgstr "): | ||
| break | ||
| msgid += getStringFromLine(line) | ||
| else: | ||
| # Single line msgid. | ||
| # Example: msgid "Secure Desktop" | ||
| msgid = getStringFromLine(line) | ||
| if context: | ||
| # The context must be considered as part of the message. | ||
| message = "[{context}] {msgid}".format(context=context, msgid=msgid) | ||
| else: | ||
| message = msgid | ||
| isExpectedError = message in EXPECTED_MESSAGES_WITHOUT_COMMENTS | ||
| if not hasComment and isExpectedError: | ||
| expectedErrors += 1 | ||
| continue | ||
| if hasComment and isExpectedError: | ||
| error = ("Message has translator comment, but one wasn't expected.\n" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, turns out you do report this case. Great! |
||
| "This is good, but please remove from EXPECTED_MESSAGES_WITHOUT_COMMENTS in tests/checkPot.py") | ||
| unexpectedSuccesses += 1 | ||
| elif not hasComment: | ||
| errors += 1 | ||
| error = "Message has no translator comment." | ||
| else: | ||
| continue | ||
| print("{error}\n" | ||
| "Source lines: {lines}\n" | ||
| "Message: {message}\n" | ||
| .format(error=error, lines=" ".join(sourceLines), message=message)) | ||
| continue | ||
| print("{errors} errors, {unexpectedSuccesses} unexpected successes, {expectedErrors} expected errors" | ||
| .format(errors=errors, unexpectedSuccesses=unexpectedSuccesses, expectedErrors=expectedErrors)) | ||
| return errors + unexpectedSuccesses | ||
|
|
||
| def getStringFromLine(line): | ||
| if line.startswith('"'): | ||
| # The quoted string begins at the start of the line. | ||
| quoted = line | ||
| else: | ||
| # The quoted string starts after a command. | ||
| # Example: msgid "Secure Desktop" | ||
| quoted = line.split(" ", 1)[1] | ||
| # Strip the quotes. | ||
| return quoted[1:-1] | ||
|
|
||
| if __name__ == "__main__": | ||
| # Support command line usage for quick testing. | ||
| fileName = sys.argv[1] | ||
| print(checkPot(fileName)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| ### | ||
| #This file is a part of the NVDA project. | ||
| #URL: http://www.nvaccess.org/ | ||
| #Copyright 2017 NV Access Limited. | ||
| #This program is free software: you can redistribute it and/or modify | ||
| #it under the terms of the GNU General Public License version 2.0, as published by | ||
| #the Free Software Foundation. | ||
| #This program 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. | ||
| #This license can be found at: | ||
| #http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
| ### | ||
|
|
||
| import checkPot | ||
|
|
||
| Import("env", "sourceDir", "pot") | ||
|
|
||
| unitTests = env.SConscript("unit/sconscript", exports=["env"]) | ||
| env.Depends(unitTests, sourceDir) | ||
| env.AlwaysBuild(unitTests) | ||
|
|
||
| def checkPotAction(target, source, env): | ||
| return checkPot.checkPot(source[0].abspath) | ||
| checkPotTarget = env.Command("checkPot", pot, checkPotAction) | ||
| env.Depends(checkPotTarget, pot) | ||
| env.AlwaysBuild(checkPotTarget) | ||
| env.Alias("checkPot", checkPotTarget) | ||
|
|
||
| # Determine the targets for scons tests. | ||
| # If specific tests are explicitly specified, only run those. | ||
| explicitUnitTests = env.get("unitTests") | ||
| explicitCheckPot = "checkPot" in COMMAND_LINE_TARGETS | ||
| explicit = explicitUnitTests or explicitCheckPot | ||
| tests = [] | ||
| if not explicit or explicitUnitTests: | ||
| tests.append(unitTests) | ||
| if not explicit or explicitCheckPot: | ||
| tests.append(checkPotTarget) | ||
| env.Alias("tests", tests) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the test fail if the exception is incorrectly specified? EG if someone adds a translator comment for one of these, and forgets to remove it from the exception list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found out it does, could you update this comment to specify that it does?