What's more, it's being hailed at a plus for localization which it isn't. Localizers should never, ever deal with string interpolation - anything past what .format() does is essentially untranslatable.
What's more, it's being hailed at a plus for localization which it isn't. Localizers should never, ever deal with string interpolation - anything past what .format() does is essentially untranslatable.
>>> '%(one)s %(two)s'%{"one":"hello", "two":"world"}
'hello world'
>>> '{one} {two}'.format(one="hello", two="world")
'hello world'
The PEP that is looking at this problem has been deferred: at the end of the document they kind of indicate that they didn't really think about the i18n problem correctly and so don't actually have a good solution to present, and are going back to think about it more.Yes, having so many solutions is not ideal, espacially when it comes to teach the language. However, it would be foolish to avoid improving the usability of Python just to avoir having "one more way to do it".
I do wish they'd deprecate Template though. It's more than useless.
I am not sure why i18n is a big deal, let another library deal with it.
Whatever, I have the choice not using this feature after it is accepted and implemented. Their PEP discussion on email always ended up in tangential. Problem I always have with string manipulation is dealing with long string, which for coding style I'd split into multiple +, and thus using format is pretty ugly.
>>> (" foo "
... "bar.")
' foo bar.'That was annoying.
Is it really worth updating all the Python syntax formatting and analysis code out there just to save one character on an operator? I don't think it's a good tradeoff.
You got:
price = 18.8
date = datetime.datetime.now()
Now you want to save in a file: "It's 18.80 and we are the 01/04/2016"
With +: num, dec = str(price).split('.')
msg = ("It's "+ num + "." + dec.ljust(2, '0') +
" and we are the " + date.strftime("%m/%d/%Y") + "\n")
f.write(msg)
It is so long and ugly we have to break it on several lines to respect PEP8.And it's a pain to write, or even worst, to read.
With print():
num, dec = str(price).split('.')
print("It's", num, ".", dec.ljust(2, '0'),
" and we are the ", date.strftime("%m/%d/%Y")), file=f)
A tiny bit better since we don't have to use that many concatenation tricks.If we didn't have to format the float, print() would have converted it to string for us which is nice (with + you have to call str() all the time).
With "%":
f.write("It's %.2f and we are the %s\n" % (price, date.strftime("%m/%d/%Y")))
Way better, but still hard to read.With format():
f.write("It's {:.2f} and we are the {:%m/%d/%Y}\n".format(price, date))
Now we are getting sometwhere...With fstrings:
f.write("It's {price:.2f} and we are the {date:%m/%d/%Y}\n")
fstrings have everything: - easier / faster to type than any other versions;
- easier to read than any other versions;
- as expressive as any other versions.
It's reduce work by making you think less, type faster, read faster (for you and your colleague) and it's easier to spot bugs.The only place where you don't want fstring are for l10n.
For example, your i18n tool can extract:
"It's {price:.2f} and we are the {date:%m/%d/%Y}\n"
as a key, and then l10n can map this to: fr_FR: "C'est {price:.2f} et nous sommes le {date:%d/%m/%Y}\n"
(Here we can fix up the silly US date formatting, too, although really you shouldn't be localising dates in your format strings).All that remains is for them to work out some way of mapping in that localised string, as you can do this:
_("It's {price:.2f} and we are the {date:%m/%d/%Y}\n").format(price=price, date=date)
any more.ISO 8601 (similar to Japanese format) is the most logical way to format dates: YYYY-MM-DD, easily sortable, not the silly way US and Europeans format their dates. ;)
If you are a fan of ISO8601 (which I am), you can then set this globally on your desktop, rather than expect the app developers / translators to choose for you.
That's because some languages have complicated changes in the text depending on eg the number of things: not just singular/plural, but more complicated. Russian is one example.
In practice, what you need is a notion of a "context" for your formatting (often something that is aware of both the current i18n and l10n factors), and that context needs access to the fundamental objects that would be rendered in to a string.
Effectively, the "format string" becomes an identifier for the particular message you want to render, though often the objects themselves are definitive enough.
In that paradigm, interpolation/formatting/whatever might be the default mechanism employed by the context, but you don't want to have that as your explicit mechanism. You want one more level of indirection before you get to it.
Some platforms make the mistake of having that context be tied to the thread, out worse still, a global, and that falls apart once you have any multiplexing logic (and the way python currently works with posix locales fully captures how terribly you can do this). Either way, I'd argue out really is a different problem from interpolation, and if you are trying to solve it using interpolation, you don't understand the problem.
I generally localize for western languages and write in many programming languages. The combination of gettext + python format strings has been working really great for me, and generally much better than other systems I've seen and put to use. In fact, the simplicity of gettext provides a very fast turn-around, and with translators experienced with the tool I never had problems. Python format strings also work great in this context, as I can supply an arbitrary dictionary of elements that the translator might need. The only real problem has been plural forms in complex text strings, where ngettext is not always sufficient.
What method (name a project I can inspect) do you recommend as a good localization architecture?
l8n_context.format('file_not_found', file)
'file_not_found' is just an identifier to lookup the actual format template (likely a format string) that will be combined with the file object to render the error message.If translators are cooperating with you, it's very easy to provide the needed elements directly in the format's dictionary (that is: you extract the translatable pieces for them). It also means you don't have to worry that they're going to fiddle with mutable state.
I generally write everything in english, and do back-translation to my own locale (I also cooperate for translating external projects into my locale), so I eat my own dogfood here.
I know I do not want to deal with extra lower-level subtleties here. Translation is hard already by itself. It's impressive how a good translation of a simple UI can take so much time. If I had to inspect the object to know what I can get out of it I would get crazy.
I'd take a pre-baked dictionary any time.
I've also already used the string-catalog approach in the past (heh, XUL), and I'd personally take gettext any day.
At some point the translator will have to format some string himself.
(I think we are already agreeing, only that you express your point differently in Python specific terms. I don't care whether you stuff your code into a context object or something else. I was talking about having the full power of the programming language available, vs using a limited language like format strings.)
It also has little in common with i18n, the use cases differ too much. Perhaps in the future someone can figure out how to bring them together, but not today.
Go and Rust are two notable new languages which require explicit formatting functions as opposed to relying on special string interpolation syntax.