Kaydet (Commit) 67e6e0fc authored tarafından Aymeric Augustin's avatar Aymeric Augustin

Fixed #17087 -- Re-organized the i18n docs to reduce confusion between…

Fixed #17087 -- Re-organized the i18n docs to reduce confusion between USE_I18N/USE_L10N and the concepts of internationalization/localisation. Re
moved some duplicate content.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@17026 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst 4f00a2d3
.. _using-translations-in-your-own-projects:
===============================================
Using internationalization in your own projects
===============================================
At runtime, Django builds an in-memory unified catalog of literals-translations.
To achieve this it looks for translations by following this algorithm regarding
the order in which it examines the different file paths to load the compiled
:term:`message files <message file>` (``.mo``) and the precedence of multiple
translations for the same literal:
1. The directories listed in :setting:`LOCALE_PATHS` have the highest
precedence, with the ones appearing first having higher precedence than
the ones appearing later.
2. Then, it looks for and uses if it exists a ``locale`` directory in each
of the installed apps listed in :setting:`INSTALLED_APPS`. The ones
appearing first have higher precedence than the ones appearing later.
3. Then, it looks for a ``locale`` directory in the project directory, or
more accurately, in the directory containing your settings file.
4. Finally, the Django-provided base translation in ``django/conf/locale``
is used as a fallback.
.. deprecated:: 1.3
Lookup in the ``locale`` subdirectory of the directory containing your
settings file (item 3 above) is deprecated since the 1.3 release and will be
removed in Django 1.5. You can use the :setting:`LOCALE_PATHS` setting
instead, by listing the absolute filesystem path of such ``locale``
directory in the setting value.
.. seealso::
The translations for literals included in JavaScript assets are looked up
following a similar but not identical algorithm. See the
:ref:`javascript_catalog view documentation <javascript_catalog-view>` for
more details.
In all cases the name of the directory containing the translation is expected to
be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``,
etc.
This way, you can write applications that include their own translations, and
you can override base translations in your project path. Or, you can just build
a big project out of several apps and put all translations into one big common
message file specific to the project you are composing. The choice is yours.
.. note::
If you're using manually configured settings, as described in
:ref:`settings-without-django-settings-module`, the ``locale`` directory in
the project directory will not be examined, since Django loses the ability
to work out the location of the project directory. (Django normally uses the
location of the settings file to determine this, and a settings file doesn't
exist if you're manually configuring your settings.)
All message file repositories are structured the same way. They are:
* All paths listed in :setting:`LOCALE_PATHS` in your settings file are
searched for ``<language>/LC_MESSAGES/django.(po|mo)``
* ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` --
deprecated, see above.
* ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
To create message files, you use the :djadmin:`django-admin.py makemessages <makemessages>`
tool. You only need to be in the same directory where the ``locale/`` directory
is located. And you use :djadmin:`django-admin.py compilemessages <compilemessages>`
to produce the binary ``.mo`` files that are used by ``gettext``. Read the
:doc:`/topics/i18n/localization` document for more details.
You can also run ``django-admin.py compilemessages --settings=path.to.settings``
to make the compiler process all the directories in your :setting:`LOCALE_PATHS`
setting.
Finally, you should give some thought to the structure of your translation
files. If your applications need to be delivered to other users and will
be used in other projects, you might want to use app-specific translations.
But using app-specific translations and project-specific translations could
produce weird problems with ``makemessages``: It will traverse all directories
below the current path and so might put message IDs into a unified, common
message file for the current project that are already in application message
files.
The easiest way out is to store applications that are not part of the project
(and so carry their own translations) outside the project tree. That way,
``django-admin.py makemessages``, when ran on a project level will only extract
strings that are connected to your explicit project and not strings that are
distributed independently.
Using translations outside views and templates
==============================================
While Django provides a rich set of i18n tools for use in views and templates,
it does not restrict the usage to Django-specific code. The Django translation
mechanisms can be used to translate arbitrary texts to any language that is
supported by Django (as long as an appropriate translation catalog exists, of
course). You can load a translation catalog, activate it and translate text to
language of your choice, but remember to switch back to original language, as
activating a translation catalog is done on per-thread basis and such change
will affect code running in the same thread.
For example::
from django.utils import translation
def welcome_translated(language):
cur_language = translation.get_language()
try:
translation.activate(language)
text = translation.ugettext('welcome')
finally:
translation.activate(cur_language)
return text
Calling this function with the value 'de' will give you ``"Willkommen"``,
regardless of :setting:`LANGUAGE_CODE` and language set by middleware.
Functions of particular interest are ``django.utils.translation.get_language()``
which returns the language used in the current thread,
``django.utils.translation.activate()`` which activates a translation catalog
for the current thread, and ``django.utils.translation.check_for_language()``
which checks if the given language is supported by Django.
......@@ -18,7 +18,6 @@ you quickly accomplish common tasks.
deployment/index
error-reporting
initial-data
i18n
jython
legacy-databases
outputting-csv
......
......@@ -56,7 +56,7 @@ Browse the following sections to find out how:
triaging-tickets
writing-code/index
writing-documentation
translations
localizing
committing-code
.. _django-users: http://groups.google.com/group/django-users
......
=======================================
Submitting and maintaining translations
=======================================
=================
Localizing Django
=================
Various parts of Django, such as the admin site and validation error messages,
are internationalized. This means they display different text depending on each
user's language setting. For this, Django uses the same internationalization and
localization infrastructure available to Django applications, described in the
:doc:`i18n documentation</topics/i18n/index>`.
are internationalized. This means they display differently depending on each
user's language or country. For this, Django uses the same internationalization
and localization infrastructure available to Django applications, described in
the :doc:`i18n documentation </topics/i18n/index>`.
Internationalization
--------------------
Translations
------------
Translations are contributed by Django users worldwide. The translation work is
coordinated at `Transifex`_.
......@@ -45,8 +45,8 @@ do:
For more information about how to use Transifex, read the
`Transifex User Guide`_.
Localization
------------
Formats
-------
You can also review ``conf/locale/<locale>/formats.py``. This file describes
the date, time and numbers formatting particularities of your locale. See
......
......@@ -146,7 +146,7 @@ Django's general catalog in ``django/conf/locale``. If you want localflavor's
texts to be translated, like form fields error messages, you must include
:mod:`django.contrib.localflavor` in the :setting:`INSTALLED_APPS` setting, so
the internationalization system can find the catalog, as explained in
:ref:`using-translations-in-your-own-projects`.
:ref:`how-django-discovers-translations`.
Adding flavors
==============
......
......@@ -1243,7 +1243,7 @@ LOCALE_PATHS
Default: ``()`` (Empty tuple)
A tuple of directories where Django looks for translation files.
See :ref:`using-translations-in-your-own-projects`.
See :ref:`how-django-discovers-translations`.
Example::
......@@ -2038,10 +2038,10 @@ USE_I18N
Default: ``True``
A boolean that specifies whether Django's internationalization system should be
enabled. This provides an easy way to turn it off, for performance. If this is
set to ``False``, Django will make some optimizations so as not to load the
internationalization machinery.
A boolean that specifies whether Django's translation system should be enabled.
This provides an easy way to turn it off, for performance. If this is set to
``False``, Django will make some optimizations so as not to load the
translation machinery.
See also :setting:`USE_L10N`
......@@ -2054,9 +2054,9 @@ USE_L10N
Default: ``False``
A boolean that specifies if data will be localized by default or not. If this
is set to ``True``, e.g. Django will display numbers and dates using the
format of the current locale.
A boolean that specifies if localized formatting of data will be enabled by
default or not. If this is set to ``True``, e.g. Django will display numbers and
dates using the format of the current locale.
See also :setting:`USE_I18N` and :setting:`LANGUAGE_CODE`
......
......@@ -455,7 +455,7 @@ appropriate entities.
:synopsis: Internationalization support.
For a complete discussion on the usage of the following see the
:doc:`Internationalization documentation </topics/i18n/internationalization>`.
:doc:`translation documentation </topics/i18n/translation>`.
.. function:: gettext(message)
......
......@@ -494,12 +494,9 @@ more on these decorators.
.. versionadded:: 1.2
If :setting:`USE_I18N` is set to ``True`` then the generated cache key will
include the name of the active :term:`language<language code>`.
This allows you to easily cache multilingual sites without having to create
the cache key yourself.
See :doc:`/topics/i18n/deployment` for more on how Django discovers the active
language.
include the name of the active :term:`language<language code>` -- see also
:ref:`how-django-discovers-language-preference`). This allows you to easily
cache multilingual sites without having to create the cache key yourself.
__ `Controlling cache: Using other headers`_
......
==========================
Deployment of translations
==========================
If you don't need internationalization
======================================
Django's internationalization hooks are on by default, and that means there's a
bit of i18n-related overhead in certain places of the framework. If you don't
use internationalization, you should take the two seconds to set
:setting:`USE_I18N = False <USE_I18N>` in your settings file. If
:setting:`USE_I18N` is set to ``False``, then Django will make some
optimizations so as not to load the internationalization machinery.
You'll probably also want to remove ``'django.core.context_processors.i18n'``
from your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
.. note::
There is also an independent but related :setting:`USE_L10N` setting that
controls if Django should implement format localization.
If :setting:`USE_L10N` is set to ``True``, Django will handle numbers times,
and dates in the format of the current locale. That includes representation
of these field types on templates and allowed input formats for dates,
times on model forms.
See :ref:`format-localization` for more details.
If you do need internationalization
===================================
.. _how-django-discovers-language-preference:
How Django discovers language preference
----------------------------------------
Once you've prepared your translations -- or, if you just want to use the
translations that come with Django -- you'll just need to activate translation
for your app.
Behind the scenes, Django has a very flexible model of deciding which language
should be used -- installation-wide, for a particular user, or both.
To set an installation-wide language preference, set :setting:`LANGUAGE_CODE`.
Django uses this language as the default translation -- the final attempt if no
other translator finds a translation.
If all you want to do is run Django with your native language, and a language
file is available for it, all you need to do is set :setting:`LANGUAGE_CODE`.
If you want to let each individual user specify which language he or she
prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language
selection based on data from the request. It customizes content for each user.
To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting. Because middleware order
matters, you should follow these guidelines:
* Make sure it's one of the first middlewares installed.
* It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
makes use of session data. And it should come before ``CommonMiddleware``
because ``CommonMiddleware`` needs an activated language in order
to resolve the requested URL.
* If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it.
For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
)
(For more on middleware, see the :doc:`middleware documentation
</topics/http/middleware>`.)
``LocaleMiddleware`` tries to determine the user's language preference by
following this algorithm:
.. versionchanged:: 1.4
* First, it looks for the language prefix in the requested URL. This is
only performed when you are using the ``i18n_patterns`` function in your
root URLconf. See :ref:`url-internationalization` for more information
about the language prefix and how to internationalize URL patterns.
* Failing that, it looks for a ``django_language`` key in the current
user's session.
* Failing that, it looks for a cookie.
The name of the cookie used is set by the :setting:`LANGUAGE_COOKIE_NAME`
setting. (The default name is ``django_language``.)
* Failing that, it looks at the ``Accept-Language`` HTTP header. This
header is sent by your browser and tells the server which language(s) you
prefer, in order by priority. Django tries each language in the header
until it finds one with available translations.
* Failing that, it uses the global :setting:`LANGUAGE_CODE` setting.
.. _locale-middleware-notes:
Notes:
* In each of these places, the language preference is expected to be in the
standard :term:`language format<language code>`, as a string. For example,
Brazilian Portuguese is ``pt-br``.
* If a base language is available but the sublanguage specified is not,
Django uses the base language. For example, if a user specifies ``de-at``
(Austrian German) but Django only has ``de`` available, Django uses
``de``.
* Only languages listed in the :setting:`LANGUAGES` setting can be selected.
If you want to restrict the language selection to a subset of provided
languages (because your application doesn't provide all those languages),
set :setting:`LANGUAGES` to a list of languages. For example::
LANGUAGES = (
('de', _('German')),
('en', _('English')),
)
This example restricts languages that are available for automatic
selection to German and English (and any sublanguage, like de-ch or
en-us).
* If you define a custom :setting:`LANGUAGES` setting, as explained in the
previous bullet, it's OK to mark the languages as translation strings
-- but use a "dummy" ``ugettext()`` function, not the one in
``django.utils.translation``. You should *never* import
``django.utils.translation`` from within your settings file, because that
module in itself depends on the settings, and that would cause a circular
import.
The solution is to use a "dummy" ``ugettext()`` function. Here's a sample
settings file::
ugettext = lambda s: s
LANGUAGES = (
('de', ugettext('German')),
('en', ugettext('English')),
)
With this arrangement, ``django-admin.py makemessages`` will still find
and mark these strings for translation, but the translation won't happen
at runtime -- so you'll have to remember to wrap the languages in the
*real* ``ugettext()`` in any code that uses :setting:`LANGUAGES` at
runtime.
* The ``LocaleMiddleware`` can only select languages for which there is a
Django-provided base translation. If you want to provide translations
for your application that aren't already in the set of translations
in Django's source tree, you'll want to provide at least a basic
one as described in the :ref:`Locale restrictions<locale-restrictions>`
note.
Once ``LocaleMiddleware`` determines the user's preference, it makes this
preference available as ``request.LANGUAGE_CODE`` for each
:class:`~django.http.HttpRequest`. Feel free to read this value in your view
code. Here's a simple example::
def hello_world(request, count):
if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")
Note that, with static (middleware-less) translation, the language is in
``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
in ``request.LANGUAGE_CODE``.
.. _settings file: ../settings/
.. _middleware documentation: ../middleware/
.. _session: ../sessions/
.. _request object: ../request_response/#httprequest-objects
How Django discovers translations
---------------------------------
As described in :ref:`using-translations-in-your-own-projects`, Django looks for
translations by following this algorithm regarding the order in which it
examines the different file paths to load the compiled :term:`message files
<message file>` (``.mo``) and the precedence of multiple translations for the
same literal:
1. The directories listed in :setting:`LOCALE_PATHS` have the highest
precedence, with the ones appearing first having higher precedence than
the ones appearing later.
2. Then, it looks for and uses if it exists a ``locale`` directory in each
of the installed apps listed in :setting:`INSTALLED_APPS`. The ones
appearing first have higher precedence than the ones appearing later.
3. Then, it looks for a ``locale`` directory in the project directory, or
more accurately, in the directory containing your settings file.
4. Finally, the Django-provided base translation in ``django/conf/locale``
is used as a fallback.
.. deprecated:: 1.3
Lookup in the ``locale`` subdirectory of the directory containing your
settings file (item 3 above) is deprecated since the 1.3 release and will be
removed in Django 1.5. You can use the :setting:`LOCALE_PATHS` setting
instead, by listing the absolute filesystem path of such ``locale``
directory in the setting value.
.. seealso::
The translations for literals included in JavaScript assets are looked up
following a similar but not identical algorithm. See the
:ref:`javascript_catalog view documentation <javascript_catalog-view>` for
more details.
In all cases the name of the directory containing the translation is expected to
be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``,
etc.
===================
Format localization
===================
.. _format-localization:
.. versionadded:: 1.2
Overview
========
Django's formatting system is capable to display dates, times and numbers in templates using the format specified for the current :term:`locale <locale
name>`. It also handles localized input in forms.
When it's enabled, two users accessing the same content may see dates, times and
numbers formatted in different ways, depending on the formats for their current
locale.
The formatting system is disabled by default. To enable it, it's
necessary to set :setting:`USE_L10N = True <USE_L10N>` in your settings file.
.. note::
The default :file:`settings.py` file created by :djadmin:`django-admin.py
startproject <startproject>` includes :setting:`USE_L10N = True <USE_L10N>`
for convenience.
.. note::
There is also an independent but related :setting:`USE_I18N` setting that
controls if Django should activate translation. See
:doc:`/topics/i18n/translation` for more details.
Locale aware input in forms
===========================
When formatting is enabled, Django can use localized formats when parsing dates,
times and numbers in forms. That means it tries different formats for different
locales when guessing the format used by the user when inputting data on forms.
.. note::
Django uses different formats for displaying data to those it uses for
parsing data. Most notably, the formats for parsing dates can't use the
``%a`` (abbreviated weekday name), ``%A`` (full weekday name),
``%b`` (abbreviated month name), ``%B`` (full month name),
or ``%p`` (AM/PM).
To enable a form field to localize input and output data simply use its
``localize`` argument::
class CashRegisterForm(forms.Form):
product = forms.CharField()
revenue = forms.DecimalField(max_digits=4, decimal_places=2, localize=True)
.. _topic-l10n-templates:
Controlling localization in templates
=====================================
When you have enabled formatting with :setting:`USE_L10N`, Django
will try to use a locale specific format whenever it outputs a value
in a template.
However, it may not always be appropriate to use localized values --
for example, if you're outputting Javascript or XML that is designed
to be machine-readable, you will always want unlocalized values. You
may also want to use localization in selected templates, rather than
using localization everywhere.
To allow for fine control over the use of localization, Django
provides the ``l10n`` template library that contains the following
tags and filters.
Template tags
-------------
.. templatetag:: localize
localize
~~~~~~~~
.. versionadded:: 1.3
Enables or disables localization of template variables in the
contained block.
This tag allows a more fine grained control of localization than
:setting:`USE_L10N`.
To activate or deactivate localization for a template block, use::
{% load l10n %}
{% localize on %}
{{ value }}
{% endlocalize %}
{% localize off %}
{{ value }}
{% endlocalize %}
.. note::
The value of :setting:`USE_L10N` isn't respected inside of a
``{% localize %}`` block.
See :tfilter:`localize` and :tfilter:`unlocalize` for template filters that will
do the same job on a per-variable basis.
Template filters
----------------
.. templatefilter:: localize
localize
~~~~~~~~
.. versionadded:: 1.3
Forces localization of a single value.
For example::
{% load l10n %}
{{ value|localize }}
To disable localization on a single value, use :tfilter:`unlocalize`. To control
localization over a large section of a template, use the :ttag:`localize` template
tag.
.. templatefilter:: unlocalize
unlocalize
~~~~~~~~~~
.. versionadded:: 1.3
Forces a single value to be printed without localization.
For example::
{% load l10n %}
{{ value|unlocalize }}
To force localization of a single value, use :tfilter:`localize`. To
control localization over a large section of a template, use the
:ttag:`localize` template tag.
Creating custom format files
============================
Django provides format definitions for many locales, but sometimes you might
want to create your own, because a format files doesn't exist for your locale,
or because you want to overwrite some of the values.
To use custom formats, specify the path where you'll place format files first.
To do that, just set your :setting:`FORMAT_MODULE_PATH` setting to the package
where format files will exist, for instance::
FORMAT_MODULE_PATH = 'mysite.formats'
Files are not placed directly in this directory, but in a directory named as
the locale, and must be named ``formats.py``.
To customize the English formats, a structure like this would be needed::
mysite/
formats/
__init__.py
en/
__init__.py
formats.py
where :file:`formats.py` contains custom format definitions. For example::
THOUSAND_SEPARATOR = ' '
to use a space as a thousand separator, instead of the default for English,
a comma.
......@@ -2,64 +2,77 @@
Internationalization and localization
=====================================
.. toctree::
:hidden:
:maxdepth: 1
translation
formatting
Overview
========
Django has full support for internationalization of text in code and
templates, and format localization of dates and numbers. Here's how it works.
The goal of internationalization and localization is to allow a single Web
application to offer its content in languages and formats tailored to the
audience.
Django has full support for :doc:`translation of text
</topics/i18n/translation>` and :doc:`formatting of dates, times and numbers
</topics/i18n/formatting>`.
Essentially, Django does two things:
* It allows developers and template authors to specify which parts of
their apps should be translatable.
* It uses these hooks to translate Web apps for particular users according
to their language preferences.
* It allows developers and template authors to specify which parts of their apps
should be translated or formatted for local languages and cultures.
* It uses these hooks to localize Web apps for particular users according to
their preferences.
The complete process can be seen as divided in three stages. It is also possible
to identify an identical number of roles with very well defined responsibilities
associated with each of these tasks (although it's perfectly normal if you
find yourself performing more than one of these roles):
Obviously, translation depends on the target language. Formatting usually
depends on the target country.
* For application authors wishing to make sure their Django apps can be
used in different locales: :doc:`/topics/i18n/internationalization`.
* For translators wanting to translate Django apps: :doc:`/topics/i18n/localization`.
* For system administrators/final users setting up internationalized apps or
developers integrating third party apps: :doc:`/topics/i18n/deployment`.
Definitions
===========
.. toctree::
:hidden:
:maxdepth: 1
The words "internationalization" and "localization" often cause confusion;
here's a simplified definition:
.. glossary::
internationalization
localization
deployment
internationalization
Preparing the software for localization. Usually done by developers.
.. _ seealso::
localization
Writing the translations and local formats. Usually done by translators.
For more general information about the topic, see the `GNU gettext documentation`_
and the `Wikipedia article`_.
More details can be found in the `W3C Web Internationalization FAQ`_, the `Wikipedia article`_ or the `GNU gettext documentation`_.
.. _W3C Web Internationalization FAQ: http://www.w3.org/International/questions/qa-i18n
.. _GNU gettext documentation: http://www.gnu.org/software/gettext/manual/gettext.html#Concepts
.. _Wikipedia article: http://en.wikipedia.org/wiki/Internationalization_and_localization
Glossary
========
.. warning::
First lets define some terms that will help us to handle a common language:
Translation and formatting are controlled by :setting:`USE_I18N` and
:setting:`USE_L10N` settings respectively. However, both features involve
internationalization and localization. The names of the settings are an
unfortunate result of Django's history.
Here are some other terms that will help us to handle a common language:
.. glossary::
locale name
A locale name, either a language specification of the form ``ll`` or a
combined language and country specification of the form ``ll_CC``.
Examples: ``it``, ``de_AT``, ``es``, ``pt_BR``. Note the underscore in
some of them and the case of the part located to its right.
Examples: ``it``, ``de_AT``, ``es``, ``pt_BR``. The language part is
always is lower case and the country part in upper case. The separator
is an underscore.
language code
Represents the name of a language. Browsers send the names of the
languages they accept in the ``Accept-Language`` HTTP header using this
format. Examples: ``it``, ``de-at``, ``es``, ``pt-br``. Note the ``-``
separator.
format. Examples: ``it``, ``de-at``, ``es``, ``pt-br``. Both the language
and the country parts are in lower case. The separator is a dash.
message file
A message file is a plain-text file, representing a single language,
......@@ -70,21 +83,6 @@ First lets define some terms that will help us to handle a common language:
translation string
A literal that can be translated.
.. _specialties-of-django-i18n:
Specialties of Django translation
=================================
Django's translation machinery uses the standard ``gettext`` module that comes
with Python. If you know ``gettext``, you might note these specialties in the
way Django does translation:
* The string domain is ``django`` or ``djangojs``. This string domain is
used to differentiate between different programs that store their data
in a common message-file library (usually ``/usr/share/locale/``). The
``django`` domain is used for python and template translation strings
and is loaded into the global translation catalogs. The ``djangojs``
domain is only used for JavaScript translation catalogs to make sure
that those are as small as possible.
* Django doesn't use ``xgettext`` alone. It uses Python wrappers around
``xgettext`` and ``msgfmt``. This is mostly for convenience.
format file
A format file is a Python module that defines the data formats for a given
locale.
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment