Skip to main content

i18nice tests Coverage Status pypi badge Anaconda-Server Badge

This library provides i18n functionality for Python 3 out of the box. The usage is mostly based on Rails i18n library.

CHANGELOG

CONTRIBUTING

Installation

Just run

pip install i18nice

If you want to use YAML to store your translations, use

pip install i18nice[YAML]

Usage

Basic usage

The simplest, though not very useful usage would be

import i18n
i18n.add_translation('foo', 'bar')
i18n.t('foo') # bar

Using translation files

YAML and JSON formats are supported to store translations. With the default configuration, if you have the following foo.en.yml file

en:
  hi: Hello world !

Or a JSON file foo.en.json

{
  "en": {
    "hi": "Hello world !"
  }
}

in /path/to/translations folder, you simply need to add the folder to the translations path.

import i18n
i18n.load_path.append('/path/to/translations')
i18n.t('foo.hi') # Hello world !

Please note that YAML format is used as default file format if you have yaml module installed. If both yaml and json modules available and you want to use JSON to store translations, explicitly specify that: i18n.set('file_format', 'json')

!WARNING! yaml.FullLoader is no longer used by default. If you need full yaml functionalities, override it with a custom loader:

class MyLoader(i18n.loaders.YamlLoader):
    loader = yaml.FullLoader

i18n.register_loader(MyLoader, ["yml", "yaml"])

Memoization

The configuration value enable_memoization (True by default) disables reloading of files every time when searching for missing translation. When translations are loaded, they're always stored in memory, hence it does not affect how existing translations are accessed.

Load everything

i18n.load_everything() will load every file in load_path and subdirectories that matches filename_format and file_format. You can call it with locale argument to load only one locale.

i18n.unload_everything() will clear all caches.

i18n.reload_everything() is just a shortcut for unload_everything() followed by load_everything().

For the best performance, you can pass lock=True to load_everything() to disable searching for missing translations completely. It'll prevent slowdowns caused by missing translations, but you'll need to use unload_everything() to be able to load files again.

Namespaces

File namespaces

In the above example, the translation key is foo.hi and not just hi. This is because the translation filename format is by default {namespace}.{locale}.{format}, so the {namespace} part of the file is used as translation.

To remove {namespace} from filename format please change the filename_format configuration.

i18n.set('filename_format', '{locale}.{format}')

Directory namespaces

If your files are in subfolders, the foldernames are also used as namespaces, so for example if your translation root path is /path/to/translations and you have the file /path/to/translations/my/app/name/foo.en.yml, the translation namespace for the file will be my.app.name and the file keys will therefore be accessible from my.app.name.foo.my_key.

Functionalities

Placeholder

You can of course use placeholders in your translations. With the default configuration, the placeholders are used by inserting %{placeholder_name} in the translation string. Here is a sample usage.

i18n.add_translation('hi', 'Hello %{name} !')
i18n.t('hi', name='Bob') # Hello Bob !

Braces are optional if the identifier is separated from following words.

To escape the delimiter you need to put it twice (like %%).

Pluralization

Pluralization is based on Rail i18n module. By passing a count variable to your translation, it will be pluralized. The translation value should be a dictionary with at least the keys one and many. You can add a zero or few key when needed, if it is not present many will be used instead. Here is a sample usage.

i18n.add_translation('mail_number', {
    'zero': 'You do not have any mail.',
    'one': 'You have a new mail.',
    'few': 'You only have %{count} mails.',
    'many': 'You have %{count} new mails.'
})
i18n.t('mail_number', count=0) # You do not have any mail.
i18n.t('mail_number', count=1) # You have a new mail.
i18n.t('mail_number', count=3) # You only have 3 new mails.
i18n.t('mail_number', count=12) # You have 12 new mails.

Fallback

You can set a fallback which will be used when the key is not found in the default locale.

i18n.set('locale', 'jp')
i18n.set('fallback', 'en')
i18n.add_translation('foo', 'bar', locale='en')
i18n.t('foo') # bar

Note that setting locale and fallback to the same value will result in fallback being None.

Skip locale from root

Sometimes i18n structure file came from another project or not contains root element with locale eg. en name.

{
  "foo": "FooBar"
}

However, we would like to use this i18n .json file in our Python sub-project or micro service as base file for translations. i18nice has special configuration that is skipping locale eg. en root data element from the file.

i18n.set('skip_locale_root_data', True)

Different directory structure

If your tree of translation files looks similar to this, you can enable use_locale_dirs to get the files properly loaded:

locales
├── en-US
│   ├── common.yml
│   └── gui
│       ├── page1.yml
│       └── page2.yml
└── fr-FR
    ├── common.yml
    └── gui
        ├── page1.yml
        └── page2.yml

Example code:

import i18n

i18n.load_path.append("locales")
i18n.set("file_format", "yml")
i18n.set("filename_format", "{namespace}.{format}")
i18n.set("skip_locale_root_data", True)
i18n.set("use_locale_dirs", True)

