Multilingual support

pygeoapi is language-aware and can handle multiple languages if these have been defined in pygeoapi’s configuration (see maintainer guide). Providers can also handle multiple languages if configured. These may even be different from the languages that pygeoapi supports. Out-of-the-box, pygeoapi “speaks” English. System messages and exceptions are always English only.

The following sections provide more information how to use and set up languages in pygeoapi.

End user guide

There are 2 ways to affect the language of the results returned by pygeoapi, both for the HTML and JSON(-LD) formats:

  1. After the requested pygeoapi URL, append a lang=<code> query parameter, where <code> should be replaced by a well-known language code. This can be an ISO 639-1 code (e.g. de for German), optionally accompanied by an ISO 3166-1 alpha-2 country code (e.g. de-CH for Swiss-German). Please refer to this W3C article for more information or this list of language codes for more examples. Another option is to send a complex definition with quality weights (e.g. de-CH, de;q=0.9, en;q=0.8, fr;q=0.7, *;q=0.5). pygeoapi will then figure out the best match for the requested language.

    For example, to view the pygeoapi landing page in Canadian-French, you could use this URL:

    https://demo.pygeoapi.io/master?lang=fr-CA

  2. Alternatively, you can set an Accept-Language HTTP header for the requested pygeoapi URL. Language tags that are valid for the lang query parameter are also valid for this header value. Please note that if your client application (e.g. browser) is configured for a certain language, it will likely set this header by default, so the returned response should be translated to the language of your client app. If you don’t want this, you can either change the language of your client app or append the lang parameter to the URL, which will override any language defined in the Accept-Language header.

Notes

  • If pygeoapi cannot find a good match to the requested language, the response is returned in the default language (US English mostly). The default language is the first language defined in pygeoapi’s server configuration YAML (see maintainer guide).

  • Even if pygeoapi itself supports the requested language, provider plugins may not support that particular language or perhaps don’t even support any language at all. In that case the provider will reply in its own “unknown” language, which may not be the same language as the default pygeoapi server language set in the Content-Language HTTP response header.

  • It is up to the creator of the provider to properly define at least 1 supported language in the provider configuration, as described in the developer guide. This will ensure that the Content-Language HTTP response header is always set properly.

  • If pygeoapi found a match to the requested language, the response will include a Content-Language HTTP header, set to the best-matching server language code. This is the default behavior for most pygeoapi requests. However, note that some responses (e.g. exceptions) always have a Content-Language: en-US header, regardless of the requested language.

  • For results returned by a provider, the Content-Language HTTP header will be set to the best-matching provider language or the best-matching pygeoapi server language if the provider is not language-aware.

  • If the provider supports a requested language, but pygeoapi does not support that same language, the Content-Language header will contain both the provider language and the best-matching pygeoapi server language.

  • Please note that the Content-Language HTTP response header only indicates the language of the intended audience. It does not necessarily mean that the content is actually written in that particular language.

Maintainer guide

Every pygeoapi instance should support at least 1 language. In the server configuration, there must be a language or a languages (note the s) property. The property can be set to a single language tag or a list of tags respectively.

If you wish to set up a multilingual pygeoapi instance, you will have to add more than 1 language to the server configuration YAML file (i.e. pygeoapi-config.yml). First, you will have to add the supported language tags/codes as a list. For example, if you wish to support American English and Canadian French, you could do:

server:
    bind: ...
    url: ...
    mimetype: ...
    encoding: ...
    languages:
        - en-US
        - fr-CA

Next, you will have to provide translations for the configured languages. This involves 3 steps:

  1. Add translations for configurable text values in the pygeoapi configuration;

  2. Verify if there are any Jinja2 HTML template translations for the configured language(s);

  3. Make sure that the provider plugins you need can handle this language as well, if you have the ability to do so. See the developer guide for more details.

Notes

  • The first language you define in the configuration determines the default language, i.e. the language that pygeoapi will use if no other language was requested or no best match for the requested language could be found.

  • It is not possible to disable language support in pygeoapi. The functionality is always on and a Content-Language HTTP response header is always set. If results should be available in a single language, you’d have to set that language only in the pygeoapi configuration.

  • Results returned from a provider may be in a different language than pygeoapi’s own server language. The “raw” requested language is always passed on to the provider, even if pygeoapi itself does not support it. For more information, see the end user guide and the developer guide.

Add translations for configurable text values

For most of the text values in pygeoapi’s server configuration where it makes sense, you can add translations. Consider the metadata section for example. The English-only version looks similar to this:

metadata:
    identification:
        title: pygeoapi default instance
        description: pygeoapi provides an API to geospatial data
        keywords:
            - geospatial
            - data
            - api

