django-rest-framework/topics/internationalization/index.html
2015-11-04 14:59:23 +00:00

508 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Internationalization - Django REST framework</title>
<link href="../../img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="canonical" href="http://www.django-rest-framework.org/topics/internationalization/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST, Internationalization">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
<link href="../../css/prettify.css" rel="stylesheet">
<link href="../../css/bootstrap.css" rel="stylesheet">
<link href="../../css/bootstrap-responsive.css" rel="stylesheet">
<link href="../../css/default.css" rel="stylesheet">
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18852272-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
span.fusion-wrap a {
display: block;
margin-top: 10px;
color: black;
}
a.fusion-poweredby {
display: block;
margin-top: 10px;
}
@media (max-width: 767px) {
div.promo {
display: none;
}
}
</style>
</head>
<body onload="prettyPrint()" class="-page">
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small " rel="prev" href="../ajax-csrf-cors/">
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small " rel="next" href="../documenting-your-api/">
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
<div class="nav-collapse collapse">
<!-- Main navigation -->
<ul class="nav navbar-nav">
<li >
<a href="../..">Home</a>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../tutorial/quickstart/">Quickstart</a>
</li>
<li >
<a href="../../tutorial/1-serialization/">1 - Serialization</a>
</li>
<li >
<a href="../../tutorial/2-requests-and-responses/">2 - Requests and responses</a>
</li>
<li >
<a href="../../tutorial/3-class-based-views/">3 - Class based views</a>
</li>
<li >
<a href="../../tutorial/4-authentication-and-permissions/">4 - Authentication and permissions</a>
</li>
<li >
<a href="../../tutorial/5-relationships-and-hyperlinked-apis/">5 - Relationships and hyperlinked APIs</a>
</li>
<li >
<a href="../../tutorial/6-viewsets-and-routers/">6 - Viewsets and routers</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../api-guide/requests/">Requests</a>
</li>
<li >
<a href="../../api-guide/responses/">Responses</a>
</li>
<li >
<a href="../../api-guide/views/">Views</a>
</li>
<li >
<a href="../../api-guide/generic-views/">Generic views</a>
</li>
<li >
<a href="../../api-guide/viewsets/">Viewsets</a>
</li>
<li >
<a href="../../api-guide/routers/">Routers</a>
</li>
<li >
<a href="../../api-guide/parsers/">Parsers</a>
</li>
<li >
<a href="../../api-guide/renderers/">Renderers</a>
</li>
<li >
<a href="../../api-guide/serializers/">Serializers</a>
</li>
<li >
<a href="../../api-guide/fields/">Serializer fields</a>
</li>
<li >
<a href="../../api-guide/relations/">Serializer relations</a>
</li>
<li >
<a href="../../api-guide/validators/">Validators</a>
</li>
<li >
<a href="../../api-guide/authentication/">Authentication</a>
</li>
<li >
<a href="../../api-guide/permissions/">Permissions</a>
</li>
<li >
<a href="../../api-guide/throttling/">Throttling</a>
</li>
<li >
<a href="../../api-guide/filtering/">Filtering</a>
</li>
<li >
<a href="../../api-guide/pagination/">Pagination</a>
</li>
<li >
<a href="../../api-guide/versioning/">Versioning</a>
</li>
<li >
<a href="../../api-guide/content-negotiation/">Content negotiation</a>
</li>
<li >
<a href="../../api-guide/metadata/">Metadata</a>
</li>
<li >
<a href="../../api-guide/format-suffixes/">Format suffixes</a>
</li>
<li >
<a href="../../api-guide/reverse/">Returning URLs</a>
</li>
<li >
<a href="../../api-guide/exceptions/">Exceptions</a>
</li>
<li >
<a href="../../api-guide/status-codes/">Status codes</a>
</li>
<li >
<a href="../../api-guide/testing/">Testing</a>
</li>
<li >
<a href="../../api-guide/settings/">Settings</a>
</li>
</ul>
</li>
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../documenting-your-api/">Documenting your API</a>
</li>
<li class="active" >
<a href="./">Internationalization</a>
</li>
<li >
<a href="../ajax-csrf-cors/">AJAX, CSRF & CORS</a>
</li>
<li >
<a href="../html-and-forms/">HTML & Forms</a>
</li>
<li >
<a href="../browser-enhancements/">Browser Enhancements</a>
</li>
<li >
<a href="../browsable-api/">The Browsable API</a>
</li>
<li >
<a href="../rest-hypermedia-hateoas/">REST, Hypermedia & HATEOAS</a>
</li>
<li >
<a href="../third-party-resources/">Third Party Resources</a>
</li>
<li >
<a href="../contributing/">Contributing to REST framework</a>
</li>
<li >
<a href="../project-management/">Project management</a>
</li>
<li >
<a href="../3.0-announcement/">3.0 Announcement</a>
</li>
<li >
<a href="../3.1-announcement/">3.1 Announcement</a>
</li>
<li >
<a href="../3.2-announcement/">3.2 Announcement</a>
</li>
<li >
<a href="../3.3-announcement/">3.3 Announcement</a>
</li>
<li >
<a href="../kickstarter-announcement/">Kickstarter Announcement</a>
</li>
<li >
<a href="../release-notes/">Release Notes</a>
</li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<div class="body-content">
<div class="container-fluid">
<!-- Search Modal -->
<div id="mkdocs_search_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 id="myModalLabel">Documentation search</h3>
</div>
<div class="modal-body">
<form role="form" autocomplete="off">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search..." id="mkdocs-search-query">
</div>
</form>
<div id="mkdocs-search-results"></div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
<li class="main">
<a href="#internationalization">Internationalization</a>
</li>
<li>
<a href="#enabling-internationalized-apis">Enabling internationalized APIs</a>
</li>
<li>
<a href="#adding-new-translations">Adding new translations</a>
</li>
<li>
<a href="#how-the-language-is-determined">How the language is determined</a>
</li>
</ul>
</div>
</div>
<div id="main-content" class="span9">
<h1 id="internationalization"><a class="toclink" href="#internationalization">Internationalization</a></h1>
<blockquote>
<p>Supporting internationalization is not optional. It must be a core feature.</p>
<p>&mdash; <a href="http://youtu.be/Wa0VfS2q94Y">Jannis Leidel, speaking at Django Under the Hood, 2015</a>.</p>
</blockquote>
<p>REST framework ships with translatable error messages. You can make these appear in your language enabling <a href="https://docs.djangoproject.com/en/1.7/topics/i18n/translation">Django's standard translation mechanisms</a>.</p>
<p>Doing so will allow you to:</p>
<ul>
<li>Select a language other than English as the default, using the standard <code>LANGUAGE_CODE</code> Django setting.</li>
<li>Allow clients to choose a language themselves, using the <code>LocaleMiddleware</code> included with Django. A typical usage for API clients would be to include an <code>Accept-Language</code> request header.</li>
</ul>
<h2 id="enabling-internationalized-apis"><a class="toclink" href="#enabling-internationalized-apis">Enabling internationalized APIs</a></h2>
<p>You can change the default language by using the standard Django <code>LANGUAGE_CODE</code> setting:</p>
<pre><code>LANGUAGE_CODE = "es-es"
</code></pre>
<p>You can turn on per-request language requests by adding <code>LocalMiddleware</code> to your <code>MIDDLEWARE_CLASSES</code> setting:</p>
<pre><code>MIDDLEWARE_CLASSES = [
...
'django.middleware.locale.LocaleMiddleware'
]
</code></pre>
<p>When per-request internationalization is enabled, client requests will respect the <code>Accept-Language</code> header where possible. For example, let's make a request for an unsupported media type:</p>
<p><strong>Request</strong></p>
<pre><code>GET /api/users HTTP/1.1
Accept: application/xml
Accept-Language: es-es
Host: example.org
</code></pre>
<p><strong>Response</strong></p>
<pre><code>HTTP/1.0 406 NOT ACCEPTABLE
{"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."}
</code></pre>
<p>REST framework includes these built-in translations both for standard exception cases, and for serializer validation errors.</p>
<p>Note that the translations only apply to the error strings themselves. The format of error messages, and the keys of field names will remain the same. An example <code>400 Bad Request</code> response body might look like this:</p>
<pre><code>{"detail": {"username": ["Esse campo deve ser unico."]}}
</code></pre>
<p>If you want to use different string for parts of the response such as <code>detail</code> and <code>non_field_errors</code> then you can modify this behavior by using a <a href="../../api-guide/exceptions/#custom-exception-handling">custom exception handler</a>.</p>
<h4 id="specifying-the-set-of-supported-languages"><a class="toclink" href="#specifying-the-set-of-supported-languages">Specifying the set of supported languages.</a></h4>
<p>By default all available languages will be supported.</p>
<p>If you only wish to support a subset of the available languages, use Django's standard <code>LANGUAGES</code> setting:</p>
<pre><code>LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
</code></pre>
<h2 id="adding-new-translations"><a class="toclink" href="#adding-new-translations">Adding new translations</a></h2>
<p>REST framework translations are managed online using <a href="https://www.transifex.com/projects/p/django-rest-framework/">Transifex</a>. You can use the Transifex service to add new translation languages. The maintenance team will then ensure that these translation strings are included in the REST framework package.</p>
<p>Sometimes you may need to add translation strings to your project locally. You may need to do this if:</p>
<ul>
<li>You want to use REST Framework in a language which has not been translated yet on Transifex.</li>
<li>Your project includes custom error messages, which are not part of REST framework's default translation strings.</li>
</ul>
<h4 id="translating-a-new-language-locally"><a class="toclink" href="#translating-a-new-language-locally">Translating a new language locally</a></h4>
<p>This guide assumes you are already familiar with how to translate a Django app. If you're not, start by reading <a href="https://docs.djangoproject.com/en/1.7/topics/i18n/translation">Django's translation docs</a>.</p>
<p>If you're translating a new language you'll need to translate the existing REST framework error messages:</p>
<ol>
<li>
<p>Make a new folder where you want to store the internationalization resources. Add this path to your <a href="https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS"><code>LOCALE_PATHS</code></a> setting.</p>
</li>
<li>
<p>Now create a subfolder for the language you want to translate. The folder should be named using <a href="https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name">locale name</a> notation. For example: <code>de</code>, <code>pt_BR</code>, <code>es_AR</code>.</p>
</li>
<li>
<p>Now copy the <a href="https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po">base translations file</a> from the REST framework source code into your translations folder.</p>
</li>
<li>
<p>Edit the <code>django.po</code> file you've just copied, translating all the error messages.</p>
</li>
<li>
<p>Run <code>manage.py compilemessages -l pt_BR</code> to make the translations
available for Django to use. You should see a message like <code>processing file django.po in &lt;...&gt;/locale/pt_BR/LC_MESSAGES</code>.</p>
</li>
<li>
<p>Restart your development server to see the changes take effect.</p>
</li>
</ol>
<p>If you're only translating custom error messages that exist inside your project codebase you don't need to copy the REST framework source <code>django.po</code> file into a <code>LOCALE_PATHS</code> folder, and can instead simply run Django's standard <code>makemessages</code> process.</p>
<h2 id="how-the-language-is-determined"><a class="toclink" href="#how-the-language-is-determined">How the language is determined</a></h2>
<p>If you want to allow per-request language preferences you'll need to include <code>django.middleware.locale.LocaleMiddleware</code> in your <code>MIDDLEWARE_CLASSES</code> setting.</p>
<p>You can find more information on how the language preference is determined in the <a href="https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference">Django documentation</a>. For reference, the method is:</p>
<ol>
<li>First, it looks for the language prefix in the requested URL.</li>
<li>Failing that, it looks for the <code>LANGUAGE_SESSION_KEY</code> key in the current users session.</li>
<li>Failing that, it looks for a cookie.</li>
<li>Failing that, it looks at the <code>Accept-Language</code> HTTP header.</li>
<li>Failing that, it uses the global <code>LANGUAGE_CODE</code> setting.</li>
</ol>
<p>For API clients the most appropriate of these will typically be to use the <code>Accept-Language</code> header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an <code>Accept-Language</code> header for API clients rather than using language URL prefixes.</p>
</div> <!--/span-->
</div> <!--/row-->
</div> <!--/.fluid-container-->
</div> <!--/.body content-->
<div id="push"></div>
</div> <!--/.wrapper -->
<footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p>
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="../../js/jquery-1.8.1-min.js"></script>
<script src="../../js/prettify-1.0.js"></script>
<script src="../../js/bootstrap-2.1.1-min.js"></script>
<script>var base_url = '../..';</script>
<script src="../../mkdocs/js/require.js"></script>
<script src="../../js/theme.js"></script>
<script>
var shiftWindow = function() {
scrollBy(0, -50)
};
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
$('.dropdown-menu').on('click touchstart', function(event) {
event.stopPropagation();
});
// Dynamically force sidenav/dropdown to no higher than browser window
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
$(function() {
$(window).resize(function() {
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
});
});
</script>
</body>
</html>