Software localization
Introduction to the gettext Tools for Internationalization
When we talk about internationalization (i18n), we refer to the process by which an application, or a set of applications turned into a package, is made aware of and able to support multiple languages. When we talk about localization (l10n) we mean the operation by which, in an application already internationalized, we make it adapt itself to handle its input and output in a fashion that is correct for some native language and cultural habits.
Internationalization is usually taken care of by developers, and localization is mostly performed by linguists.
In this tutorial, we'll have a look at how to use all available gettext tools in practice with a small Python application. Our goal is to get a better understanding of how to best internationalize and localize it so both the developer and linguist can benefit from an efficient workflow.
🔗 Resource » Learn how to localize form validation in an example app and provide valuable error messages for your users in our detailed guide.
Overview of gettext
The GNU gettext package is a set of tools that handle i18n and l10n aspects in computer programs with the ultimate purpose of minimizing the impact of them and hardly noticeable as possible. With gettext we mainly work with PO and MO files and variations of them.
PO files are meant to be read and edited by humans and MO files are meant to be read by programs and are binary in nature. One other characteristic of MO files is that they are often different from system to system; non-portable in nature.
A single PO file is dedicated to a single target language. If a package supports many languages, there is one such PO file per language supported, and each package has its own set of PO files. Files ending with .pot are kind of base translation files found in distributions, in PO file format.
Before we start with our tutorial, we need to make sure we have installed gettext on our system:
Fedora
$ dnf install gettext-devel intltool
Ubuntu
$ sudo apt-get install gettext
Mac
$ brew install gettext $ brew link --force gettext
Windows
https://mlocati.github.io/articles/gettext-iconv-windows.html
Example Project
Before we delve into the tooling it's important to start with an example project that we would like to include i18n and l10n support using gettext. We are using a small Python application that resembles an Online Bank with operations such as: Creating a new Bank account, printing your current statements, depositing some money or transferring funds between existing accounts.
1. Create a main.py file and add the following code:
$ touch main.py
File: main.py
import uuid class BankAccount(object): def __init__(self, initial_balance=0): self.balance = initial_balance self.id = str(uuid.uuid4()) print("Bank account '{id}' was created with initial Balance: {balance}").format(id=self.id, balance=self.balance)) def deposit(self, amount): self.balance += amount print("Deposited {amount} to current balance").format(amount=amount)) def withdraw(self, amount): self.balance -= amount print("Withdrawned {amount} from current balance").format(amount=amount)) def overdrawn(self): return self.balance < 0 def print_balance(self): print("Balance for Account '{id}' is: {balance}"). format(id=self.id, balance=self.balance)) class Bank(object): bank_accounts = [] def create_account(self, initial_balance=0): new_account = BankAccount(initial_balance) self.bank_accounts.append(new_account) return new_account def list_accounts(self): return self.bank_accounts def transfer_balance(self, from_acccount_id, to_account_id, amount): from_account = self.get_account_by_id(from_acccount_id) to_account = self.get_account_by_id(to_account_id) if from_account is None or to_account is None: print("One of the Account numbers does not exist") return from_account.withdraw(amount) to_account.deposit(amount) print("Successfully transfered {amount} from Account '{from_acccount_id}' to Account: {to_account_id}"). format(amount=amount, from_acccount_id=from_acccount_id, to_account_id=to_account_id)) def get_account_by_id(self, id_param): accounts = [acc for acc in self.bank_accounts if acc.id == id_param] if len(accounts) == 1: return accounts[0] else: return None if __name__ == '__main__': bank = Bank() first = bank.create_account(100) second = bank.create_account(150) bank.transfer_balance(first.id, second.id, 50) first.print_balance() second.print_balance()
2. Run the program to see the result in the console:
$ python main.py Bank account '22fc68d4-ac7e-401b-ad24-11d86d09979b' was created with initial Balance: 100 Bank account '4a28287c-c07a-4574-a97e-bd749d21605e' was created with initial Balance: 150 Withdrawned 50 from current balance Deposited 50 to current balance Successfully transfered 50 from Account '22fc68d4-ac7e-401b-ad24-11d86d09979b' to Account: 4a28287c-c07a-4574-a97e-bd749d21605e Balance for Account '22fc68d4-ac7e-401b-ad24-11d86d09979b' is: 50 Balance for Account '4a28287c-c07a-4574-a97e-bd749d21605e' is: 200
So far so good.
Let's say now that the upper management requests to add i18n capabilities so we can support multiple locales for the messages printed. They have assigned you to this task, with the need that you make it easy to use by translators and maintainers.
The first thing you need to do is to mark all strings used for gettext translations and make them a bit easier to read. Python and a few programming languages support gettext. We only need to import the relevant library and use the message format function:
3. Import the gettext library and use the relevant format function to all translatable strings:
import uuid import gettext _ = gettext.gettext class BankAccount(object): def __init__(self, initial_balance=0): self.balance = initial_balance self.id = str(uuid.uuid4()) print(_("Bank account '{id}' was created with initial Balance: {balance}").format(id=self.id, balance=self.balance)) def deposit(self, amount): self.balance += amount print(_("Deposited {amount} to current balance").format(amount=amount)) def withdraw(self, amount): self.balance -= amount print(_("Withdrawned {amount} from current balance").format(amount=amount)) def overdrawn(self): return self.balance < 0 def print_balance(self): print(_("Balance for Account '{id}' is: {balance}"). format(id=self.id, balance=self.balance)) class Bank(object): bank_accounts = [] def create_account(self, initial_balance=0): new_account = BankAccount(initial_balance) self.bank_accounts.append(new_account) return new_account def list_accounts(self): return self.bank_accounts def transfer_balance(self, from_acccount_id, to_account_id, amount): from_account = self.get_account_by_id(from_acccount_id) to_account = self.get_account_by_id(to_account_id) if from_account is None or to_account is None: print(_("One of the Account numbers does not exist")) return from_account.withdraw(amount) to_account.deposit(amount) print(_("Successfully transfered {amount} from Account '{from_acccount_id}' to Account: {to_account_id}"). format(amount=amount, from_acccount_id=from_acccount_id, to_account_id=to_account_id)) def get_account_by_id(self, id_param): accounts = [acc for acc in self.bank_accounts if acc.id == id_param] if len(accounts) == 1: return accounts[0] else: return None if __name__ == '__main__': bank = Bank() first = bank.create_account(100) second = bank.create_account(150) bank.transfer_balance(first.id, second.id, 50) first.print_balance() second.print_balance()
If you run the same program again you will see that nothing really has changed. You are now able to use the gettext tools to translate the files.
Let's start with creating the .pot file
Extracting POT files with xgettext
The xgettext program finds and extract all marked translatable strings, and creates a PO template file out of all these. If you run it without any arguments it will create a file named domainname.po . This file should be used as a basis for all later locale-specific translations as it has all the original program strings. Initially, all strings are empty and they contain only the msgid's, that is the unique message keys.
We would like to rename this file using a .pot extension (Template PO file) and place it in the locale folder:
$ mkdir locale $ xgettext main.py -d messages -p locale $ mv locale/messages.po locale/messages.pot
The xgettext tool can be called with the following format:
$ xgettext [option] [inputfile] …
and in that case, we use the -d flag to specify the language domain and the -p flag to specify the output folder.
Let's inspect the contents of this file:
File: locale/messages.pot
# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-05-28 16:05+0100\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" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: main.py:15 #, python-brace-format msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "" #: main.py:19 #, python-brace-format msgid "Deposited {amount} to current balance" msgstr "" #: main.py:23 #, python-brace-format msgid "Withdrawned {amount} from current balance" msgstr "" #: main.py:29 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "" #: main.py:50 msgid "One of the Account numbers does not exist" msgstr "" #: main.py:56 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr ""
We won't be touching this file for now as for new translations we need to make a copy of it and fill the initial metadata strings. However as you can see the program was able to add extra information about the specific placeholder format using the #, python-brace-format comment.
Let's see now how can a translator can use that .pot file to provide translations for a new language.
Creating PO files with msginit
A new translator comes in and wants to start a new translation for a specific locale. The first thing we need do is to copy that .pot file that we created earlier and change the metadata to show the specific locale info.
The easiest way to do that is with the help of msginit program. For example:
$ msginit -i locale/messages.pot --locale=el_GR -o locale/el/LC_MESSAGES/messages.po
The first time you invoke that command you will have to specify an email address for giving feedback about the translations.
In that case, we use the -i flag to reference the .pot file that we created earlier as base translation file, the --locale to specify the target locale and the -o flag to specify the output file.
Let's inspect the contents of this file:
File: locale/el/LC_MESSAGES/messages.po
# Greek translations for PACKAGE package. # Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # IT Spare <itspare@ccc.com>, 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-05-28 16:05+0100\n" "PO-Revision-Date: 2018-05-28 16:21+0100\n" "Last-Translator: IT Spare <itspare@ccc.com>\n" "Language-Team: Greek\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: main.py:15 #, python-brace-format msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "" #: main.py:19 #, python-brace-format msgid "Deposited {amount} to current balance" msgstr "" #: main.py:23 #, python-brace-format msgid "Withdrawned {amount} from current balance" msgstr "" #: main.py:29 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "" #: main.py:50 msgid "One of the Account numbers does not exist" msgstr "" #: main.py:56 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr ""
As you can see the program added a few locale-specific information about the target language and copied all the strings from the .pot input file. You could give more info but in most cases, you are good to go. Just provide the relevant translations:
... #: main.py:12 #, python-brace-format msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "Τραπεζικός λογαριασμός με κωδικό '{id}' δημιουργήθηκε με αρχικό κεφάλαιο: {balance}" #: main.py:16 #, python-brace-format msgid "Deposited {amount} to current balance" msgstr "Το ποσό των {amount} κατατέθηκε στο τρέχων κεφάλαιο" #: main.py:20 #, python-brace-format msgid "Withdrawned {amount} from current balance" msgstr "Το ποσό των {amount} αποσύρθηκε από το τρέχων κεφάλαιο" #: main.py:26 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό '{id}' είναι: {balance}" #: main.py:47 msgid "One of the Account numbers does not exist" msgstr "Αυτός ο τραπεζικός λογαριασμός δέν υπάρχει" #: main.py:53 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr "Μεταφέρθηκε το ποσό των {amount} απο τον λογαριασμό με κωδικό '{from_acccount_id}' στον λογαριασμό με με κωδικό '{to_account_id}'"
Now add the following 2 lines in the main.py to activate the Greek translations:
File: main.py
import uuid import gettext el = gettext.translation('messages', localedir='locale', languages=['el:en']) el.install() _ = el.gettext ...
Now before we can actually run this program we need to generate the .mo files from the .po files as this is the only way that our little Python program can do to recognize our translations.
Turning PO files into MO files with msgfmt
As we mentioned earlier we need .mo files to use our translations and there is a tool for that:
The msgfmt program generates a binary message catalog from a message catalog.
The calling format of this program is:
$ msgfmt [option] filename.po
Let's use it now to generate that file from the Greek translations:
$ msgfmt locale/el/LC_MESSAGES/messages.po -o locale/el/LC_MESSAGES/messages.mo
In the command above we used the -o flag to specify the output file.
Note: There is also a tool that performs the inverse operation. The msgunfmt program attempts to convert a binary message catalog back to a .po file.
Now we are ready to see the correct translations. Execute the program to see the results:
$ python main.py Τραπεζικός λογαριασμός με κωδικό '8a26b9d5-b26d-4a33-bd94-5893225cd5d0' δημιουργήθηκε με αρχικό κεφάλαιο: 100 Τραπεζικός λογαριασμός με κωδικό 'f9120696-3fcb-4575-af47-489d14d4207b' δημιουργήθηκε με αρχικό κεφάλαιο: 150 Το ποσό των 50 αποσύρθηκε Το ποσό των 50 κατατέθηκε Μεταφέρθηκε το ποσό των 50 απο τον λογαριασμό με κωδικό '8a26b9d5-b26d-4a33-bd94-5893225cd5d0' στον λογαριασμό με με κωδικό 'f9120696-3fcb-4575-af47-489d14d4207b' Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό '8a26b9d5-b26d-4a33-bd94-5893225cd5d0' είναι: 50 Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό 'f9120696-3fcb-4575-af47-489d14d4207b' είναι: 200
As the application now evolves we need to intervene from time to time as new untranslated entries are added or removed and strings become not relevant.
Let's see now how can a translator can use the msgmerge tool to handle those translation needs.
Updating PO files with msgmerge
The msgmerge tool is mainly used for existing .po files and especially existing translations. If our application updates its base extracted messages from the .pot file, we need to be able to update the relevant entries in the .po files we have.
Let's simulate that now to see how this tool does that in practice.
1. Modify the main.py and add a new message string then remove some of them and change one of them:
File: main.py
import uuid import gettext el = gettext.translation('messages', localedir='locale', languages=['el:en']) el.install() _ = el.gettext class BankAccount(object): def __init__(self, initial_balance=0): self.balance = initial_balance self.id = str(uuid.uuid4()) print(_("Bank account '{id}' was created and the initial balance is: {balance}").format(id=self.id, balance=self.balance)) def deposit(self, amount): self.balance += amount def withdraw(self, amount): self.balance -= amount def overdrawn(self): return self.balance < 0 def print_balance(self): print(_("Balance for Account '{id}' is: {balance}"). format(id=self.id, balance=self.balance)) class Bank(object): bank_accounts = [] def create_account(self, initial_balance=0): new_account = BankAccount(initial_balance) self.bank_accounts.append(new_account) return new_account def list_accounts(self): return self.bank_accounts def transfer_balance(self, from_acccount_id, to_account_id, amount): from_account = self.get_account_by_id(from_acccount_id) to_account = self.get_account_by_id(to_account_id) if from_account is None or to_account is None: print(_("One of the Account numbers does not exist")) return from_account.withdraw(amount) to_account.deposit(amount) print(_("Successfully transfered {amount} from Account '{from_acccount_id}' to Account: {to_account_id}"). format(amount=amount, from_acccount_id=from_acccount_id, to_account_id=to_account_id)) def get_account_by_id(self, id_param): accounts = [acc for acc in self.bank_accounts if acc.id == id_param] if len(accounts) == 1: return accounts[0] else: return None def print_bank_info(self): print(_("Bank has {num_accounts} accounts"). format(num_accounts=len(self.bank_accounts))) if __name__ == '__main__': bank = Bank() first = bank.create_account(100) second = bank.create_account(150) bank.transfer_balance(first.id, second.id, 50) first.print_balance() second.print_balance() bank.print_bank_info()
We have removed the messages from the deposit and withdraw methods, we added a new message on the print_bank_info and modified the existing message on the BankAccount constructor.
2. Replace the old .pot file using the xgettext tool
$ xgettext main.py -d messages -p locale $ mv locale/messages.po locale/messages.pot
3. Use the msgmerge tool to update the relevant .po files using the new .pot template
$ msgmerge locale/el/LC_MESSAGES/messages.po locale/messages.pot -o locale/el/LC_MESSAGES/messages.po $ msgmerge locale/en/LC_MESSAGES/messages.po locale/messages.pot -o locale/en/LC_MESSAGES/messages.po
The calling format of this tool is:
$ msgmerge [option] def.po ref.pot
In the example invocation, we used the -o flag to specify the output file.
Let's inspect the messages.po file for the Greek translations to see what was changed
File: locale/el/LC_MESSAGES/messages.po
... #: main.py:15 #, fuzzy, python-brace-format msgid "Bank account '{id}' was created and the initial balance is: {balance}" msgstr "" "Τραπεζικός λογαριασμός με κωδικό '{id}' δημιουργήθηκε με αρχικό κεφάλαιο: " "{balance}" #: main.py:27 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "" "Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό '{id}' είναι: " "{balance}" #: main.py:48 msgid "One of the Account numbers does not exist" msgstr "Αυτός ο τραπεζικός λογαριασμός δέν υπάρχει" #: main.py:54 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr "" "Μεταφέρθηκε το ποσό των {amount} απο τον λογαριασμό με κωδικό " "'{from_acccount_id}' στον λογαριασμό με με κωδικό '{to_account_id}'" #: main.py:66 #, python-brace-format msgid "Bank has {num_accounts} accounts" msgstr "" #~ msgid "Deposited {amount} to current balance" #~ msgstr "Το ποσό των {amount} κατατέθηκε στο τρέχων κεφάλαιο" #~ msgid "Withdrawned {amount} from current balance" #~ msgstr "Το ποσό των {amount} αποσύρθηκε από το τρέχων κεφάλαιο"
Here is the summary of the changes observed:
- A fuzzy comment was added to the entry that was updated. Fuzzy translations or entries account for messages that need revision by the translator as the program cannot decide if the meaning has remained the same or it has changed
- Deleted entries were commented out. This is to show that those entries are no longer available for display.
- Newly created entries were just placed in the right spot and based on the line number.
The job of the translator now is much easier as he has a lot of information about the status of the changes and what has to do.
Let's see now how can we find duplicate entries.
Finding duplicate entries with msguniq
Sometimes when merging or manipulating .po files using the above tools, you may find that some of the messages have the same id string. In order to find and highlight those keys, we can use the msguniq tool.
The calling format of this tool is:
$ msguniq [option] [inputfile]
Let's add a duplicate key and run this tool.
1. Modify the locale/el/LC_MESSAGES/messages2.po file and add the following lines:
File: locale/el/LC_MESSAGES/messages2.po
... msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "Τραπεζικός λογαριασμός με κωδικό '{id}' δημιουργήθηκε με αρχικό κεφάλαιο: {balance}" ...
2. Run the msguniq tool and inspect the output
$ msguniq locale/el/LC_MESSAGES/messages.po -d # Greek translations for PACKAGE package. # Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # IT Spare <itspare@c02pv2x1fvh6>, 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-05-15 15:46+0100\n" "PO-Revision-Date: 2018-05-15 15:52+0100\n" "Last-Translator: IT Spare <itspare@c02pv2x1fvh6>\n" "Language-Team: Greek\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: main.py:12 #, python-brace-format msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "" "Τραπεζικός λογαριασμός με κωδικό '{id}' δημιουργήθηκε με αρχικό κεφάλαιο: " "{balance}"
We used the -d flag to indicate that we want only the duplicate keys to be printed.
Note: This will not work if you have the same message id but with different placeholder parameters. For example, if you have this msgid:
msgid "Bank account '{idParam}' was created with initial Balance: {balanceParam}"
then it would be considered as having a different key and will not be picked up.
The benefit of this tool is that it gives a nice color output and it can work well with multiple domains.
There is also a similar tool called msgcomm that performs a similar job but with a different perspective. It checks two .po files and finds their common messages. Either case with those tools we should be able to identify duplicate entries.
The last tool for this article is the msgcat utility, that can help us concatenate 2 or more .po files into a single one.
Concatenating PO Files with msgcat
If we have a set of .po files in different packages or project and we would like to combine them we can use the msgcat tool. It will perform a search to find the common messages first and remove duplicates before creating the output file so the .po file that was created will also be valid.
Let's use that in practice.
1. Create a new .po file in locale/el/LC_MESSAGES folder called messages2.po and move half of the messages from the messages.po file. Make sure also you leave at least one common entry between those 2 files.
$ touch locale/el/LC_MESSAGES/messages2.po
File: locale/el/LC_MESSAGES/messages2.po
# Greek translations for PACKAGE package. # Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # IT Spare <itspare@c02pv2x1fvh6>, 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-05-15 15:46+0100\n" "PO-Revision-Date: 2018-05-15 15:52+0100\n" "Last-Translator: IT Spare <itspare@c02pv2x1fvh6>\n" "Language-Team: Greek\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: main.py:20 #, python-brace-format msgid "Withdrawned {amount} from current balance" msgstr "Το ποσό των {amount} αποσύρθηκε από το τρέχων κεφάλαιο" #: main.py:26 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό '{id}' είναι: {balance}" #: main.py:47 msgid "One of the Account numbers does not exist" msgstr "Αυτός ο τραπεζικός λογαριασμός δέν υπάρχει" #: main.py:53 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr "Μεταφέρθηκε το ποσό των {amount} απο τον λογαριασμό με κωδικό '{from_acccount_id}' στον λογαριασμό με με κωδικό '{to_account_id}'"
2. Use the msgcat tool to concatenate those files together.
$ msgcat locale/el/LC_MESSAGES/messages.po locale/el/LC_MESSAGES/messages2.po -o locale/el/LC_MESSAGES/messages3.po
File: locale/el/LC_MESSAGES/messages3.po
# Greek translations for PACKAGE package. # Copyright (C) 2018 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # IT Spare <itspare@c02pv2x1fvh6>, 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-05-15 15:46+0100\n" "PO-Revision-Date: 2018-05-15 15:52+0100\n" "Last-Translator: IT Spare <itspare@c02pv2x1fvh6>\n" "Language-Team: Greek\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: main.py:12 #, python-brace-format msgid "Bank account '{id}' was created with initial Balance: {balance}" msgstr "" "Τραπεζικός λογαριασμός με κωδικό '{id}' δημιουργήθηκε με αρχικό κεφάλαιο: " "{balance}" msgid "Bank account '{param}' was created with initial Balance: {balance}" msgstr "" "Τραπεζικός λογαριασμός με κωδικό '{param}' δημιουργήθηκε με αρχικό κεφάλαιο: " "{balance}" #: main.py:16 #, python-brace-format msgid "Deposited {amount} to current balance" msgstr "Το ποσό των {amount} κατατέθηκε στο τρέχων κεφάλαιο" #: main.py:20 #, python-brace-format msgid "Withdrawned {amount} from current balance" msgstr "Το ποσό των {amount} αποσύρθηκε από το τρέχων κεφάλαιο" #: main.py:26 #, python-brace-format msgid "Balance for Account '{id}' is: {balance}" msgstr "" "Το τρέχων κεφάλαιο του τραπεζικόυ λογαριασμού με κωδικό '{id}' είναι: " "{balance}" #: main.py:47 msgid "One of the Account numbers does not exist" msgstr "Αυτός ο τραπεζικός λογαριασμός δέν υπάρχει" #: main.py:53 #, python-brace-format msgid "" "Successfully transfered {amount} from Account '{from_acccount_id}' to " "Account: {to_account_id}" msgstr "" "Μεταφέρθηκε το ποσό των {amount} απο τον λογαριασμό με κωδικό " "'{from_acccount_id}' στον λογαριασμό με με κωδικό '{to_account_id}'"
As you can see the tool removed any duplicates and combined the messages in one file correctly.
You can also use the -u flag to specify that you want to keep only the unique entries, thus it will work just like the msguniq tool.
🔗 Resource » Find out more about alternative ways to combine two PO files together in our compact guide.
Alas, most of the tools that we discussed in this article have myriads of flags and options so each case usage is different. If you want to learn in detail about the full spectrum of this library you can visit the official page here. Hopefully, though this article has shown the best practical applications of each tool and you won't have to invest more time on that.
Use Phrase
Phrase supports many different languages and frameworks, including gettext. It allows you to easily import and export translations data and search for any missing translations, which is really convenient. On top of that, you can collaborate with translators as it is much better to have professionally done localization for your website. If you’d like to learn more about Phrase, refer to the Phrase Localization Platform.
Conclusion
I hope with this tutorial to have tempted your interest enough and given you more practical examples of how to use the gettext collection of programs. Stay put for more future articles related to this topic.