Skip to main content

Override translations by putting some .po files in the i18n directory of the zope 2 instance

Project description

collective.recipe.i18noverrides

This is a buildout recipe. It creates an i18n directory within one or more zope 2 instances in your buildout. It copies some .po files to those directories. The translations in those .po files will override any other translations.

Plone/Zope versions

This is tested for Plone 3 with Zope 2.10. It should work for a plain Zope site without Plone as well, as there is nothing Plone specific to this recipe. It should work fine for all previous versions as well.

For Plone 4 and Zope 2.12 it has no effect: there is no code anymore in those versions that looks for translations in an i18n folder of your instance. You should create an own package and register a locales directory with translation in it. For more info or questions, see the plone-internationalization mailing list.

Use case

An example use case is:

  • In the Dutch Plone translations the msgid ‘Manager’ is translated as ‘Beheerder’.

  • A customer wanted it to be translated as ‘Site admin’ instead.

  • Just putting this translation within the i18n directory of the customer product is not guaranteed to work as it depends on the order in which the i18n folders get read on Zope startup: is CMFPlone/i18n or Customer/i18n read first.

  • When you create an i18n directory within the zope 2 instance and add a po file with that msgid there, this is guaranteed to get used.

Note that this should work for overriding translations within i18n directories. Overriding translations in locales directories is not a use case of this recipe.

Contents of .po file

What should be in the .po file? You need all the headers that are normally in .po files. So copy the headers of the current .po file that has the translation that you want to override. Then just add the msgid and a new msgstr. The name of the file does not really matter. It should be meaningful to you and end with ‘.po’. In the mentioned use case it makes sense to call it plone-nl.po as that is the name of the original file from the plone translations. The contents would be something like this (non-interesting header lines skipped for clarity):

msgid ""
msgstr ""
...
"Language-Code: nl\n"
"Language-Name: Nederlands\n"
"Domain: plone\n"

msgid "Manager"
msgstr "Site admin"

Detailed Documentation

Supported options

The recipe supports the following options:

source

Source directory that contains the .po files that the recipe will copy. All *.po files will be copied. This option is mandatory.

egg

Egg that contains the source directory. If this option is mentioned, the source directory has to be a relative path.

package

Can be mentioned when source cannot be found in egg for one of the following reasons : egg holds a version specification; egg is not equal to the name of the installed package; source is in a subpackage.

destinations

Target directory or directories. This should point to the directory of the zope 2 instance. The recipe will create an i18n directory in each of the destinations and copy all *.po files from the source directory to these i18n directories. This option is mandatory.

Example usage

We’ll start by creating a buildout that uses the recipe. Here is a template where we only have to fill in the source and destinations:

>>> buildout_config_template = """
... [buildout]
... index = http://pypi.python.org/simple
... parts = i18noverrides
... versions = versions
...
... [versions]
... zc.buildout = 1.4.3
... zc.recipe.egg = 1.2.2
... setuptools = 0.6c11
... distribute = 0.6.14
...
... [i18noverrides]
... recipe = collective.recipe.i18noverrides
... source = %(source)s
... destinations = %(dest)s
... """

We will start with specifying some non existing source and destination directories:

>>> write('buildout.cfg', buildout_config_template % {
... 'source': '${buildout:directory}/translations',
... 'dest': '${buildout:directory}/instance'})

Running the buildout gives us:

>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/translations' does not exist. You must list the i18noverrides part after all plone.recipe.zope2instance parts.
<BLANKLINE>

The source must be a directory:

>>> write('translations', 'This is a file.')
>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/translations' must be a directory.
<BLANKLINE>

Now we remove this file and try with a proper directory:

>>> remove('translations')
>>> mkdir('translations')
>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/instance' does not exist. You must list the i18noverrides part after all plone.recipe.zope2instance parts.
<BLANKLINE>

So we set a destination too and first try with a file as well before creating a directory:

>>> write('instance', 'This is a file.')
>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/instance' must be a directory.
<BLANKLINE>
>>> remove('instance')
>>> mkdir('instance')
>>> print system(buildout)
Installing i18noverrides.
collective.recipe.i18noverrides: Warning: source '/sample-buildout/translations' contains no .po files.
<BLANKLINE>

Now the source and destination have been setup correctly, but we get a warning as the source directory has no translation files. We first add a file that does not end with .po. Since the previous buildout run only had a warning and finished successfully, the recipe now runs in update mode, which does the same as the install mode:

>>> write('translations', 'not-a-po-file', 'I am not a po file')
>>> print system(buildout)
Updating i18noverrides.
collective.recipe.i18noverrides: Warning: source '/sample-buildout/translations' contains no .po files.
<BLANKLINE>
>>> write('translations', 'plone-nl.po', 'I am a Dutch plone po file')
>>> write('translations', 'plone-de.po', 'I am a German plone po file')
>>> print system(buildout)
Updating i18noverrides.
collective.recipe.i18noverrides: Creating directory /sample-buildout/instance/i18n
collective.recipe.i18noverrides: Copied 2 po files.
<BLANKLINE>

