Files
pygeoapi/docs/source/language.rst
T
Sander Schaminee 023f24d26b Multilingual support (alternative) (#664)
* Created localization (l10n) module + tests. Added l10n support to API and plugins (wip).

* Big refactor:

* All routed API methods are now decorated by @pre_process (consistency) and no longer have a headers+format argument but a request argument (**kwargs also removed)
* The pre_process decorator turns an incoming Flask/Starlette request into a generic APIRequest instance
* The new APIRequest class extracts all relevant info (params, data, locale, etc.) from the request and exposes them as properties
* Removed a lot of boilerplate (i.e. format checking) and wrapped that into methods
* Updated server-specific API calls in each route method (pass entire request object, not headers and query params)

* Several improvements and fixes:

* Updated OpenAPI page with "l" query param
* Added example translations (metadata)
* Changed plugin signature: added explicit locale attribute (instead of **kwargs)
* Moved locale processing to get_plugin_locale() function in l10n module
* API should pass raw requested locale to plugins, locale should always be set
* Fixed API tests and added APIRequest tests
* Prepared utils.py for Jinja2 i18n extension
* Rebased on commit b40297a8 and fixed compatibility with #661 and #662

* Updated documentation for language support

* Rebased and fixed compatibility with PR #658:

* Fixed EDR provider signature (added locale)
* Fixed EDR API routes and query function (and improved parameter-name handling)
* Fixed EDR tests

* Translate entire config in render_j2_template for requested locale:

* Added new translate_dict function to l10n module (+ tests)
* Updated all render_j2_template calls with locale parameter
* Updated pygeoapi-test-config.yml with some language structs

* Minor improvements

* support both 'language' and 'languages' property in server config and provider definitions
* renamed and modified translate_dict() to more generic translate_struct() function (l10n module)
* remove Content-Language header from provider responses if provider has no language support and format is json(ld)
* updated tests

* Leave provider locale handling to API

* Moved code to determine locale from providers to API class (and remove for formatters and processes)
* Removed locale parameter from plugin __init__ signatures
* Removed locale parameter from load_plugin()
* Added **kwargs to provider implementations for get, query, get_metadata, get_coverage_domainset and get_coverage_rangetype method signatures
* Added language=<locale> to all API calls to provider get, query, get_metadata, get_coverage_domainset and get_coverage_rangetype methods

* Use 'lang' instead of 'l' as language query parameter

* Updated Open API
* Updated documentation
* Fixed tests

* Implemented requested PR changes:

* Added usage examples to the APIRequest docstring
* Removed language support from coverage functions
* Updated plugins.rst and language.rst to match new behavior
* Removed language struct from resource links in pygeoapi-config.yml
* Rebased on latest master (fixed test_api.py)

* Rebased and applied fixes:

* Data property in APIRequest now is an awaitable attribute (fixed for Starlette compatibility)
* Named references to 'l' parameter to 'lang'

* Final changes/improvements:

* Make sure that Content-Language is always set;
* Added more tests to ensure that the default language returned is the first configured language (if no language was requested by the user);
* Updated docs;
* Replaced re-occuring strings with constants in api.py;
* Fixed Flake8 checks.

* add missing async to starlette routes (#704)

Co-authored-by: Tom Kralidis <tomkralidis@gmail.com>
2021-06-08 18:46:35 -04:00

234 lines
12 KiB
ReStructuredText

.. _language:
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 <https://www.w3.org/International/articles/language-tags/>`_ for more information or
this `list of language codes <http://www.lingoes.net/en/translator/langcode.htm>`_ 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:
.. code-block:: yaml
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 server YAML file;
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:
.. code-block:: yaml
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:
.. code-block:: yaml
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.
.. todo:: Add docs on HTML templating.
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:
.. code-block:: yaml
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:
.. code-block:: python
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:
.. code-block:: python
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 <http://babel.pocoo.org/en/latest/api/core.html#babel.core.Locale>`_.
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 :ref:`plugins` page.