Repository: pypi/warehouse

Issue:
Support for properly displaying the site translated to RTL languages
**What's the problem this feature will solve?**
Display PyPI.org propely when translated to RTL languages such as Arabic, Farsi and Hebrew.

**Describe the solution you'd like**
I can't suggest a specific technical solution, since I'm not familiar with the website's design and implementation.

**Additional context**
The context is the recent work on translation into additional languages.


Discussion:
Options to investigate: 
- https://www.npmjs.com/package/postcss-rtl
- https://github.com/MohammadYounes/rtlcss
I wanted to toss in another possibility; GoDaddy just recently developed this WebPack plugin during our efforts to build better BiDi support: https://github.com/godaddy/webpack-rtlcss-plugin

It uses rtlcss under the covers (linked above by @nlhkabu), but might make it a bit easier to use.
At the time of writing, our Arabic translation is at 29%, Hebrew is at ~0%, and Farsi does not yet exist, so this is low- to medium-priority for now.
> our Arabic translation is at 29%, Hebrew is at ~0%, and Farsi does not yet exist

This is a chicken-and-egg problem: There's little point in translating to a language that the site doesn't support at a technical level. It's also more difficult since one can't properly see the result of the translations on the site itself.

If it would help, I'd be willing to help with the Hebrew translation (I'm a native speaker).
To be clear, translations are done through https://hosted.weblate.org/projects/pypa/warehouse/ and can be completed independently of whether PyPI supports them or not.

All the currently completed translations were done through that interface, and none of the translators were able to see the result of the translations on the site until they reached ~100% completion and we explicitly added support for their given locale.

I'm also not convinced that our lack of support for RTL languages is what's causing the low completion percentages for those locales. I would assume that most contributors are unaware that we'd have extra work to do to support their locale once it reaches ~100% complete.

> If it would help, I'd be willing to help with the Hebrew translation (I'm a native speaker).

Absolutely! If you're able to significantly increase the completion percentage for just one of these languages, we can prioritize this work to ensure that we're ready to support it when it's fully translated.

Let us know if you have any questions!
> If you're able to significantly increase the completion percentage for just one of these languages, we can prioritize this work to ensure that we're ready to support it when it's fully translated.

That's great to hear! This is why I hadn't started helping with the translation earlier: I wasn't sure whether RTL language support would actually be worked on.

In that case I'll start working on the translation when I can.
I've translated 50% of the strings for the Hebrew language so far.

BTW,
> I would assume that most contributors are unaware that we'd have extra work to do to support their locale once it reaches ~100% complete.

IMO most of those using RTL languages would likely be acutely aware of this, since we see so many issues caused by our languages being RTL in this digital age...
I've completed 100% translation of PyPI.org to Hebrew. Ready for the next step!
@di, with the Hebrew translation done, can this be given higher priority?
Thank you for this @taleinat!  @di - maybe we can book some pairing time together to look at this?
@taleinat Yes!

@nlhkabu Yes! Will DM you. 
@taleinat FYI, @di and I started on this today.  Once we have made some more progress, we'll probably be looking for feedback from you as to whether or not everything is displayed correctly.

Resolving patch:
diff --git a/warehouse/i18n/__init__.py b/warehouse/i18n/__init__.py
--- a/warehouse/i18n/__init__.py
+++ b/warehouse/i18n/__init__.py
@@ -19,19 +19,21 @@
 
 from warehouse.cache.http import add_vary
 
-# Taken from:
-# https://github.com/django/django/blob/master/django/conf/locale/__init__.py
 KNOWN_LOCALES = {
-    "en": "English",  # English
-    "es": "español",  # Spanish
-    "fr": "français",  # French
-    "ja": "日本語",  # Japanese
-    "pt_BR": "Português Brasileiro",  # Brazilian Portugeuse
-    "uk": "Українська",  # Ukrainian
-    "el": "Ελληνικά",  # Greek
-    "de": "Deutsch",  # German
-    "zh_Hans": "简体中文",  # Simplified Chinese
-    "ru": "Русский",  # Russian
+    identifier: Locale.parse(identifier, sep="_")
+    for identifier in [
+        "en",  # English
+        "es",  # Spanish
+        "fr",  # French
+        "ja",  # Japanese
+        "pt_BR",  # Brazilian Portugeuse
+        "uk",  # Ukranian
+        "el",  # Greek
+        "de",  # German
+        "zh_Hans",  # Simplified Chinese
+        "ru",  # Russian
+        "he",  # Hebrew
+    ]
 }
 
 LOCALE_ATTR = "_LOCALE_"
@@ -60,9 +62,9 @@ def __str__(self):
 
 def _locale(request):
     """
-    Computes a babel.core:Locale() object for this request.
+    Gets a babel.core:Locale() object for this request.
     """
-    return Locale.parse(request.locale_name, sep="_")
+    return KNOWN_LOCALES.get(request.locale_name, "en")
 
 
 def _negotiate_locale(request):


Test patch:
diff --git a/tests/unit/i18n/test_init.py b/tests/unit/i18n/test_init.py
--- a/tests/unit/i18n/test_init.py
+++ b/tests/unit/i18n/test_init.py
@@ -87,13 +87,12 @@ def view(context, request):
 
 
 def test_sets_locale(monkeypatch):
+    locale_name = pretend.stub()
     locale_obj = pretend.stub()
-    locale_cls = pretend.stub(parse=pretend.call_recorder(lambda l, **kw: locale_obj))
-    monkeypatch.setattr(i18n, "Locale", locale_cls)
-    request = pretend.stub(locale_name=pretend.stub())
+    monkeypatch.setattr(i18n, "KNOWN_LOCALES", {locale_name: locale_obj})
+    request = pretend.stub(locale_name=locale_name)
 
     assert i18n._locale(request) is locale_obj
-    assert locale_cls.parse.calls == [pretend.call(request.locale_name, sep="_")]
 
 
 def test_negotiate_locale(monkeypatch):