polib appears to be THE library of choice for working with gettext/po files in Python. The docs show how to iterate through message strings, save po and mo files, etc. However, it's not clear to me, how can one edit a specific entry?
Let's say, I iterate over all messages in an existing po file and display them in an HTML form with textareas. By submitting the form, I get - as an example - the original
msgid = "Hello World"
and the via textarea translated
msgstr = "Hallo Welt"
The original part inside the po file may look like this:
#: .\accounts\forms.py:26 .\accounts\registration\forms.py:48
msgid "Hello World"
msgstr ""
or with fuzzy flag set:
#: .\accounts\forms.py:26 .\accounts\registration\forms.py:48
#, fuzzy
msgid "Hello World"
msgstr "Hallo"
Now how do I update this particular translation in the actual po file? And in case this message was marked as "fuzzy", how do I remove this flag?
Any help appreciated ...
Ok, after reading through the source code of polib, I found this way to achieve, what I want:
entry = po.find('Email address')
if entry:
entry.msgstr = 'E-Mail-Adresse'
if 'fuzzy' in entry.flags:
entry.flags.remove('fuzzy')
This seems to be the way to go ...
In the case of pluralisation - just as an example:
entry = po.find('%s hour ago')
if entry and entry.msgid_plural:
entry.msgstr_plural['0'] = 'Vor %s Stunde'
entry.msgstr_plural['1'] = 'Vor %s Stunden'
The docs of polib should definitively be updated. Otherwise great tool.
Related
I'm working on code to automate survey participation requests. My current code looks as follows:
def survey_mail(name, receiver, sender):
text_content = f"Hello {name},\r\n
Thank you for participating in my survey via your mail {receiver}\r\n.
You can contact me via {sender}."
html_content = """\ Hello """ + str(name) + """,<br>
Thank you for participating in my survey via your mail """ + str(receiver) + """<br>.
You can contact me via """ + str(sender) + """.
"""
content = MIMEMultipart('alternative')
content.attach(MIMEText(text_content, 'plain'))
content.attach(MIMEText(html_content, 'html'))
...
I have two questions here:
First, would it be possible to import the two string above simply as
template files?
Second, is there a better way to handle variables in the
string? The current method comes with two different ways to format variables: {} vs. """ + var + """.
I tried to insert the two templates as *.txt files, and then load the templates:
with open("text.txt") as f:
text_content = f.read()
with open("html.txt") as f:
html_content = f.read()
However, this did not work. The code does just import the template as a full string.
f-strings are evaluated are definition time, so you cannot read them from a file. The second way in your example (for html) is an expression. While an expression can be eval-ed, it is generally seen as a poor security practice, because it allows execution of uncontrolled code.
But you could just use format as a poor man's templating engine: it have far less features that full fledged template engines, but is enough here.
Example file for the text part:
Hello {name},
Thank you for participating in my survey via your mail {receiver}.
You can contact me via {sender}.
You can then use it that way:
with open("text.txt") as f:
text_content = f.read().format(sender=sender, receiver=receiver)
I am trying to get my script to post a comment on all comments of a parent post that meet a certain requirement. I am able to get the text to post reliably but I can't seem to get the photo attachment to show up. I'm programming in Python3 and using the Facebook-sdk library to assist.
When reading the Graph API documentation I found the following fields described on the comments edge:
attachment_id
An optional ID of a unpublished photo (see no_story field in /{user-id}/photos) uploaded to Facebook to include as a photo comment. One of attachment_url, attachment_id, message or source must be provided when publishing.
(string)
attachment_url
The URL of an image to include as a photo comment. One of attachment_url, attachment_id, message or source must be provided when publishing.
(string)
source
A photo, encoded as form data, to use as a photo comment. One of attachment_url, attachment_id, message or source must be provided when publishing.
(multipart/form-data)
My code is currently formatted as such (I've provided partial code relevant to this issue):
my_dict = {
0: ('file_1.JPG', "Some text for file 1"),
1: ('file_2.jpg', "Different text for file 2"),
2: ('file_3.JPG', "More different text for file 3"),
3: ('file_4.JPG', "A fourth bit of text for file 4.")
}
comments = api.get_object('PAGE.NUMBER.HERE?fields=posts{comments}')
com_index = comments['posts']['data'][0]['comments']['data']
photo_id = my_dict[x][0]
my_image = 'file:///Users/filepath/{}'.format(photo_id)
text = my_dict[x][1]
api.put_object(com_index[com_index]['id'], "comments/comments", source=(my_image, 'image/jpg'), message=text)
I have tried both with and without the 'image/jpg' argument in the source tuple.
Instead of using 'source' I've also tried:
attachment_url=card_image
attachment=card_image
When using attachment_url I get an invalid url error; when using the other parameters the text always is posted but the photo is not posted.
Lastly, I've tried changing the edge to be a /photos edge of the comment instead of the /comments edge of another comment, but still no luck (as below):
api.put_object(com_index[comment]['id'], "comments/photo", source=(my_image, 'image/jpg'), message=text)
What is the proper method to post a reply that has a photo attachment?
I think {objet-id}/comments is depreciated since version 2.10. Check this.
I'm not a Python guy, but perhaps this is working anyway ;)
my_dict = {
0: ('file_1.JPG', "Some text for file 1"),
1: ('file_2.jpg', "Different text for file 2"),
2: ('file_3.JPG', "More different text for file 3"),
3: ('file_4.JPG', "A fourth bit of text for file 4.")
}
comments = api.get_object('PAGE.NUMBER.HERE?fields=posts{comments}')
com_index = comments['posts']['data'][0]['comments']['data']
photo_id = my_dict[x][0]
my_image = 'file:///Users/filepath/{}'.format(photo_id)
photo = open(my_image)
text = my_dict[x][1]
api.put_object(com_index[com_index]['id'], "comments/comments", source=photo.read(), message=text)
photo.close()
I have some code which is supposed to render a file from svn into an email
command = 'svn cat -r {} "{}{}"'.format(svn_revision, svn_repo, filename)
file_content = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout.read()
context = {
'file_content': file_content,
}
print file_content
email = get_template('file_email/file_email.txt').render(Context(context))
print email
The email template is (for now) just:
{{file_content|safe}}
The first print statement dumps the file as expected.
The second print statement prints nothing.
The template is rendered, if I put words around the tag then they show up.
It only happens for some files (others work fine) and it seems to be an encoding problem. Notepad++ believes the offending files are encoded "UCS-2 Little Endian" whereas the ones that work are "UTF-8 without BOM".
How can I reliably dump a file, regardless of encoding, to a template?
I am currently loading my data into a variable (seen below as 'data') and then reading my template file and replacing %s with variables contained in 'data'. Here is my page reading, substitution, writing then displaying the new page on local server code:
def main
contents = makePage('varibletest.html', (data['Address'], data['Admin'], data['City'], data['ContractNo'], data['DealStatus'], data['Dealer'], data['Finance'], data['FinanceNumber'], data['First'], data['Last'], data['Message'], data['Notes'], data['Result'], data['SoldDate'], data['State'], data['Zip'])) # process input into a page
browseLocal(contents, 'Z:/xampp/htdocs/', 'SmartFormTest{}.php'.format((data['ContractNo']))) # display page
def fileToStr(fileName):
"""Return a string containing the contents of the named file."""
fin = open(fileName);
contents = fin.read();
fin.close()
return contents
def makePage(templateFileName, substitutions):
"""Returns a string with substitutions into a format string taken
from the named file. The single parameter substitutions must be in
a format usable in the format operation: a single data item, a
dictionary, or an explicit tuple."""
pageTemplate = fileToStr(templateFileName)
return pageTemplate % substitutions
def strToFile(text, savefile):
"""Write a file with the given name and the given text."""
output = file(savefile,"w")
output.write(text)
output.close()
def browseLocal(webpageText, path, filename):
"""Start your webbrowser on a local file containing the text."""
savefile = path + filename
strToFile(webpageText, savefile)
import webbrowser
b = webbrowser
b.open('192.168.1.254:1337/' + filename)
main()
Here is my template file (included is some silliness to demonstrate I have tried quite a few things to get this working):
%s
%s
%s
%s
%s
%s
%s.format(Address)
%s.format(data['Address'])
%s[2]
%s(2)
%s{2]
%s
%s
%s
%s
%s
When the new page is opened the variables are all there in sequential order. I need the ability to insert, say, address in multiple places.
Thanks in advance for your help!
EDIT --
Here's my new code with solution:
def main()
fin = open('DotFormatTemplate.html')
contents = fin.read();
output = contents.format(**data)
print output
main()
Template file:
I live at
Address: {Address}
Hope this makes someones life easier as it did mine!
Simple templates using string.format
Typical way of rendering simple template by means of string.format method:
data = {"Address": "Home sweet home", "Admin": "James Bond", "City": "London"}
template = """
I live at
Address: {Address}
in City of: {City}
and my admin is: {Admin}
"""
print template.format(**data)
what prints:
I live at
Address: Home sweet home
in City of: London
and my admin is: James Bond
The **data is needed to pass all data keywords and related values to the function.
Using Jinja2 for loopy templates
string.format is great in that it is part of Python standard library. However, as soon as you come to more complex data structure including lists and other iterables, string.format comes short or requires building output part by part, what makes soon your template broken to too many parts.
There are many other templating libraries, jinja2 being my favourite one:
$ pip install jinja2
Then we can play this way:
>>> from jinja2 import Template
>>> jdata = {'Name': 'Jan', 'Hobbies': ['Python', 'collecting principles', 'DATEX II']}
>>> templstr = """
... My name is {{ Name }} and my Hobbies are:
...
... {% for hobby in Hobbies %}
... - {{ hobby }}
... {% endfor %}
... """
>>> templ = Template(templstr)
>>> print templ.render(jdata)
My name is Jan and my Hobbies are:
- Python
- collecting principles
- DATEX II
With jinja2 there is no need to call templ.render(**jdata), but such call would also work.
Conclusions
Samples above shall give you initial idea, what can be done with templates and how they can be used.
In both cases there are many more functionalities provided by given solutions, just read doc and enjoy it.
I would like to list all strings within my large python project.
Imagine the different possibilities to create a string in python:
mystring = "hello world"
mystring = ("hello "
"world")
mystring = "hello " \
"world"
I need a tool that outputs "filename, linenumber, string" for each string in my project. Strings that are spread over multiple lines using "\" or "('')" should be shown in a single line.
Any ideas how this could be done?
unwind's suggestion of using the ast module in 2.6 is a good one. (There's also the undocumented _ast module in 2.5.) Here's example code for that
code = """a = 'blah'
b = '''multi
line
string'''
c = u"spam"
"""
import ast
root = ast.parse(code)
class ShowStrings(ast.NodeVisitor):
def visit_Str(self, node):
print "string at", node.lineno, node.col_offset, repr(node.s)
show_strings = ShowStrings()
show_strings.visit(root)
The problem is multiline strings. If you run the above you'll get.
string at 1 4 'blah'
string at 4 -1 'multi\nline\nstring'
string at 5 4 u'spam'
You see that it doesn't report the start of the multiline string, only the end. There's no good solution for that using the builtin Python tools.
Another option is that you can use my 'python4ply' module. This is a grammar definition for Python for PLY, which is a parser generator. Here's how you might use it:
import compiler
import compiler.visitor
# from python4ply; requires the ply parser generator
import python_yacc
code = """a = 'blah'
b = '''multi
line
string'''
c = u"spam"
d = 1
"""
tree = python_yacc.parse(code, "<string>")
#print tree
class ShowStrings(compiler.visitor.ASTVisitor):
def visitConst(self, node):
if isinstance(node.value, basestring):
print "string at", node.lineno, repr(node.value)
visitor = ShowStrings()
compiler.walk(tree, visitor)
The output from this is
string at 1 'blah'
string at 2 'multi\nline\nstring'
string at 5 u'spam'
There's no support for column information. (There is some mostly complete commented out code to support that, but it's not fully tested.) Then again, I see you don't need it. It also means working with Python's 'compiler' module, which is clumsier than the AST module.
Still, with a 30-40 lines of code you should have exactly what you want.
Python's included tokenize module will also do the trick.
from __future__ import with_statement
import sys
import tokenize
for filename in sys.argv[1:]:
with open(filename) as f:
for toktype, tokstr, (lineno, _), _, _ in tokenize.generate_tokens(f.readline):
if toktype == tokenize.STRING:
strrepr = repr(eval(tokstr))
print filename, lineno, strrepr
If you can do this in Python, I'd suggest starting by looking at the ast (Abstract Syntax Tree) module, and going from there.
Are you asking about the I18N utilities in Python?
http://docs.python.org/library/gettext.html#internationalizing-your-programs-and-modules
There's a utility called po-utils (formerly xpot) that can help with this.
http://po-utils.progiciels-bpi.ca/README.html
You may also consider to parse your code with
pygments.
I don't know the other solution, but it sure is very
simple to use.
Gettext might help you. Put your strings in _(...) structures:
a = _('Test')
b = a
c = _('Another text')
Then run in the shell prompt:
pygettext test.py
You'll get a messages.pot file with the information you need:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL#ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2009-02-25 08:48+BRT\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL#ADDRESS>\n"
"Language-Team: LANGUAGE <LL#li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: teste.py:1
msgid "Test"
msgstr ""
#: teste.py:3
msgid "Another text"
msgstr ""