If you wish to make these text values available in English and French, you could use the following language struct:

metadata:
    identification:
        title:
            en: pygeoapi default instance
            fr: instance par défaut de pygeoapi
        description:
            en: pygeoapi provides an API to geospatial data
            fr: pygeoapi fournit une API aux données géospatiales
        keywords:
            en:
                - geospatial
                - data
                - api
            fr:
                - géospatiale
                - données
                - api

In other words: each plain text value should be replaced by a dictionary, where the language code is the key and the translated text represents the matching value. For lists, this can be applied as well (see keywords example above), as long as you nest the entire list under a language key instead of each list item.

Note that the example above uses generic language tags, but you can also supply more localized tags (with a country code) if required. pygeoapi should always be able find the best match to the requested language, i.e. if the user wants Swiss-French (fr-CH) but pygeoapi can only find fr tags, those values will be returned. However, if a fr-CH tag can also be found, that value will be returned and not the fr value.

Warning

A language struct is only translated if all language tags (keys) in the struct are valid locales.

Todo

Add docs on HTML templating.

Translator guide

Hardcoded strings in pygeoapi templates are translated using the Babel translation system. Translation files are stored on the /locale folder. Translators can follow these steps to prepare their environment for translations.

  1. Extract from latest code the keys to be translated. These keys are captured in a .pot file. Note that the .pot file is not to be stored in version control, but as an intermediary file used to update /locale/*/LC_MESSAGES/messages.po files:

    pybabel extract -F babel-mapping.ini -o locale/messages.pot ./
    
  2. Update the existing .po language file:

    pybabel update -d locale -l fr -i locale/messages.pot
    
  3. Open the relevant .po file and contribute your translations. Then compile a .mo file to be used by the application:

    pybabel compile -d locale -l fr
    

Within jinja templates keys are prepared to be translated by wrapping them in:

{% trans %}Key{% endtrans %}

Developer guide

If you are a developer who wishes to create a pygeoapi provider plugin that “speaks” a certain language, you will have to fully implement this yourself. Needless to say, if your provider depends on some backend, it will only make sense to implement language support if the backend can be queried in another language as well.

You are free to set up the language support anyway you like, but there are a couple of steps you’ll have to walk through:

  1. You will have to define the supported languages in the provider configuration YAML. This can be done in a similar fashion as the languages configuration for pygeoapi itself, as described in the maintainer guide section above. For example, a TinyDB records provider that supports English and French could be set up like:

    my-records:
        type: collection
        ..
        providers:
            - type: record
              name: TinyDBCatalogue
              data: ..
              languages:
                  - en
                  - fr
    
  2. If your provider implements any of the query, get or get_metadata methods of the base class and you wish to make them language-aware, either add an implicit **kwargs parameter or an explicit language=None parameter to the method signature.

An example Python code block for a custom provider with a language-aware query method could look like this:

class MyCoolVectorDataProvider(BaseProvider):
"""My cool vector data provider"""

def __init__(self, provider_def):
    super().__init__(provider_def)

def query(self, startindex=0, limit=10, resulttype='results', bbox=[],
          datetime_=None, properties=[], sortby=[], select_properties=[],
          skip_geometry=False, q=None, language=None):
    LOGGER.debug(f'Provider queried in {language.english_name} language')
    # Implement your logic here, returning JSON in the requested language

Alternatively, you could also use **kwargs in the query method and get the language value:

def query(self, **kwargs):
    LOGGER.debug(f"Provider locale set to: {kwargs.get('language')}")
    # Implement your logic here, returning JSON in the requested language

This is all that is required. The pygeoapi API class will make sure that the correct HTTP Content-Language headers are set on the response object.

Notes

  • If your provider implements any of the aforementioned query, get and get_metadata methods, it must add a **kwargs or language=None parameter, even if it does not need to use the language parameter.

  • Contrary to the pygeoapi server configuration, adding a language or languages (both are supported) property to the provider definition is not required and may be omitted. In that case, the passed-in language parameter language-aware provider methods (query, get, etc.) will be set to None. This results in the following behavior:

    • HTML responses returned from the providers will have the Content-Language header set to the best-matching pygeoapi server language.

    • JSON(-LD) responses returned from providers will not have a Content-Language header if language is None.

  • If the provider supports a requested language, the passed-in language will be set to the best matching Babel Locale instance. Note that this may be the provider default language if no proper match was found. No matter the output format, API responses returned from providers will always contain a best-matching Content-Language header if one ore more supported provider languages were defined.

  • For general information about building plugins, please visit the Customizing pygeoapi: plugins page.