print(i18n.t("common.text1", locale="en-US"))
print(i18n.t("gui.page1.title", locale="en-US"))

Lists

It's possible to use lists of translations, for example:

# translations.en.yml
en:
  days:
    - Monday
    - Tuesday
    - Wednesday
    ...
# translate.py
from datetime import date
import i18n

i18n.load_path.append(".")
# will print current day
print(i18n.t("translations.days")[date.today().weekday()])

It's also possible to use pluralization in lists:

days:
  - one: Monday
    many: Mondays
  ...

Note 1: The function actually returns a LazyTranslationTuple instead of list.

Note 2: Because the tuple is lazy, it'll only process elements when they're requested. If you need to get fully processed translation, you can force it with [:]:

# ({'one': 'Monday', 'many': 'Mondays'}, {'one': 'Tuesday', 'many': 'Tuesdays'}, ...)
print(i18n.t("translations.days", count=3))
# ('Mondays', 'Tuesdays', 'Wednesdays')
print(i18n.t("translations.days", count=3)[:])

Note 3 (for type checking): t declares its return type as str by default. To simplify type checking in situations where lists are used, you can pass _list=True to it, which should have less overhead than calling cast and be less intrusive than type: ignore comment. This will NOT affect actual return type and is purely for type checkers.

Static references

Static references allow you to refer to other translation values. This can be useful to avoid repetition. To create a static reference, simply put a key prefixed with namespace delimiter to a placeholder. For example:

{
  "en": {
    "progname": "Program Name",
    "welcome": "Welcome to %{.progname}!"
  }
}

Note that you don't need to specify the absolute key:

{
  "en": {
    "interface": {
      "progname": "Program Name",
      "ref": "%{.progname} and %{.interface.progname} refer to the same value"
    }
  }
}

To be exact, keys are searched from top to bottom. For example, if you referred to .c.my_key in a.b.c.d, the library will first check for c.my_key, then a.c.my_key, and finally find a.b.c.my_key if it's present. If not, it'll try to search c.my_key in other files and throw an exception if that also fails.

Error handling

There are three config options for handling different situations. Setting it to None disables handling (default), "error" enables error throwing. You can also set your custom handlers:

on_missing_translation(key, locale, **kwargs)

on_missing_plural(key, locale, translation, count)

on_missing_placeholder(key, locale, translation, placeholder)

Example:

import logging, i18n

def handler(key, locale, text, name):
    logging.warning(f"Missing placeholder {name!r} while translating {key!r} to {locale!r} (in {text!r})")
    return "undefined"

i18n.set("on_missing_placeholder", handler)
i18n.add_translation("am", "Amount is %{amount}")
print(i18n.t("am"))
# output:
# WARNING:root:Missing placeholder 'amount' while translating 'am' to 'en' (in 'Amount is %{amount}')
# Amount is undefined

Custom functions

Add your custom functions and choose translation variants during runtime. All arguments given to t would be passed to the function as keyword arguments. This may be an alternative for pluralization, especially if a language has more than one plural form.

Example (correct plural form of days in Ukrainian):

i18n.set("locale", "uk")
i18n.add_translation("days", "%{count} %{p(день|дні|днів)}")

def determine_plural_form(*, count):
    count = abs(count)
    if count % 10 >= 5 or count % 10 == 0 or (count % 100) in range(11, 20):
        return 2
    elif count % 10 == 1:
        return 0
    return 1

i18n.add_function("p", determine_plural_form, "uk")
i18n.t("days", count=1) # 1 день
i18n.t("days", count=2) # 2 дні
# i18n.t("days", count=5) # 5 днів

Testing

You can run tests with python dev-helper.py run-tests.

Metadata

Release files for i18nice 0.13.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for i18nice 0.13.0
File Size Uploaded
i18nice-0.13.0.tar.gz 21.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for i18nice 0.13.0
File Interpreter ABI Platform
i18nice-0.13.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.4 kB

Release files / i18nice-0.13.0.tar.gz

Download URL i18nice-0.13.0.tar.gz
Size 21.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5b72b3030f4937706604d7e42194ec57a6d71e989c6ce3802770c35ed6c75803
BLAKE2b-256 checksum
How to use checksums
20155ffdad20538a042c6419f6d049da2800f960a2a36a66fc8efd5ef9cecda4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.12.1

Release files / i18nice-0.13.0-py3-none-any.whl

Download URL i18nice-0.13.0-py3-none-any.whl
Size 19.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d80f666b7dec8317356c3e07de7162f1c9bda82faf6b312d79598709a8ceb96b
BLAKE2b-256 checksum
How to use checksums
12c8afd8a282c39e33e9361db758fd566c469d0e8988a2b3cb344db8cfa4a520
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.12.1

Release history Release notifications | RSS feed

0.16.0

2 release files

0.15.5

2 release files

0.15.4

2 release files

0.15.3

2 release files

0.15.2

2 release files

0.13.3

2 release files

This release

0.13.0 This release

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page