# Localization and Internationalization Localization and internationalization are important aspects of the **addons-server** project, ensuring that the application can support multiple languages and locales. This section covers the key concepts and processes for managing localization and internationalization. ## Locale Management Locale management involves compiling and managing translation files. The **addons-server** project uses a structured approach to handle localization files efficiently. 1. **Compiling Locales**: - The Makefile provides commands to compile locale files, ensuring that translations are up-to-date. - Use the following command to compile locales: ```sh make compile_locales ``` 2. **Managing Locale Files**: - Locale files are typically stored in the `locale` directory within the project. - The project structure ensures that all locale files are organized and easily accessible for updates and maintenance. ## Adding New Translations We write the english translations of our strings directly in the code, using the `gettext` function. For example: ```python from django.utils.translation import gettext_lazy as _ def my_view(request): output = _('Welcome to my site.') return HttpResponse(output) ``` When developing locally you should not really need to do anything special to see the translations. The `gettext` function will return the string as is if it can't find a translation for it. In CI translation strings are automatically extracted and uploaded to Pontoon for translation. ## Translation Management Translation management involves handling translation strings and merging them as needed. The **addons-server** project follows best practices to ensure that translations are accurate and consistent. 1. **Handling Translation Strings**: - Translation strings are extracted from the source code and stored in `.po` files. - The `.po` file format is used to manage locale strings, providing a standard way to handle translations. 2. **Merging Translation Strings**: - To extract new locales from the codebase, use the following command: ```sh make extract_locales ``` - This command scans the codebase and updates the `.po` files with new or changed translation strings. - After extraction, scripts are used to merge new or updated translation strings into the existing locale files. - This process ensures that all translations are properly integrated and maintained. ## Additional Tools and Practices 1. **Pontoon**: - The **addons-server** project uses Pontoon, Mozilla's localization service, to manage translations. - Pontoon provides an interface for translators to contribute translations and review changes, ensuring high-quality localization. 2. **.po File Format**: - The `.po` file format is a widely used standard for managing translation strings. - It allows for easy editing and updating of translations, facilitating collaboration among translators. ## Translating Fields on Models The `olympia.translations` app defines a `olympia.translations.models.Translation` model, but for the most part, you shouldn't have to use that directly. When you want to create a foreign key to the `translations` table, use `olympia.translations.fields.TranslatedField`. This subclasses Django's `django.db.models.ForeignKey` to make it work with our special handling of translation rows. ### Minimal Model Example A minimal model with translations in addons-server would look like this: ```python from django.db import models from olympia.amo.models import ModelBase from olympia.translations.fields import TranslatedField, save_signal class MyModel(ModelBase): description = TranslatedField() models.signals.pre_save.connect(save_signal, sender=MyModel, dispatch_uid='mymodel_translations') ``` ### How It Works Behind the Scenes A `TranslatedField` is actually a `ForeignKey` to the `translations` table. To support multiple languages, we use a special feature of MySQL allowing a `ForeignKey` to point to multiple rows. #### When Querying Our base manager has a `_with_translations()` method that is automatically called when you instantiate a queryset. It does two things: - Adds an extra `lang=lang` in the query to prevent query caching from returning objects in the wrong language. - Calls `olympia.translations.transformers.get_trans()` which builds a custom SQL query to fetch translations in the current language and fallback language. This custom query ensures that only the specified languages are considered and uses a double join with `IF`/`ELSE` for each field. The results are fetched using a slave database connection to improve performance. #### When Setting Every time you set a translated field to a string value, the `TranslationDescriptor` `__set__` method is called. It determines whether it's a new translation or an update to an existing translation and updates the relevant `Translation` objects accordingly. These objects are queued for saving, which happens on the `pre_save` signal to avoid foreign key constraint errors. #### When Deleting Deleting all translations for a field is done using `olympia.translations.models.delete_translation()`, which sets the field to `NULL` and deletes all attached translations. Deleting a specific translation is possible but not recommended due to potential issues with fallback languages and foreign key constraints. ### Ordering by a Translated Field `olympia.translations.query.order_by_translation` allows you to order a `QuerySet` by a translated field, honoring the current and fallback locales like when querying. By following these practices, the **addons-server** project ensures that the application can support multiple languages and locales effectively. For more detailed instructions, refer to the project's Makefile and locale management scripts in the repository.