diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
|
a
|
b
|
|
| 159 | 159 | res.merge(t) |
| 160 | 160 | return res |
| 161 | 161 | |
| 162 | | for localepath in settings.LOCALE_PATHS: |
| 163 | | if os.path.isdir(localepath): |
| 164 | | res = _merge(localepath) |
| 165 | | |
| 166 | | for appname in settings.INSTALLED_APPS: |
| | 162 | for appname in reversed(settings.INSTALLED_APPS): |
| 167 | 163 | app = import_module(appname) |
| 168 | 164 | apppath = os.path.join(os.path.dirname(app.__file__), 'locale') |
| 169 | 165 | |
| … |
… |
|
| 173 | 169 | if projectpath and os.path.isdir(projectpath): |
| 174 | 170 | res = _merge(projectpath) |
| 175 | 171 | |
| | 172 | for localepath in reversed(settings.LOCALE_PATHS): |
| | 173 | if os.path.isdir(localepath): |
| | 174 | res = _merge(localepath) |
| | 175 | |
| 176 | 176 | if res is None: |
| 177 | 177 | if fallback is not None: |
| 178 | 178 | res = fallback |
diff --git a/docs/howto/i18n.txt b/docs/howto/i18n.txt
|
a
|
b
|
|
| 4 | 4 | Using internationalization in your own projects |
| 5 | 5 | =============================================== |
| 6 | 6 | |
| 7 | | At runtime, Django looks for translations by following this algorithm: |
| | 7 | At runtime, Django builds a in-memory unified catalog of literals-translations. |
| | 8 | To achieve this it looks for translations by following this algorithm regarding |
| | 9 | the order in which it examines the different file paths to load the compiled |
| | 10 | :term:`message files <message file>` (``.mo``) and the precedence of multiple |
| | 11 | translations for the same literal: |
| 8 | 12 | |
| 9 | | * First, it looks for a ``locale`` directory in the directory containing |
| 10 | | your settings file. |
| 11 | | * Second, it looks for a ``locale`` directory in the project directory. |
| 12 | | * Third, it looks for a ``locale`` directory in each of the installed apps. |
| 13 | | It does this in the reverse order of INSTALLED_APPS |
| 14 | | * Finally, it checks the Django-provided base translation in |
| 15 | | ``django/conf/locale``. |
| | 13 | * The directories listed in :setting:`LOCALE_PATHS` have the highest |
| | 14 | precedence, with the ones appearing first having higher precedence than |
| | 15 | the ones appearing later. |
| | 16 | * Then, it looks for and uses if it exists a ``locale`` directory in each |
| | 17 | of the installed apps listed in :setting:`INSTALLED_APPS`. |
| | 18 | The ones appearing first have higher precedence than the ones appearing |
| | 19 | later. |
| | 20 | * Then, it looks for a ``locale`` directory in the project directory, or |
| | 21 | more accurately, in the directory containing your settings file. |
| | 22 | * Finally, the Django-provided base translation in ``django/conf/locale`` |
| | 23 | is used as a fallback. |
| 16 | 24 | |
| 17 | 25 | In all cases the name of the directory containing the translation is expected to |
| 18 | 26 | be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``, |
| … |
… |
|
| 37 | 45 | * ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
| 38 | 46 | * ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
| 39 | 47 | * All paths listed in ``LOCALE_PATHS`` in your settings file are |
| 40 | | searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)`` |
| | 48 | searched for ``<language>/LC_MESSAGES/django.(po|mo)`` |
| 41 | 49 | * ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)`` |
| 42 | 50 | |
| 43 | 51 | To create message files, you use the :djadmin:`django-admin.py makemessages <makemessages>` |
| … |
… |
|
| 50 | 58 | to make the compiler process all the directories in your :setting:`LOCALE_PATHS` |
| 51 | 59 | setting. |
| 52 | 60 | |
| 53 | | Application message files are a bit complicated to discover -- they need the |
| 54 | | :class:`~django.middleware.locale.LocaleMiddleware`. If you don't use the |
| 55 | | middleware, only the Django message files and project message files will be |
| 56 | | installed and available at runtime. |
| 57 | | |
| 58 | 61 | Finally, you should give some thought to the structure of your translation |
| 59 | 62 | files. If your applications need to be delivered to other users and will |
| 60 | 63 | be used in other projects, you might want to use app-specific translations. |
diff --git a/docs/topics/i18n/deployment.txt b/docs/topics/i18n/deployment.txt
|
a
|
b
|
|
| 171 | 171 | How Django discovers translations |
| 172 | 172 | --------------------------------- |
| 173 | 173 | |
| 174 | | As described in :ref:`using-translations-in-your-own-projects`, |
| 175 | | at runtime, Django looks for translations by following this algorithm: |
| | 174 | As described in :ref:`using-translations-in-your-own-projects`, Django looks for |
| | 175 | translations by following this algorithm regarding the order in which it |
| | 176 | examines the different file paths to load the compiled :term:`message files |
| | 177 | <message file>` (``.mo``) and the precedence of multiple translations for the |
| | 178 | same literal: |
| 176 | 179 | |
| 177 | | * First, it looks for a ``locale`` directory in the directory containing |
| 178 | | your settings file. |
| 179 | | * Second, it looks for a ``locale`` directory in the project directory. |
| 180 | | * Third, it looks for a ``locale`` directory in each of the installed apps. |
| 181 | | It does this in the reverse order of INSTALLED_APPS |
| 182 | | * Finally, it checks the Django-provided base translation in |
| 183 | | ``django/conf/locale``. |
| | 180 | * The directories listed in :setting:`LOCALE_PATHS` have the highest |
| | 181 | precedence, with the ones appearing first having higher precedence than |
| | 182 | the ones appearing later. |
| | 183 | * Then, it looks for and uses if it exists a ``locale`` directory in each |
| | 184 | of the installed apps listed in :setting:`INSTALLED_APPS`. |
| | 185 | The ones appearing first have higher precedence than the ones appearing |
| | 186 | later. |
| | 187 | * Then, it looks for a ``locale`` directory in the project directory, or |
| | 188 | more accurately, in the directory containing your settings file. |
| | 189 | * Finally, the Django-provided base translation in ``django/conf/locale`` |
| | 190 | is used as a fallback. |
| 184 | 191 | |
| 185 | 192 | In all cases the name of the directory containing the translation is expected to |
| 186 | 193 | be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``, |
diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.mo
index 6db1cb1a8ccf488dc7336d0fd707f6693a873a26..241de05ddb04199df4a515d720dc9ea263a06ca7
GIT binary patch
literal 668
zc${sK!EVz)5QewGAZjEIZ~@W7a6oE>HnAO~R&G<Zgj5t9H426UmueeNoQ>?=Xm?#v
zgm??YE5MlxZ@^n{;mSiWb|Ok8M*95ESpTfYJ3sGupBTjrVuMJCTf_&pj4#AG@s;q1
zZ^UilJMn<{LEI#M5vOa6{U*I%pVMOOF6or?8tHq|d!!#*%X2=HuCc4pTB*0v>#Moy
ze)dc_>^NB>OY29em79SQC3-_~!7K_hnR$;gFlej7vA>yR=V;Cv)5z#KP8>hZ_!~4<
zY868^7b@4_&^qgnaiNXlLz~NtKdo}hQyqgpI!Sropm5Z|>p5bGf-vAg#KR{LZpYp3
zW2yr(@-d!Ed&L*>AmmX9Q5bi(dC(<aBCO-75y}?AX%oW>p_H_cpv<s<y?Ij}6iwNd
zYO43CN9C2sk*6q1nj2v+!-GP$waNGW;qh>A`BPy#@cVQ@sOU2n3!1~>JJ(qhLaJSu
z%!IM%dau(XzH{lT$C;wx12xf^RCx?LXVUqjqB5f3N7|G&2DNBP+lzK#nf5jnJnTWZ
t<<~2!%FbJq4mOjM{$VmW9UZ36Uv07fy&DQyZ)x0YNw|M=WH0Fk*dL85s@MPk
diff --git a/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/de/LC_MESSAGES/django.po
|
a
|
b
|
|
| 8 | 8 | "Project-Id-Version: django tests\n" |
| 9 | 9 | "Report-Msgid-Bugs-To: \n" |
| 10 | 10 | "POT-Creation-Date: 2010-02-14 17:33+0100\n" |
| 11 | | "PO-Revision-Date: 2011-01-16 17:14+0100\n" |
| | 11 | "PO-Revision-Date: 2011-01-21 21:37-0300\n" |
| 12 | 12 | "Last-Translator: Jannis Leidel <jannis@leidel.info>\n" |
| 13 | 13 | "Language-Team: de <de@li.org>\n" |
| 14 | 14 | "MIME-Version: 1.0\n" |
| … |
… |
|
| 18 | 18 | |
| 19 | 19 | #: models.py:3 |
| 20 | 20 | msgid "Time" |
| 21 | | msgstr "Time (LOCALE_PATHS)" |
| | 21 | msgstr "Zeit (LOCALE_PATHS)" |
| 22 | 22 | |
| 23 | 23 | #: models.py:5 |
| | 24 | msgid "Date/time" |
| | 25 | msgstr "Datum/Zeit (LOCALE_PATHS)" |
| | 26 | |
| | 27 | #: models.py:7 |
| 24 | 28 | msgctxt "month name" |
| 25 | 29 | msgid "May" |
| 26 | 30 | msgstr "Mai" |
| 27 | 31 | |
| 28 | | #: models.py:7 |
| | 32 | #: models.py:9 |
| 29 | 33 | msgctxt "verb" |
| 30 | 34 | msgid "May" |
| 31 | 35 | msgstr "Kann" |
| 32 | 36 | |
| 33 | | #: models.py:9 |
| | 37 | #: models.py:11 |
| 34 | 38 | msgctxt "search" |
| 35 | 39 | msgid "%d result" |
| 36 | 40 | msgid_plural "%d results" |
| 37 | 41 | msgstr[0] "%d Resultat" |
| 38 | 42 | msgstr[1] "%d Resultate" |
| 39 | | |
diff --git a/tests/regressiontests/i18n/resolution/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/resolution/locale/de/LC_MESSAGES/django.mo
index 1c37ac5c87e9392a742ea3db6149b902c9667bd9..4d4f202be2e13a07a07dcb894e04f35e03092c91
GIT binary patch
literal 492
zc$`&`!A=`75Qg2B9=fNh=N_gv5E`=HAb|`)EXf8%?1ojc;m}L#Chpd-u~)W(;6eIc
zeF7xjgxPXPJM!n5k$#VV`{~opZ$|Ns_)HuU?}?nf#&_ZaA&DL0j`;S5v6k{j;t$mk
z<!8#Y*ju)ZZYCSapn5^C6|1_fboN#)15Q?Wqny{)2q@FxReG7hO*WfPuBXvVwblhJ
ze63e_v8g>)RzOr<S2%P^20Db@SOH1QV~i8zhro|SzrRm?OhTNgd+q<zaf~@891<@2
zM;P}>mrEb8a?<!l2I~Zj7kLiTw8-EzE7D1Ro{mPdY(76Dsi`-zR#+*y7LcdY%SC7T
zG|$f)ePCUE78R2s+dhgP#L>{2pbXvd*pi8$9zw5eq&6q8{3V@NVX&yic)ZnhUVbWv
znOWMEHno7`A38*3vvIP)v2|-Nz_fo`KS)mCwH)jk_%Z;#7j@sgS@*sw9bh*t%RTl_
F`2u-se*XXf
diff --git a/tests/regressiontests/i18n/resolution/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/i18n/resolution/locale/de/LC_MESSAGES/django.po
|
a
|
b
|
|
| 9 | 9 | "Project-Id-Version: PACKAGE VERSION\n" |
| 10 | 10 | "Report-Msgid-Bugs-To: \n" |
| 11 | 11 | "POT-Creation-Date: 2010-02-14 17:33+0100\n" |
| 12 | | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
| | 12 | "PO-Revision-Date: 2011-01-21 21:37-0300\n" |
| 13 | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
| 14 | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" |
| 15 | 15 | "MIME-Version: 1.0\n" |
| … |
… |
|
| 18 | 18 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" |
| 19 | 19 | |
| 20 | 20 | #: models.py:3 |
| | 21 | msgid "Time" |
| | 22 | msgstr "Zeit (APP)" |
| | 23 | |
| | 24 | #: models.py:5 |
| 21 | 25 | msgid "Date/time" |
| 22 | 26 | msgstr "Datum/Zeit (APP)" |
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
|
a
|
b
|
|
| 660 | 660 | |
| 661 | 661 | def assertUgettext(self, msgid, msgstr): |
| 662 | 662 | result = ugettext(msgid) |
| 663 | | self.assert_(msgstr in result, ("The string '%s' isn't in the " |
| | 663 | self.assertTrue(msgstr in result, ("The string '%s' isn't in the " |
| 664 | 664 | "translation of '%s'; the actual result is '%s'." % (msgstr, msgid, result))) |
| 665 | 665 | |
| 666 | 666 | class AppResolutionOrderI18NTests(ResolutionOrderI18NTests): |
| 667 | 667 | |
| 668 | 668 | def setUp(self): |
| 669 | 669 | self.old_installed_apps = settings.INSTALLED_APPS |
| 670 | | settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + ['regressiontests.i18n.resolution'] |
| | 670 | settings.INSTALLED_APPS = ['regressiontests.i18n.resolution'] + list(settings.INSTALLED_APPS) |
| 671 | 671 | super(AppResolutionOrderI18NTests, self).setUp() |
| 672 | 672 | |
| 673 | 673 | def tearDown(self): |
| … |
… |
|
| 691 | 691 | def test_locale_paths_translation(self): |
| 692 | 692 | self.assertUgettext('Time', 'LOCALE_PATHS') |
| 693 | 693 | |
| | 694 | def test_locale_paths_override_app_translation(self): |
| | 695 | old_installed_apps = settings.INSTALLED_APPS |
| | 696 | settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + ['regressiontests.i18n.resolution'] |
| | 697 | try: |
| | 698 | self.assertUgettext('Time', 'LOCALE_PATHS') |
| | 699 | finally: |
| | 700 | settings.INSTALLED_APPS = old_installed_apps |
| | 701 | |
| | 702 | def test_locale_paths_override_project_translation(self): |
| | 703 | old_settings_module = settings.SETTINGS_MODULE |
| | 704 | settings.SETTINGS_MODULE = 'regressiontests' |
| | 705 | try: |
| | 706 | self.assertUgettext('Date/time', 'LOCALE_PATHS') |
| | 707 | finally: |
| | 708 | settings.SETTINGS_MODULE = old_settings_module |
| | 709 | |
| 694 | 710 | class ProjectResolutionOrderI18NTests(ResolutionOrderI18NTests): |
| 695 | 711 | |
| 696 | 712 | def setUp(self): |
| … |
… |
|
| 708 | 724 | def test_project_override_app_translation(self): |
| 709 | 725 | old_installed_apps = settings.INSTALLED_APPS |
| 710 | 726 | settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + ['regressiontests.i18n.resolution'] |
| 711 | | self.assertUgettext('Date/time', 'PROJECT') |
| 712 | | settings.INSTALLED_APPS = old_installed_apps |
| 713 | | |
| 714 | | def test_project_override_locale_paths_translation(self): |
| 715 | | old_locale_paths = settings.LOCALE_PATHS |
| 716 | | settings.LOCALE_PATHS += (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'other', 'locale'),) |
| 717 | | self.assertUgettext('Date/time', 'PROJECT') |
| 718 | | settings.LOCALE_PATHS = old_locale_paths |
| | 727 | try: |
| | 728 | self.assertUgettext('Date/time', 'PROJECT') |
| | 729 | finally: |
| | 730 | settings.INSTALLED_APPS = old_installed_apps |
| 719 | 731 | |
| 720 | 732 | class DjangoFallbackResolutionOrderI18NTests(ResolutionOrderI18NTests): |
| 721 | 733 | |
| 722 | 734 | def test_django_fallback(self): |
| 723 | | self.assertUgettext('Date/time', 'Datum/Zeit') |
| | 735 | self.assertEqual(ugettext('Date/time'), 'Datum/Zeit') |
| 724 | 736 | |
| 725 | 737 | |
| 726 | 738 | class TestModels(TestCase): |
diff --git a/tests/regressiontests/locale/de/LC_MESSAGES/django.mo b/tests/regressiontests/locale/de/LC_MESSAGES/django.mo
index 2ee860a80b5e75cd9f5fe727c02f74936918dc9c..81616ca7bdabe07ebff028d2480f6cada79b65f3
GIT binary patch
literal 500
zc${63!EO^V5QYtu1AE|rIC2<nt<W^<O{G?DQ*}u;RbV$+Z8kk{0h5@mVPmgsrz#J^
zd+`L^cn@ajA%c-V&y4hY{M)}?9Q<GuPl#8<3GtN3*+YCHo)MBbAifbFA2HTZeo6eK
z`Z?v_lxeZYY#;qhHjzOMg5D}t_gm@gSG5i}+u)6IURxueOvmrj>kMwPYB^iXqe^ww
z1uT4{H~4nfc&x2}s9e-Ic1i|1g#B0nNz7x66XYkrPsMQfhWeO<SgAYh|Iu-bIVKzu
zE{3NV4@s9xAFy`P_*Mq%1WZ?X4)e6g;36y1S$>sHCRMgvUXs)_JJ~3#mD~!*)A{wP
zw|tT3SFJv_uDOhgS&{7@#gF4?Y)w#x?)cu3iJ<PoplhWz=diw&&Z{t5)l)p%>v}I=
zl*7!d?M9nMz}aUVqO#pN+2Yi?truXrzpWo7=kQRD4h_5>0Y8fR@7`?(ACwMoSXPU7
L*|<Jp|FwSrmUDr3
diff --git a/tests/regressiontests/locale/de/LC_MESSAGES/django.po b/tests/regressiontests/locale/de/LC_MESSAGES/django.po
|
a
|
b
|
|
| 9 | 9 | "Project-Id-Version: PACKAGE VERSION\n" |
| 10 | 10 | "Report-Msgid-Bugs-To: \n" |
| 11 | 11 | "POT-Creation-Date: 2010-02-14 17:33+0100\n" |
| 12 | | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
| | 12 | "PO-Revision-Date: 2011-01-21 21:37-0300\n" |
| 13 | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
| 14 | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" |
| 15 | 15 | "MIME-Version: 1.0\n" |
| … |
… |
|
| 18 | 18 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" |
| 19 | 19 | |
| 20 | 20 | #: models.py:3 |
| | 21 | msgid "Time" |
| | 22 | msgstr "Zeit (PROJECT)" |
| | 23 | |
| | 24 | #: models.py:5 |
| 21 | 25 | msgid "Date/time" |
| 22 | 26 | msgstr "Datum/Zeit (PROJECT)" |