No warnings, no errors, so let’s see what the end result is:

>>> ls('translations')
-  not-a-po-file
-  plone-de.po
-  plone-nl.po
>>> ls('instance')
d  i18n

A i18n directory has been created in the instance. Inside that directory we find our two po files:

>>> ls('instance', 'i18n')
-  plone-de.po
-  plone-nl.po
>>> cat('instance', 'i18n', 'plone-de.po')
I am a German plone po file
>>> cat('instance', 'i18n', 'plone-nl.po')
I am a Dutch plone po file

If the destination directory for some strange reason already contains a i18n file instead of a directory, we fail:

>>> remove('instance', 'i18n')
>>> write('instance', 'i18n', 'I am a file')
>>> print system(buildout)
Updating i18noverrides.
While:
  Updating i18noverrides.
Error: '/sample-buildout/instance/i18n' is not a directory.
<BLANKLINE>
>>> remove('instance', 'i18n')

It should also be possible to have multiple destinations:

>>> write('buildout.cfg', buildout_config_template % {
... 'source': '${buildout:directory}/translations',
... 'dest': """
...     ${buildout:directory}/instance
...     ${buildout:directory}/instance2"""})
>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/instance2' does not exist. You must list the i18noverrides part after all plone.recipe.zope2instance parts.
<BLANKLINE>

Right, right, we will create that directory too:

>>> mkdir('instance2')
>>> print system(buildout)
Installing i18noverrides.
collective.recipe.i18noverrides: Creating directory /sample-buildout/instance/i18n
collective.recipe.i18noverrides: Creating directory /sample-buildout/instance2/i18n
collective.recipe.i18noverrides: Copied 2 po files.
<BLANKLINE>

Let’s check the result:

>>> ls('instance')
d  i18n
>>> ls('instance', 'i18n')
-  plone-de.po
-  plone-nl.po
>>> ls('instance2')
d  i18n
>>> ls('instance2', 'i18n')
-  plone-de.po
-  plone-nl.po
>>> cat('instance2', 'i18n', 'plone-de.po')
I am a German plone po file
>>> cat('instance2', 'i18n', 'plone-nl.po')
I am a Dutch plone po file

Clean up:

>>> remove('instance')
>>> remove('instance2')

Integration with plone.recipe.zope2instance

As the recipe is normally used to add translations to zope 2 instances, it makes sense to search for buildout parts that setup zope instances and take those locations.

>>> write('buildout.cfg', """
... [buildout]
... index = http://pypi.python.org/simple
... parts = instance instance2 zeoclient i18noverrides
... versions = versions
...
... [versions]
... zc.buildout = 1.4.3
... zc.recipe.egg = 1.2.2
... setuptools = 0.6c11
... distribute = 0.6.14
... plone.recipe.zope2instance = 3.9
... mailinglogger = 3.3
...
... [i18noverrides]
... recipe = collective.recipe.i18noverrides
... source = ${buildout:directory}/translations
...
... [instance]
... recipe = plone.recipe.zope2instance
... user = admin:admin
...
... [instance2]
... recipe = plone.recipe.zope2instance
... user = admin:admin
...
... [zeoclient]
... recipe = plone.recipe.zope2instance
... user = admin:admin
... """)

We mock a mkzopeinstance script in the bin directory:

>>> write('bin/mkzopeinstance', """
... import sys
... import os
... path = sys.argv[2]
... os.mkdir(path)
... os.mkdir(os.path.join(path, 'etc'))
... """)

We do not want to install a complete zope2 instance in the tests, so we do not add it to the buildout parts. That does mean that running buildout now fails:

>>> print system(buildout)
Getting distribution for 'plone.recipe.zope2instance==3.9'.
...
Installing instance.
Generated script '.../bin/instance'.
...
Installing i18noverrides.
collective.recipe.i18noverrides: Creating directory .../i18n
collective.recipe.i18noverrides: Creating directory .../i18n
collective.recipe.i18noverrides: Creating directory .../i18n
collective.recipe.i18noverrides: Copied 2 po files.
<BLANKLINE>
>>> ls('parts', 'instance')
d etc
d i18n
>>> ls('parts', 'instance', 'i18n')
-  plone-de.po
-  plone-nl.po
>>> ls('parts', 'instance2', 'i18n')
-  plone-de.po
-  plone-nl.po
>>> ls('parts', 'zeoclient', 'i18n')
-  plone-de.po
-  plone-nl.po

If we explicitly specify destinations, the recipes are ignored.

>>> write('buildout.cfg', """
... [buildout]
... index = http://pypi.python.org/simple
... parts = dummy i18noverrides
... versions = versions
...
... [versions]
... zc.buildout = 1.4.3
... zc.recipe.egg = 1.2.2
... setuptools = 0.6c11
... distribute = 0.6.14
... plone.recipe.zope2instance = 3.9
... mailinglogger = 3.3
...
... [i18noverrides]
... recipe = collective.recipe.i18noverrides
... source = ${buildout:directory}/translations
... destinations = ${buildout:directory}/dest
...
... [dummy]
... recipe = plone.recipe.zope2instance
... user = admin:admin
... """)
>>> mkdir('dest')
>>> print system(buildout)
Uninstalling ...
Installing i18noverrides.
collective.recipe.i18noverrides: Creating directory /.../dest/i18n
collective.recipe.i18noverrides: Copied 2 po files.
<BLANKLINE>
>>> ls('parts', 'dummy')
d etc
>>> ls('dest', 'i18n')
-  plone-de.po
-  plone-nl.po

Clean up:

>>> remove('translations')

Usage with directory in egg

We start by creating a buildout that uses the recipe. Here is a template where we only have to fill in the source, egg and destinations:

>>> buildout_config_template = """
... [buildout]
... index = http://pypi.python.org/simple
... parts = i18noverrides
... versions = versions
...
... [versions]
... zc.buildout = 1.4.3
... zc.recipe.egg = 1.2.2
... setuptools = 0.6c11
... distribute = 0.6.14
... # We need to pin this one because it still needs to be uninstalled.
... # If we do not pin, the uninstall code will get the latest version,
... # which depends on Zope2, which means we are hosed...
... plone.recipe.zope2instance = 3.9
... mailinglogger = 3.3
...
... [i18noverrides]
... recipe = collective.recipe.i18noverrides
... source = %(source)s
... egg = %(egg)s
... destinations = %(dest)s
... """

We specify egg and source:

>>> write('buildout.cfg', buildout_config_template % {
... 'source': 'tests/translations',
... 'egg': 'collective.recipe.i18noverrides',
... 'dest': 'translations'})

We prepare target directory:

>>> mkdir('translations')

Running the buildout gives us:

>>> print system(buildout)
Uninstalling ...
Installing i18noverrides.
collective.recipe.i18noverrides: Creating directory translations/i18n
collective.recipe.i18noverrides: Copied 2 po files.
<BLANKLINE>

Let’s check the result:

>>> ls('translations')
d  i18n
>>> ls('translations', 'i18n')
-  test-fr.po
-  test-nl.po
>>> cat('translations', 'i18n', 'test-fr.po')
Un fichier .po
>>> cat('translations', 'i18n', 'test-nl.po')
Een .po bestand

We specify egg and an absolute path in source:

>>> write('buildout.cfg', buildout_config_template % {
... 'source': '/translations',
... 'egg': 'testegg',
... 'dest': 'translations'})

Running the buildout gives us:

>>> print system(buildout)
Uninstalling i18noverrides.
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: Because egg option is provided,
source '/translations' should be relative, not absolute.
<BLANKLINE>

We specify an egg` that does not hold the configured source:

>>> write('buildout.cfg', buildout_config_template % {
... 'source': 'translations',
... 'egg': 'zc.recipe.egg',
... 'dest': 'translations'})

Running the buildout gives us:

>>> print system(buildout)
Installing i18noverrides.
While:
  Installing i18noverrides.
Error: path '/sample-buildout/eggs/zc.recipe.egg.../zc/recipe/egg/translations' does not exist. You must list the i18noverrides part after all plone.recipe.zope2instance parts.
<BLANKLINE>

Contributors

Maurits van Rees, Author

Change history

History of collective.recipe.i18noverrides

1.2 (2013-01-23)

  • Raise zc.buildout.UserWarning in case of errors. This is how it should be done. It is more noticeable than logging an error (which may not be really visible as error) and quitting. [maurits]

1.1 (2012-09-13)

1.0 (2010-08-25)

  • Added a note to warn that this recipe will have no effect in Plone 4 (Zope 2.12) or higher. You should create an own package and register a locales directory there. [maurits]

0.4 (2009-09-08)

  • Only consider buildout parts for inclusion in the destinations when they are actually in the parts section of buildout. Otherwise you can run into errors like this: Error: Missing option: zptdebugger:__buildout_signature__ [maurits]

0.3 (2009-09-08)

  • If no destinations are specified, we automatically copy the po files to all parts that use plone.recipe.zope2instance. [maurits]

0.2 (2009-08-12)

  • Allow to specify an egg (with optional package) where the source directory can be found. [gotcha]

  • Pin packages (at least zc.buildout) in both the main buildout.cfg and the one in the tests, to avoid test failures simply because the used zc.buildout is upgraded during the test run. [maurits]

0.1 (2009-06-05)

  • Initial implementation moved over from a one-off script. [maurits]

  • Created recipe with ZopeSkel [Maurits van Rees]

Download

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

collective.recipe.i18noverrides-1.2.zip (29.3 kB view details)

Uploaded Source

File details

Details for the file collective.recipe.i18noverrides-1.2.zip.

File metadata

File hashes

Hashes for collective.recipe.i18noverrides-1.2.zip
Algorithm Hash digest
SHA256 e20618ae9219381dfd4d8f2b04d81cff0fe555e3585d9a4ec7b218ceb8eb562a
MD5 4e3dd3e44bfa96a355cde0a98d6397e3
BLAKE2b-256 97780073be3da2282c5bdd19a8265b53e606527e56bff1137f31612e7b1986c8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page